캐릭터 모델
Pawn 대신 Character 액터를 만들어 조작해보자. Character 액터는 Pawn 액터를 상속받는데, Pawn 과 다른 점은 CharacterMovement 컴포넌트를 사용해 움직임을 관리한다는 것이다(Actor>>Pawn>>Character). 이 컴포넌트가 FloatingMovement 에 비해 가지는 장점은 다음과 같다.
- 점프와 같은 중력을 반영한 움직임을 제공
- 다양한 움직임 설정 가능(기어가기, 날아가기, 수영하기 등등) + 현재 움직임에 좀 더 많은 정보 전달
- 멀티 플레이 네트워크 환경에서 캐릭터들의 움직임을 자동으로 동기화한다.
코드에서 불러올 WarrioranimBlueprint애니메이션블루프린트를 확인한다. 없다면 간단히 만들어보자
애니메이션 블루프린트 생성
애니메이션 폴더를 열고 빈공간에 우클릭히 애니메이션블루프린트를 선택한다.
설정창이 열리면 SK_Mannequin_Skeleton을 선택하고 Create후 이름을 WarrioranimBlueprint로 한다.
WarrioranimBlueprint을 열고 에셋브라우저에서 WarriorRun을 끌어다 OutputPose와 연결한다. 컴파일하고 저장한다.
WarrioranimBlueprint
캐릭터C++클래스 생성
이제 ABCharacter C++클래스를 만든다.
ABPawn과 마찬가지로 ABCharacter.h에 SpringArm, Camera, UpDown, LeftRight를 추가한다.
ABCharater클래스는 ACharacter를 상속받은 클래스다 ACharacter위에 마우스커서를 놓고 F12를 눌러 소스를 보자
class ARENABATTLE_API AABCharacter : public ACharacter
APawn을 상속받았고 Mesh, CharacterMovement CapsuleComponent등이 이미 포함되어 있다. 이들을 사용하기 위해서는 GetMesh() GetCapsule()등의 메써드를 사용해야한다.
class ENGINE_API ACharacter : public APawn
{
private:
/** The main skeletal mesh associated with this Character (optional sub-object). */
UPROPERTY(Category=Character, VisibleAnywhere, BlueprintReadOnly, meta=(AllowPrivateAccess = "true"))
TObjectPtr<USkeletalMeshComponent> Mesh;
/** Movement component used for movement logic in various movement modes (walking, falling, etc), containing relevant settings and functions to control movement. */
UPROPERTY(Category=Character, VisibleAnywhere, BlueprintReadOnly, meta=(AllowPrivateAccess = "true"))
TObjectPtr<UCharacterMovementComponent> CharacterMovement;
/** The CapsuleComponent being used for movement collision (by CharacterMovement). Always treated as being vertically aligned in simple collision check functions. */
UPROPERTY(Category=Character, VisibleAnywhere, BlueprintReadOnly, meta=(AllowPrivateAccess = "true"))
TObjectPtr<UCapsuleComponent> CapsuleComponent;
ACharacter.h에 springArm과 Camera 그리고 UpDown, Leftright Axis를 추가한다. APawn.h에서 카피하면 된다.
#include "ABLog.h" //추가 EngineMinimal.h가 필요
...
UPROPERTY(VisibleAnywhere, Category = Camera)
USpringArmComponent* SpringArm;
UPROPERTY(VisibleAnywhere, Category = Camera)
UCameraComponent* Camera;
private:
void UpDown(float NewAxisValue);
void LeftRight(float NewAxisValue);
ACharacter.cpp 생성자를 다음과 같이 입력한다.
코드중 USkeletalMesh와 UAnimationBlueprint등의 패스는 에셋을 선택후 Ctrl-C를 하거나 우클릭히 레퍼런스를 카피해도 된다.
AABCharacter::AABCharacter()
{
// Set this character to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
SpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("SPRINGARM"));
Camera = CreateDefaultSubobject<UCameraComponent>(TEXT("CAMERA"));
SpringArm->SetupAttachment(GetCapsuleComponent());
Camera->SetupAttachment(SpringArm);
GetMesh()->SetRelativeLocationAndRotation(FVector(0.0f, 0.0f, -88.0f), FRotator(0.0f, -90.0f, 0.0f));
SpringArm->TargetArmLength = 400.0f;
SpringArm->SetRelativeRotation(FRotator(-15.0f, 0.0f, 0.0f));
// 캐릭터 메시를 가져온다.
static ConstructorHelpers::FObjectFinder<USkeletalMesh> SK_CARDBOARD(TEXT("/Game/InfinityBladeWarriors/Character/CompleteCharacters/SK_CharM_Cardboard.SK_CharM_Cardboard"));
if (SK_CARDBOARD.Succeeded())
{
GetMesh()->SetSkeletalMesh(SK_CARDBOARD.Object);
}
GetMesh()->SetAnimationMode(EAnimationMode::AnimationBlueprint);
static ConstructorHelpers::FClassFinder<UAnimInstance> WARRIOR_ANIM(
TEXT("/Game/Book/Animations/WarriorAnimBlueprint.WarriorAnimBlueprint_C"));
if (WARRIOR_ANIM.Succeeded())
{
GetMesh()->SetAnimInstanceClass(WARRIOR_ANIM.Class);
}
}
Input을 바인딩해주고 Axis를 구현해준다.
void AABCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
PlayerInputComponent->BindAxis(TEXT("UpDown"), this, &AABCharacter::UpDown);
PlayerInputComponent->BindAxis(TEXT("LeftRight"), this, &AABCharacter::LeftRight);
}
void AABCharacter::UpDown(float NewAxisValue) {
//ABLOG(Warning, TEXT("%f"), NewAxisValue);
AddMovementInput(GetActorForwardVector(), NewAxisValue);
}
void AABCharacter::LeftRight(float NewAxisValue) {
//ABLOG(Warning, TEXT("%f"), NewAxisValue);
AddMovementInput(GetActorRightVector(), NewAxisValue);
}
ABGameModeBase.cpp에 ACharacter를 DefaultPawn으로 등록해준다. ACharacter.h 헤더도 추가해준다. 플레이해보면 비슷한 상태이나 폰모델과 다른 점은 CharacterMovement컴포넌트를 사용한다는 점이다. 장점은
1. 점프와 같은 중력을 반영한 움직임을 제공한다.
2. 다양한 움직임을 설정할 수 있다. 걷기 외에도 기어가기, 날아가기, 수용하기 등의 다양한 이동 모드를 설정가능하다.
3. 멀티 플레이 네트워크 환경에서 캐릭터들의 움직임을 자동으로 동기화 한다.
컨트롤 회전의 활용
플레이어가 게임에 입장할때 부여받는 두 종류의 액터로 플레이어 컨트롤러와 폰이 있다. 플레이어와 1:1로 매칭돼 플레이어의 의지를 전달하는 플레이어의 컨트롤러는 주로 게임세계의 물리적인 요소를 고려하지 않는 플레이어의 의지에 관련된 데이터를 관리한다. 반면에 폰은 게임 세계에서 물리적인 제약을 가지기 때문에 현재 캐릭터가 처한 물리적인 상황을 관리한다. 폰이 관리하는 중요한 속성은 속도다. 반면에 플레이어 컨트롤러는 컨트롤 회전이라는 속성을 제공한다.
마우스의 상하좌우 값은 삼인칭 템플릿에서 제공하는 Turn과 LookUp Axis에서 받아올수 있다. 플레이어의 회전을 위해서는 AddControllerInputYaw, Roll, Pitch라는 세가지 명령을 제공한다.
#include "ABGameModeBase.h"
#include "ABPlayerController.h"
#include "ABCharacter.h"
//AABGameModeBase() 생성자
AABGameModeBase::AABGameModeBase()
{
DefaultPawnClass = AABCharacter::StaticClass(); //디폴트PawnClass지정
PlayerControllerClass = AABPlayerController::StaticClass(); //디폴트PlayerController지정
}
ABCharacter.h 맨아래 LookUP(), Turn()을 선언해주고
private:
void UpDown(float NewAxisValue);
void LeftRight(float NewAxisValue);
void LookUp(float NewAxisValue);
void Turn(float NewAxisValue);
ABCharacter.cpp에서 Binding해주고 구현해준다.
void AABCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
PlayerInputComponent->BindAxis(TEXT("UpDown"), this, &AABCharacter::UpDown);
PlayerInputComponent->BindAxis(TEXT("LeftRight"), this, &AABCharacter::LeftRight);
PlayerInputComponent->BindAxis(TEXT("LookUp"), this, &AABCharacter::LookUp);
PlayerInputComponent->BindAxis(TEXT("Turn"), this, &AABCharacter::Turn);
}
void AABCharacter::LookUp(float NewAxisValue)
{
AddControllerPitchInput(NewAxisValue);
}
void AABCharacter::Turn(float NewAxisValue)
{
AddControllerYawInput(NewAxisValue);
}
VS에서 빌드후 자동로딩후 플레이해보면 카메라와 캐릭터가 동시에 Turn은 되는데 LookUp은 안된다.
왜 이렇게 동작하는지 분석해보자
플레이후 ~키를 누르면 콘솔이 나타나고 displayall PlayerController ControlRotation을 입력하면 화면에 Rotation값이 나타난다.
마우스 상하좌우가 잘 입력된다. 하지만 디테일에서 보면 Z Rotation 값만 움직인다. 이건 언리얼의 Z축과 폰의 Z축이 서로 연동돼 있다. Pawn카테고리 UseControllerRotationYaw 만 설정되어 있어서 그렇다. 이설정으로 마우스의 좌우만 반응하게 된다. Pitch도 설정해주면 아래위로 움직이게 된다.
SprinArm의 Use Pawn Control Rotaion을 켜줘도 움직인다.
플레이어 컨트롤러는 게임에서 플레이어의 입력과 출력되는 화면을 책임진다.
현재 플레이 버튼을 눌러 콘텐츠를 테스트할 때 매번 언리얼 에디터 뷰포트를 클릭해 포커스를 잡아야 입력 신호가 비로소 게임에 전달된다. 다음과 같이 코드를 추가하면 바로 게임을 시작할 수 있다.
ABPlayerController.h
protected:
virtual void BeginPlay() override;
ABPlayerController.cpp
void AABPlayerController::BeginPlay()
{
FInputModeGameOnly InputMode;
SetInputMode(InputMode);
}
'언리얼C++게임개발 > 06.캐릭터의 제작과 컨트롤' 카테고리의 다른 글
삼인칭 컨트롤 구현(GTA방식) (0) | 2023.11.04 |
---|