언리얼 엔진 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