Actor
언리얼 엔진의 레벨(월드)에 배치할 수 있는 tranform을 가진 오브젝트
Pawn
플레이어나 AI가 제어할 수 있는 액터(액터를 상속 한다)
Character
폰에서에서 좀 더 복잡한 애니메이션을 위해 파생된 클래스. 예를 들어 점프하고 수영하고, 하늘을 날아다니는 애니메이션들과 그 애니메이션들을 블렌딩하는 작업 등 복잡한 애니메이션을 처리할 수 있다.
실사용 예시
Actor | Pawn | Character | |
이동 | X | O | O |
애니메이션 | X | X | O |
플레이어, AI가 제어 | X | O | O |
예시 | 건물 | 체스 말 | 사람, 몬스터 |
출처: https://tenlie10.tistory.com/183 [게임 엔진/클라이언트 개발자:티스토리]
● 플레이어가 조종할 수 있는 특수한 액터인 폰은 움직이는 액터에 조종당하는 기능이 추가된 액터이다.
● 기존에 만들어져 있는 마네킹과 같은 애니메이션이 있는 인간형 폰을 만들려면 고려할 게 많다.
- 시각적 요소 : 인간형 폰이 되려면 애니메이션 기능이 필요하다. 애니메이션을 재생하도록 리깅 데이터를 추가한 메시를 스켈레탈 메시라고 하며, 이를 관리하는 컴포넌트는 스켈레탈 메시 컴포넌트이다.
- 충돌 요소 : 스켈레탈 메시는 애니메이션에 따라 변하므로 충돌을 담당할 충돌 컴포넌트를 별도로 사용하는 것이 적합하다. 인간형의 경우 캡슐 컴포넌트를 사용한다.
- 움직임 요소 : 언리얼 엔진은 폰의 움직임을 위해 플레이어의 입력에 따라 반응하는 폰 무브번트 컴포넌트라 부르는 특별한 컴포넌트를 제공하고 있다. 제공되는 폰 무브먼트 컴포넌트는 FloatingPawnMovement, CharacterMovement이다.
- 내비게이션 : 폰은 언리얼 엔진의 내비게이션 시스템과 연동되어 있어 목적지를 알려주면 스스로 목적지까지 이동하는 길 찾기 기능을 갖고 있다.
- 카메라 출력 : 플레이어가 조종하는 폰은 플레이어로부터 입력을 해석해 이동하지만, 동시에 자신이 보는 게임 세계를 플레이어의 모니터에 전송해 줘야 한다.
마켓플레이스로 가서 InfinityBlade:Warriors패키지를 다운로드하고 프로젝트로 추가한다. 버전 5.3까지 지원하니 문제 없이 쓸수 있다.


언리얼에디터의 다음 폴더에서 다양한 캐릭터들을 확인할 수 있다.

언리얼 엔진은 폰에 카메라 컴포넌트를 부착하면, 플레이어 컨트롤러가 폰에 빙의 할 때 자동으로 폰에 부착된 카메라의 상을 플레이어의 화면으로 전송한다.
● 인간형 폰을 만들 때, 위 시각적 요소와 충돌 요소를 위해서 스켈레탈 메시와 캡슐 컴포넌트를 추가해야 한다. 각 컴포넌트의 역할은 다음과 같다.
- Capsule : 폰의 움직임을 담당하는 충돌 컴포넌트이다. 보통 충돌 영역은 캐릭터 메시가 들어갈 만큼의 크기로 설정한다. 그리고 이 캡슐 컴포넌트는 폰을 대표해 게임 세계에서의 움직임을 담당할 예정이므로 루트 컴포넌트로 설정한다.
- SkeletalMesh : 캐릭터 애셋을 보여주고 추가로 애니메이션도 담당한다.
- FloatingPawnMovement : 플레이어의 입력에 따라 캐릭터가 움직이도록 설정해 주는 컴포넌트이다. 이 무브먼트를 사용하면 중력을 고려하지 않은 간단한 움직임을 구현할 수 있다.
- SpringArm : 스프링암은 삼인칭 시점으로 카메라 구도를 편리하게 설정할 수 있는 부가 컴포넌트.
- Camera : 폰에 카메라 컴포넌트 부착하면 언리얼 엔진은 카메라가 바라보는 게임 세계의 화면을 플레이어의 화면으로 전송한다. 3인칭 시점을 구현할 때 카메라 컴포넌트를 스트링 암 컴포넌트의 자식으로 설정하고, 트랜스폼 정보를 초기화하면 카메라는 자동으로 스프링 암 끝에 걸린다.
폰의 컴포넌트를 설정한 코드는 다음과 같다.
ABPawn.h publc:에 넣어준다 새로운 헤더파일도 넣어준다.
#include "GameFramework/FloatingPawnMovement.h"
...
class ARENABATTLE_API AABPawn : public APawn
{
...
public:
UPROPERTY(VisibleAnywhere, Category=Collision)
UCapsuleComponent* Capsule;
UPROPERTY(VisibleAnywhere, Category=Visual)
USkeletalMeshComponent* Mesh;
UPROPERTY(VisibleAnywhere, Category=Movement)
UFloatingPawnMovement* Movement;
UPROPERTY(VisibleAnywhere, Category=Camera)
USpringArmComponent* SpringArm;
UPROPERTY(VisibleAnywhere, Category=Camera)
UCameraComponent* Camera;
}
ABPawn.cpp
생성자에 넣어준다
Capsule = CreateDefaultSubobject<UCapsuleComponent>(TEXT("CAPSULE"));
Mesh = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("MESH"));
Movement = CreateDefaultSubobject<UFloatingPawnMovement>(TEXT("MOVEMENT"));
SpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("SPRINGARM"));
Camera = CreateDefaultSubobject<UCameraComponent>(TEXT("CAMERA"));
RootComponent = Capsule;
Mesh->SetupAttachment(Capsule);
SpringArm->SetupAttachment(Capsule);
Camera->SetupAttachment(SpringArm);
Capsule->SetCapsuleHalfHeight(88.0f);
Capsule->SetCapsuleRadius(34.0f);
Mesh->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())
{
Mesh->SetSkeletalMesh(SK_CARDBOARD.Object);
}
Mesh의 위치를 -88.0f 만큼, 회전(Yaw)을 -90.0f 만큼 조정해 주었다. SpringArm 은 -15.0f 만큼 조정해 캐릭터의 어깨 너머의 배경이 보이도록 설정했다.
위와 같은 설정을 하면 카드보드 액터가 등장한다.



이미 애니메이션을 적용해서 달리고 있음
폰의 조작
이제 폰을 움직일 수 있도록 입력에 대한 처리 로직과 무브먼트를 설정해 보자. 프로젝트 세팅으로 들어가 입력 매핑을 추가해 보자. UpDown, LegtRight Axis를 다음과 같이 넣어준다.

액션 매핑은 일회성 값을, 축 매핑은 지속적인 입력 값을 측정한다
바인딩을 설정한 후에 InputComponent 의 BindAxis 와 BindAction 이라는 함수를 이용해 폰의 움직임을 연결시켜 준다.
ABPawn.h
UCLASS()
class ARENABATTLE_API AABPawn : public APawn
{
GENERATED_BODY()
...
private:
void UpDown(float NewAxisValue);
void LeftRight(float NewAxisValue);
...
ABPawn.cpp
// Called to bind functionality to input
void AABPawn::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
PlayerInputComponent->BindAxis(TEXT("UpDown"), this, &AABPawn::UpDown);
PlayerInputComponent->BindAxis(TEXT("LeftRight"), this, &AABPawn::LeftRight);
}
void AABPawn::UpDown(float NewAxisValue) {
//ABLOG(Warning, TEXT("%f"), NewAxisValue);
AddMovementInput(GetActorForwardVector(), NewAxisValue);
}
void AABPawn::LeftRight(float NewAxisValue) {
//ABLOG(Warning, TEXT("%f"), NewAxisValue);
AddMovementInput(GetActorRightVector(), NewAxisValue);
}
먼저 UInputComponent 의 BindAxis 함수를 이용해 입력 바인딩을 연결하고, AddMovementInput 함수를 이용해 액터의 벡터 방향으로 입력값을 더해준다.
폰을 조정하기 위해 입력 로직은 폰클래스에서 구현하는 것이 일반적이다 하지만 언리얼에서는 입력 시스템은 중간에 컨트롤러를 거쳐서 폰에 전달된다. 만일 플레이어 컨트롤러에서 특정 입력을 처리하면 해당 입력은 필터링되어 더이상 폰에 전달되지 않는다.
ABPlayerController.h
protected:
virtual void BeginPlay() override;
virtual void SetupInputComponent() override;
private:
void LeftRight(float NewAxisValue);
ABPlayerController.cpp
void AABPlayerController::SetupInputComponent()
{
Super::SetupInputComponent();
InputComponent->BindAxis(TEXT("LeftRight"), this, &AABPlayerController::LeftRight);
}
void AABPlayerController::LeftRight(float NewAxisValue)
{
if(NewAxisValue)
ABLOG(Warning,TEXT("NewAxisValue = %f"),NewAxisValue);
}
위와 같이 하면 좌우키가 움직이지 않고 로그를 찍어낸다. 실험이 끝난후 이 코드는 제거한다.
현재 플레이시 매번 마우스로 뷰포트를 클릭해야하는데 플레이어 컨트롤러에게 UI 를 배제하고 게임에게만 입력을 전달하도록 명령을 내려 앞으로 편리하게 게임을 테스트해 보자.
ABPlayerController.h
protected:
virtual void BeginPlay() override;
ABPlayerController.cpp
void AABPlayerController::BeginPlay()
{
Super::BeginPlay();
FInputModeGameOnly InputMode;
SetInputMode(InputMode);
}
애니메이션의 설정
예제파일 Resource>Chapter5폴더안의 내용을 적당한 곳에 복사후 Content/Book폴더밑에 Animations폴더를 만들고 우클릭후 Animation을 Import클릭한다. 브라우저창이 뜨면 모든 애니메이션 파일을 선택해주면


FBX Import Option이 뜨는데 Mesh Skeleton에서 SK_Mannequin_Skeleton을 선택한다. Import All을 클릭한다.


Animations폴더안에 애니메이션이 Import되어 있고 클릭하면 움직임을 확인할 수 있다.

애니메이션 애셋을 불러들이는 코드를 ABPawm/cpp BeginPlay()에 설정할 수 있다.
Animations폴더의 WarriorRun위를 우클릭해서 Copy Reference를 한후 LoadObject()(nullptr,TEXT("레퍼런스 붙여넣기")) 한다. 작은 따옴표 안에만 남겨둔다.

ABPawn.cpp
// Called when the game starts or when spawned
void AABPawn::BeginPlay()
{
Super::BeginPlay();
Mesh->SetAnimationMode(EAnimationMode::AnimationSingleNode);
UAnimationAsset* AnimAsset = LoadObject<UAnimationAsset>(nullptr,
TEXT("/Game/Book/Animations/WarriorRun.WarriorRun"));
if (AnimAsset != nullptr)
{
Mesh->PlayAnimation(AnimAsset, true);
}
}
실행해보면 캐릭터는 달리는 애니메이션이 작동하고 있고 이동키와 독립적으로 작동한다.

위의 코드는 EAnimationMode::AnimationSingleNode 로 보아, 메시가 단일 애니메이션을 사용하는 것을 알 수 있다. 하지만, 애니메이션 블루프린트(애님 그래프로 구성된)를 사용하면 상황에 따른 애니메이션 전환이 더 자유로워진다.
Animations폴더위를 우클릭후 추가>애니메이션>애니메이션블루프린트를 선택한다.


캐릭터 스켈레톤 에셋을 앞서 지정한 SK_Mannequin_Skeleton으로 선택한다.
블루프린트가 만들어지면 이름을 WarriorAnimBlueprint라고 하고 열어본다.
에디터가 열리면 AnimGraph탭을 선택하고 오른쪽 하단 Asset Browser의 WarriorRun을 끌어다 그래프의 Out Pose 왼쪽 드래그한다.


WarriorRun과 OutputPose노드를 사람핀을 이용해 연결해준다.


컴파일 저장후 잠시 애니메이션이 동작하고 노드의 흐름이 보인다.

스켈레탈 메시 컴포넌트는 자신이 관리하는 캐릭터의 애니메이션을 이 애님 인스턴스에 위임하는 구조로 설계되어 있다. 스켈레탈 메시가 애니메이션 블루 프린트를 실행시키려면 블루 프린트 애셋의 클래스 정보를 애님 인스턴스 속성에 지정해 줘야 한다.
방금만든 Animation Blueprint는 Animations폴더에서 확인할수 있고

우클릭해서 생성한 애니메이션 블루 프린트 레퍼런를 복사하여 ConstructorHelpers::FClassFInder 코드로 지정해 주면 된다.
스켈레탈 메시 컴포넌트에 애니메이션 블루 프린트의 클래스 정보를 등록하면, 컴포넌트는 인스턴스를 생성해서 애니메이션을 관리하도록 동작한다.
여기서 블루 프린트 애셋을 사용하기 때문에 경로 끝에 '_C'를 추가로 붙여 클래스 정보를 가져오는 경로를 써서 스켈레탈 메시 컴포넌트의 애니메이션 블루 프린트 클래스에 등록해야 한다.
이전 코드를 지우고 블루프린트를 불러온후 실행해보면 같은 결과를 볼 수 있다.
ABPawn.cpp BeginPlay() 에 추가한 코드를 지우고 생성자 안에 넣어준다
// Called when the game starts or when spawned
void AABPawn::AABPawn()
{
...
Mesh->SetAnimationMode(EAnimationMode::AnimationBlueprint);
static ConstructorHelpers::FClassFinder<UAnimInstance> WARRIOR_ANIM(
TEXT("/Game/Book/Animations/WarriorAnimBlueprint.WarriorAnimBlueprint_C"));
if (WARRIOR_ANIM.Succeeded())
{
Mesh->SetAnimInstanceClass(WARRIOR_ANIM.Class);
}
...
}
라이브코딩후 동작시키면 전과 동일하다