언리얼 엔진 C++에서 캐릭터 애니메이션은 게임이나 다른 대화형 경험에서 캐릭터의 동작과 움직임을 제어하기 위해 코

드를 작성하는 것이다. 이는 언리얼 엔진의 여러 C++ 클래스 및 함수, 예를 들어 캐릭터 클래스와 애니메이션 블루프린트 시스템을 사용하여 달성할 수 있다.

 

 

 


 

 

Character Animation

 

애니메이션을 가지고 있으면, C++ 코드를 사용하여 애니메이션의 타이밍 및 블렌딩을 제어하고, 플레이어 및 다른 게임 시스템에서의 입력 처리와 같은 작업을 수행할 수 있다. 이는 PlayAnimMontage()와 같은 함수를 사용하여 특정 애니메이션 시퀀스를 재생하고, 애니메이션 상태 기계를 설정하여 애니메이션이 서로 전환하는 방식을 제어하는 것을 포함할 수 있다.

 

언리얼 엔진 C++에서의 캐릭터 애니메이션은 3D 모델링 및 애니메이션 소프트웨어, 언리얼 엔진의 애니메이션 블루프린트 시스템 및 C++ 프로그래밍의 조합을 사용하여 게임이나 다른 대화형 경험에서 캐릭터의 동작과 움직임을 제어하는 것을 의미한다.

 

 

01_Spawn
  C01_Properties.h .cpp
C02_Mesh
C02_Mesh_Sphere
C02_Mesh_Cone
C03_Spawner.h .cpp
02_Profiler
  C01_Log.h .cpp
C02_DrawDebug.h .cpp
03_Collision
  C01_ActorOverlap.h .cpp 생성
Utilities
  CHelpers.h
 
CAnimInstance.h .cpp
CGameMode.h .cpp
CPlayer

 


 

CHelper - 체크용 매크로 추가

 

CHelper.h

더보기
#pragma once
#include "CoreMinimal.h"
//검증용 매크로
#define CheckTrue(x) { if(x == true) return; }
#define CheckTrueResult(x, y) { if(x == true) return y; }//리턴이 필요한 경우
#define CheckFalse(x) { if(x == false) return;}
#define CheckFalseResult(x, y) { if(x == false) return y;}//리턴이 필요한 경우
#define CheckNull(x) { if(x == nullptr) return;} //nullptr일 때 리턴
#define CheckNullResult(x, y) { if(x == nullptr) return y;}//리턴이 필요한 경우
#define CreateTextRender()\
{\
CHelpers::CreateComponent<UTextRenderComponent>(this, &Text, "Text", Root);\
Text->SetRelativeLocation(FVector(0, 0, 100));\
Text->SetRelativeRotation(FRotator(0, 180, 0));\
Text->SetRelativeScale3D(FVector(2));\
Text->TextRenderColor = FColor::Red;\
Text->HorizontalAlignment = EHorizTextAligment::EHTA_Center;\
Text->Text = FText::FromString(GetName().Replace(L"Default__", L""));\
}
//아래부분 변경사항 없음
class U2212_03_API CHelpers
{
public:
template<typename T>
static void CreateComponent(AActor* InActor, T** OutComponent, FName InName, USceneComponent* InParent = nullptr)
{
*OutComponent = InActor->CreateDefaultSubobject<T>(InName);
if (!!InParent)
{
(*OutComponent)->SetupAttachment(InParent);
return;
}
InActor->SetRootComponent(*OutComponent);
}
template<typename T>
static void GetAsset(T** OutObject, FString InPath)
{
ConstructorHelpers::FObjectFinder<T> asset(*InPath);
*OutObject = asset.Object;
}
template<typename T>
static void GetAssetDynamic(T** OutObject, FString InPath)
{
*OutObject = Cast<T>(StaticLoadObject(T::StaticClass(), nullptr, *InPath));
}
template<typename T>
static void GetClass(TSubclassOf<T>* OutClass, FString InPath)
{
ConstructorHelpers::FClassFinder<APawn> asset(*InPath);
*OutClass = asset.Class;
}
};

검증용 매크로 추가

  • #define CheckTrue(x) { if(x == true) return; }
  • #define CheckTrueResult(x, y) { if(x == true) return y; }
  • #define CheckFalse(x) { if(x == false) return;}
  • #define CheckFalseResult(x, y) { if(x == false) return y;}
  • #define CheckNull(x) { if(x == nullptr) return;}
  • #define CheckNullResult(x, y) { if(x == nullptr) return y;}

TextRender 매크로 추가

  • #define CreateTextRender()
  • TextRender는 자주 사용하므로 매크로로 사용하도록 추가하였다.
    • FRotator(0, 180, 0)는 Pitch, Yaw, Roll 순서이다. C++에서의 rotator 순서.
    • Text->Text = FText::FromString(GetName().Replace(L"Default__", L""))
      • 위의 코드는 TextRender 앞부분에 Default__ 부분을 없애기위해 사용.

 


 

CAnimInstance

 

CAnimInstance.h

더보기
#pragma once
#include "CoreMinimal.h" //Actor를 가지고 있다. But, Pawn과 Character는 없다.
#include "Animation/AnimInstance.h"
#include "CAnimInstance.generated.h"
UCLASS()
class U2212_03_API UCAnimInstance : public UAnimInstance
{
GENERATED_BODY()
protected:
UPROPERTY(BlueprintReadOnly, EditAnywhere, Category = "Animation")
float Speed;
public:
//언리얼에서 오버라이딩 했으면 override 붙여준다. 의무사항
void NativeBeginPlay() override;
void NativeUpdateAnimation(float DeltaSeconds) override;
private:
class ACharacter* OwnerCharacter;
};
  • #include "CoreMinimal.h" 
    • CoreMinimal 헤더는 Actor를 가지고 있다. 하지만 Pawn과 Character는 없다.
    • 따라서 캐릭터를 사용하기 위해 CAnimInstance.cpp에 "GameFramework/Character.h"를 추가한다.

 

 

CAnimInstance.cpp

더보기
#include "CAnimInstance.h"
#include "Global.h"
#include "GameFramework/Character.h"
void UCAnimInstance::NativeBeginPlay()
{
Super::NativeBeginPlay();
OwnerCharacter = Cast<ACharacter>(TryGetPawnOwner());
}
void UCAnimInstance::NativeUpdateAnimation(float DeltaSeconds)
{
Super::NativeUpdateAnimation(DeltaSeconds);
CheckNull(OwnerCharacter); //nullptr일 때 실행X. nullptr체크. CHelper에서 만든 검증 매크로 사용.
Speed = OwnerCharacter->GetVelocity().Size2D();//Velocity의 크기를 사용.
}

Runtime Mode가 아닌 경우에도 블루프린트 업데이트 애니메이션은 테스트용 Pawn들 때문에 블루프린트 에디터(Blueprint Editor) 상에서도 실행된다. 

  • 그래서 우리가 체크하기 위해 TryGetPawnOwner 캐릭터용으로 캐스팅한다.
  • OwnerCharacter = Cast<ACharacter>(TryGetPawnOwner());

 

 

 

 

Velocity의 크기

더보기

 

 

 

 

※ 부모를 콜하는 방법 - 일반적인 방법, 언리얼 Super 키워드

더보기
void UCAnimInstance::NativeBeginPlay()
{
UAnimInstance::NativeBeginPlay(); //일반적인 방법
Super::NativeBeginPlay(); //언리얼에서 지원하는 Super키워드 사용
}
void UCAnimInstance::NativeUpdateAnimation(float DeltaSeconds)
{
UCAnimInstance::NativeUpdateAnimation(DeltaSeconds);
Super::NativeUpdateAnimation(DeltaSeconds);
}

 

 

ABP_Character 생성

 

애니메이션 - 애니메이션 블루프린트 -  CAnimInstance, Skel_Mannequin - ABP_Character 생성

 

 

AnimGraph

 

 

Actor이외에 Editor에 값을 노출시켜야 한다면 EditAnywhere로 설정하여야 한다. 

 

 


 

 

CPlayer

 

CPlayer.h

더보기
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "CPlayer.generated.h"
UCLASS()
class U2212_03_API ACPlayer : public ACharacter
{
GENERATED_BODY()
private:
UPROPERTY(VisibleAnywhere)
class USpringArmComponent* SpringArm;
UPROPERTY(VisibleAnywhere)
class UCameraComponent* Camera;
public:
ACPlayer();
protected:
virtual void BeginPlay() override;
public:
virtual void Tick(float DeltaTime) override;
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
private:
void OnMoveForward(float InAxisValue);
void OnMoveRight(float InAxisValue);
void OnHorizontalLook(float InAxisValue);
void OnVerticalLook(float InAxisValue);
private:
void OnRun();
void OffRun();
};

 

 

CPlayer.cpp

더보기
#include "CPlayer.h"
#include "Global.h"
#include "CAnimInstance.h"
#include "GameFramework/SpringArmComponent.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "Camera/CameraComponent.h"
#include "Components/CapsuleComponent.h"
#include "Components/InputComponent.h"
// Sets default values
ACPlayer::ACPlayer()
{
// 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;
CHelpers::CreateComponent<USpringArmComponent>(this, &SpringArm, "SpringArm", GetCapsuleComponent());
CHelpers::CreateComponent<UCameraComponent>(this, &Camera, "Camera", SpringArm);
USkeletalMesh* mesh;
//SK_Mannequin을 레퍼런스 복사하여 경로를 넣어준다.
CHelpers::GetAsset<USkeletalMesh>(&mesh, "SkeletalMesh'/Game/Character/Mesh/SK_Mannequin.SK_Mannequin'");
GetMesh()->SetSkeletalMesh(mesh);
GetMesh()->SetRelativeLocation(FVector(0, 0, -90));
GetMesh()->SetRelativeRotation(FRotator(0, -90, 0));
TSubclassOf<UCAnimInstance> animInstance;
CHelpers::GetClass<UCAnimInstance>(&animInstance, "AnimBlueprint'/Game/ABP_Character.ABP_Character_C'");
GetMesh()->SetAnimClass(animInstance); //Mesh에 animInstance 할당
bUseControllerRotationYaw = false;
GetCharacterMovement()->bOrientRotationToMovement = true; //이동방향으로 회전
GetCharacterMovement()->MaxWalkSpeed = 400;
SpringArm->SetRelativeLocation(FVector(0, 0, 60));
SpringArm->TargetArmLength = 200;
SpringArm->bUsePawnControlRotation = true;
SpringArm->bEnableCameraLag = true;
}
// Called when the game starts or when spawned
void ACPlayer::BeginPlay()
{
Super::BeginPlay();
}
// Called every frame
void ACPlayer::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
// Called to bind functionality to input
void ACPlayer::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) //입력
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
PlayerInputComponent->BindAxis("MoveForward", this, &ACPlayer::OnMoveForward);
PlayerInputComponent->BindAxis("MoveRight", this, &ACPlayer::OnMoveRight);
PlayerInputComponent->BindAxis("HorizontalLook", this, &ACPlayer::OnHorizontalLook);
PlayerInputComponent->BindAxis("VerticalLook", this, &ACPlayer::OnVerticalLook);
PlayerInputComponent->BindAction("Run", EInputEvent::IE_Pressed, this, &ACPlayer::OnRun);
PlayerInputComponent->BindAction("Run", EInputEvent::IE_Released, this, &ACPlayer::OffRun);
}
void ACPlayer::OnMoveForward(float InAxisValue)
{
FRotator rotator = FRotator(0, GetControlRotation().Yaw, 0);
FVector direction = FQuat(rotator).GetForwardVector();
AddMovementInput(direction, InAxisValue); //이동
}
void ACPlayer::OnMoveRight(float InAxisValue)
{
FRotator rotator = FRotator(0, GetControlRotation().Yaw, 0);
FVector direction = FQuat(rotator).GetRightVector();
AddMovementInput(direction, InAxisValue); //이동
}
void ACPlayer::OnHorizontalLook(float InAxisValue)
{
AddControllerYawInput(InAxisValue);
}
void ACPlayer::OnVerticalLook(float InAxisValue)
{
AddControllerPitchInput(InAxisValue);
}
void ACPlayer::OnRun()
{
GetCharacterMovement()->MaxWalkSpeed = 600;
}
void ACPlayer::OffRun()
{
GetCharacterMovement()->MaxWalkSpeed = 400;
}

AnimInstance를 사용하기 위해 헤더를 추가한다.

  • #include "CAnimInstance.h"

Mesh에 animInstance를 할당한다.

  • TSubclassOf<UCAnimInstance> animInstance;
  • CHelpers::GetClass<UCAnimInstance>(&animInstance, "AnimBlueprint'/Game/ABP_Character.ABP_Character_C'");
  • GetMesh()->SetAnimClass(animInstance);

BindAction

  • PlayerInputComponent->BindAction("Run", EInputEvent::IE_Pressed, this, &ACPlayer::OnRun);
  • 눌렀을 때 this에 ACPlayer에 있는 OnRun을 연결시켜준다.

 

  • IE_Axis: 축 값 입력
  • IE_DoubleClick: 더블 클릭
  • IE_Pressed: 눌렀느냐
  • IE_Released: 누르고 떼었느냐
  • IE_Repeat: 누르고 있느냐

 


 

C01_ActorOverlap

 

새 C++ 클래스 - Actor - C01_ActorOverlap 생성

 

 

C01_ActorOverlap.h

더보기
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "C01_ActorOverlap.generated.h"
UCLASS()
class U2212_03_API AC01_ActorOverlap : public AActor
{
GENERATED_BODY()
private:
UPROPERTY(VisibleAnywhere)
class USceneComponent* Root;
UPROPERTY(VisibleAnywhere)
class UBoxComponent* Box;
UPROPERTY(VisibleAnywhere)
class UTextRenderComponent* Text;
public:
AC01_ActorOverlap();
protected:
virtual void BeginPlay() override;
private:
UFUNCTION()
void BeginOverlap(AActor* OverlappedActor, AActor* OtherActor);
UFUNCTION()
void EndOverlap(AActor* OverlappedActor, AActor* OtherActor);
};

 

 

C01_ActorOverlap.cpp

더보기
#include "03_Collision/C01_ActorOverlap.h"
#include "Global.h"
#include "Components/BoxComponent.h"
#include "Components/TextRenderComponent.h"
AC01_ActorOverlap::AC01_ActorOverlap()
{
CHelpers::CreateComponent<USceneComponent>(this, &Root, "Root");
CHelpers::CreateComponent<UBoxComponent>(this, &Box, "Box", Root);
CreateTextRender();
Box->bHiddenInGame = false; //Unreal C++에서는 bool형에 앞에 b를 붙여준다.
Box->SetRelativeScale3D(FVector(3));
}
void AC01_ActorOverlap::BeginPlay()
{
Super::BeginPlay();
OnActorBeginOverlap.AddDynamic(this, &AC01_ActorOverlap::BeginOverlap);
OnActorEndOverlap.AddDynamic(this, &AC01_ActorOverlap::EndOverlap);
}
void AC01_ActorOverlap::BeginOverlap(AActor * OverlappedActor, AActor * OtherActor)
{
FString str = FString::Printf(L"Begin - Overlapped : %s, Other : %s", *OverlappedActor->GetName(), *OtherActor->GetName());
CLog::Print(str);
}
void AC01_ActorOverlap::EndOverlap(AActor * OverlappedActor, AActor * OtherActor)
{
FString str = FString::Printf(L"End - Overlapped : %s, Other : %s", *OverlappedActor->GetName(), *OtherActor->GetName());
CLog::Print(str, -1, 10, FColor::Red);
}

TextRender "Default__" 지우기

  • CHelpers 매크로 활용.

 

 

※ Runtime에 사용될 Delegation에 연결된 함수들은 전부 직렬화되어야 한다.

 

 

 

우클릭 - 새 C01_ActorOverlap 기반 블루프린트 클래스 생성 - BP_C01_ActorOverlap 생성

 

 

 


 

DELEGATE

 

DECLARE_DELEGATE

1:1 매핑. 블루프린트에서 공개X. Return 존재O

 

DECLARE_MULTICAST

1:n 매핑. 블루프린트에서 공개X. Return 존재X

 

EVENT

 

DYNAMIC

1:1 매핑. 블루프린트에서 공개O. Return 존재O

 

DYNAMIC MULTICAST

1:n 매핑. 블루프린트에서 공개O. Return 존재X

 

DYNAMIC_SPARSE_MULTICAST

-1:n 매핑. 블루프린트에서 공개O. Return 존재X ??

- DELEGATE 타입, 클래스, 명, ___, ___, ___, ____

가장 엄격한 방법. 충돌에 많이 사용.

 

타입, 변수명 일치시켜야 한다.

 

 

https://docs.unrealengine.com/4.27/ko/ProgrammingAndScripting/ProgrammingWithCPP/UnrealArchitecture/Delegates/

 

델리게이트

C++ 오브젝트 상의 멤버 함수를 가리키고 실행시키는 데이터 유형입니다.

docs.unrealengine.com

 

https://darkcatgame.tistory.com/66

 

UE4 C++ Delegate 정리 & 샘플 프로젝트

개인적으로 UE4 C++에서 Delegate를 사용할 때 처음에 굉장히 에러를 많이 겪었습니다, 그래서 이번 포스팅은 UE4 C++에서 Delegate를 사용하는 방법에 대해 정리하고 샘플프로젝트도 만들었습니다. https

darkcatgame.tistory.com

 


 

실행화면

 

 

 


 

'⭐ Unreal Engine > UE FPS TPS' 카테고리의 다른 글

[UE] Collsion(trigger, MultiTrigger, Explosion)  (0) 2023.03.13
[UE] Collision(Overlap, hit)  (0) 2023.03.09
[UE] Character, GameMode  (0) 2023.03.07
[UE] Log 출력하기, Draw Debug 구현  (0) 2023.03.03
[UE] Mesh, Log  (0) 2023.03.02