Hello! I make fighting game for two players by using the side scroller template. I have extended ACharacter class, AGameMode class, APlayerController class and UGameViewportClient class. I have created the Blueprint class of character based on my C++ class of character. And I have placed camera in the level. When I launch game in editor, camera is in mesh of first player. But I need camera behaviour like in games Tekken 3 and Mortal Kombat 4. How I can do this?
Code of my character class:
#pragma once
#include "GameFramework/Character.h"
#include "FighingGameCharacter.generated.h"
UCLASS(config=Game)
class AFighingGameCharacter : public ACharacter
{
GENERATED_BODY()
public:
AFighingGameCharacter();
};
#include "FighingGame.h"
#include "FighingGameCharacter.h"
AFighingGameCharacter::AFighingGameCharacter()
{
// Set size for collision capsule
GetCapsuleComponent()->InitCapsuleSize(42.f, 96.0f);
// Don't rotate when the controller rotates.
bUseControllerRotationPitch = false;
bUseControllerRotationYaw = false;
bUseControllerRotationRoll = false;
// Configure character movement
GetCharacterMovement()->bOrientRotationToMovement = true; // Face in the direction we are moving..
GetCharacterMovement()->GravityScale = 2.f;
GetCharacterMovement()->GroundFriction = 3.f;
GetCharacterMovement()->MaxWalkSpeed = 100.f;
// Note: The skeletal mesh and anim blueprint references on the Mesh component (inherited from Character)
// are set in the derived blueprint asset named MyCharacter (to avoid direct content references in C++)
}
Code of my game mode class:
#pragma once
#include "GameFramework/GameMode.h"
#include "FighingGameGameMode.generated.h"
UCLASS(minimalapi)
class AFighingGameGameMode : public AGameMode
{
GENERATED_BODY()
public:
AFighingGameGameMode();
virtual void BeginPlay() override;
};
#include "FighingGame.h"
#include "FighingGameGameMode.h"
#include "FighingGameCharacter.h"
#include "EngineGlobals.h"
#include "Kismet/GameplayStatics.h"
#include "Engine.h"
#include "FightingGameViewportClient.h"
#include "FightingGamePlayerController.h"
AFighingGameGameMode::AFighingGameGameMode()
{
// set default pawn class to our Blueprinted character
//static ConstructorHelpers::FClassFinder<APawn> PlayerPawnBPClass(TEXT("/Game/SideScrollerCPP/Blueprints/SideScrollerCharacter"));
static ConstructorHelpers::FClassFinder<APawn> PlayerPawnBPClass(TEXT("/Game/Blueprints/BP_FighingGameCharacter"));
if (PlayerPawnBPClass.Class != NULL)
{
DefaultPawnClass = PlayerPawnBPClass.Class;
}
PlayerControllerClass = AFightingGamePlayerController::StaticClass();
}
void AFighingGameGameMode::BeginPlay()
{
Super::BeginPlay();
UGameplayStatics::CreatePlayer(GetWorld());
}
Code of my player controller class:
#pragma once
#include "GameFramework/PlayerController.h"
#include "FightingGamePlayerController.generated.h"
/**
*
*/
UCLASS(config=Game)
class FIGHINGGAME_API AFightingGamePlayerController : public APlayerController
{
GENERATED_BODY()
public:
AFightingGamePlayerController();
virtual void InitInputSystem() override;
private:
void HorizontalMovePawn(float AxisValue);
void VerticalMovePawn(float AxisValue);
};
#include "FighingGame.h"
#include "FightingGamePlayerController.h"
#include "FighingGameCharacter.h"
AFightingGamePlayerController::AFightingGamePlayerController()
{
}
void AFightingGamePlayerController::InitInputSystem()
{
Super::InitInputSystem();
UE_LOG(LogTemp, Warning, TEXT("AFightingGamePlayerController::InitInputSystem()"));
if (GetLocalPlayer() != NULL)
{
switch (GetLocalPlayer()->GetControllerId())
{
case 0:
InputComponent->BindAxis("HorizontalMoveForFirstPlayer", this, &AFightingGamePlayerController::HorizontalMovePawn);
InputComponent->BindAxis("VerticalMoveForFirstPlayer", this, &AFightingGamePlayerController::VerticalMovePawn);
break;
case 1:
InputComponent->BindAxis("HorizontalMoveForSecondPlayer", this, &AFightingGamePlayerController::HorizontalMovePawn);
InputComponent->BindAxis("VerticalMoveForSecondPlayer", this, &AFightingGamePlayerController::VerticalMovePawn);
break;
default:
break;
}
}
}
void AFightingGamePlayerController::HorizontalMovePawn(float Value)
{
GetPawn()->AddMovementInput(FVector(0.f, -1.f, 0.f), Value);
}
void AFightingGamePlayerController::VerticalMovePawn(float Value)
{
GetPawn()->AddMovementInput(FVector(-1.f, 0.f, 0.f), Value);
}
Code of my game viewport client class:
#pragma once
#include "Engine/GameViewportClient.h"
#include "FightingGameViewportClient.generated.h"
/**
*
*/
UCLASS(Within = Engine, transient, config = Engine)
class FIGHINGGAME_API UFightingGameViewportClient : public UGameViewportClient
{
GENERATED_UCLASS_BODY()
public:
#if WITH_HOT_RELOAD_CTORS
/** DO NOT USE. This constructor is for internal usage only for hot-reload purposes. */
UFightingGameViewportClient(FVTableHelper& Helper);
#endif // WITH_HOT_RELOAD_CTORS
UFightingGameViewportClient();
//UFightingGameViewportClient(class FObjectInitializer const& Initializer);
virtual ~UFightingGameViewportClient();
virtual bool InputKey(FViewport* Viewport, int32 ControllerId, FKey Key, EInputEvent EventType, float AmountDepressed = 1.f, bool bGamepad = false) override;
};
#include "FighingGame.h"
#include "EngineGlobals.h"
#include "Engine/Engine.h"
#include "Engine.h"
#include "FightingGameViewportClient.h"
#include "Engine/Console.h"
#include "SlateApplication.h"
UFightingGameViewportClient::UFightingGameViewportClient()
{
}
UFightingGameViewportClient::UFightingGameViewportClient(class FObjectInitializer const& Initializer)
: Super(Initializer)
{
}
#if WITH_HOT_RELOAD_CTORS
/** DO NOT USE. This constructor is for internal usage only for hot-reload purposes. */
UFightingGameViewportClient::UFightingGameViewportClient(FVTableHelper& Helper)
: Super(Helper)
{
}
#endif // WITH_HOT_RELOAD_CTORS
UFightingGameViewportClient::~UFightingGameViewportClient()
{
}
bool UFightingGameViewportClient::InputKey(FViewport* InViewport, int32 ControllerId, FKey Key, EInputEvent EventType, float AmountDepressed, bool bGamepad)
{
if (IgnoreInput())
{
return ViewportConsole ? ViewportConsole->InputKey(ControllerId, Key, EventType, AmountDepressed, bGamepad) : false;
}
if (Key == EKeys::Enter && EventType == EInputEvent::IE_Pressed && FSlateApplication::Get().GetModifierKeys().IsAltDown() && GetDefault<UInputSettings>()->bAltEnterTogglesFullscreen)
{
HandleToggleFullscreenCommand();
return true;
}
if (InViewport->IsPlayInEditorViewport() && Key.IsGamepadKey())
{
GEngine->RemapGamepadControllerIdForPIE(this, ControllerId);
}
// route to subsystems that care
bool bResult = (ViewportConsole ? ViewportConsole->InputKey(ControllerId, Key, EventType, AmountDepressed, bGamepad) : false);
if (!bResult)
{
const TArray<class ULocalPlayer*>& GamePlayers = GEngine->GetGamePlayers(this);
for (ULocalPlayer *GamePlayer : GamePlayers)
{
if (GamePlayer && GamePlayer->PlayerController)
{
bResult = bResult & GamePlayer->PlayerController->InputKey(Key, EventType, AmountDepressed, bGamepad);
}
}
// A gameviewport is always considered to have responded to a mouse buttons to avoid throttling
if (!bResult && Key.IsMouseButton())
{
bResult = true;
}
}
// For PIE, let the next PIE window handle the input if we didn't
// (this allows people to use multiple controllers to control each window)
if (!bResult && ControllerId > 0 && InViewport->IsPlayInEditorViewport())
{
UGameViewportClient *NextViewport = GEngine->GetNextPIEViewport(this);
if (NextViewport)
{
bResult = NextViewport->InputKey(InViewport, ControllerId - 1, Key, EventType, AmountDepressed, bGamepad);
}
}
return bResult;
}