본문 바로가기

게임 개발/이득우의 언리얼 C++

[이득우의 언리얼 C++ 게임개발의 정석] #7. 애니메이션 시스템의 설계

반응형

이득우의-언리얼-C++-게임개발의-정석-표지

애니메이션 블루프린트의 기반을 이루는 '애님 인스턴스'클래스를 C++로 제작하고

애님 인스턴스 클래스의 데이터를 기반으로 캐릭터가 애니메이션을 체계적으로 재생할 수 있도록

스테이트 머신에 기반한 애니메이션 시스템 제작 방법

<1>. 애니메이션 블루프린트 = '애님 그래프' + '애님 인스턴스'

  • 애님 인스턴스 : 스켈레탈 메시를 소유하는 폰의 정보를 받아 애님 그래프가 참조할 데이터 제공.
  • 애님 그래프 : 애님 인스턴스의 변수 값에 따라 변화하는 애니메이션 시스템 설계하는 공간.

※ C++로 애님 인스턴스를 제작 → 폰의 현재 속력을 애님 인스턴스에 저장 → 이 값에 따라 '애님 그래프'에서는 IDLE 혹은 RUN 애니메이션 재생하도록 해보자

AnimInstance를 부모로 하는 ABAnimInstance라는 c++ 클래스 생성.

//ABAnimInstance.h

class ARENABATTLE_API UABAnimInstance : public UAnimInstance
{
	...
public:
	UABAnimInstance(); //생성자 선언. 생성자는 보통 캐릭터의 속성값을 설정해주는 역할인듯.
private:
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category=Pawn, Meta=(AllowPrivateAccess=true))
    //애님 그래프에서 사용할 수 있도록, 블루프린트에서 접근할 수 있도록
    float CurrentPawnSpeed;
};
//ABAnimInstance.cpp

UABAnimInstance::UABAnimInstance()
{
	CurrentPawnSpeed = 0.0f;
}

 

애니메이션 블루프린트 → 클래스 세팅 →부모 클래스 정보 ABAnimInstance로 변경 → CurrentPawnSpeed 변수 사용가능.

애님그래프

 

CurrentPawnSpeed, 'float > float'노드, blend(bool로 포즈를 블렌딩 하는 기능)으로 'CurrentPawnSpeed'를 바탕으로 모션을 재생하는 노드를 만들 수 있다.

 

<2>. 폰과 데이터 연동

실제 게임에서 폰의 속도에 따라 애니메이션을 재생하려면 '프레임마다 폰의 속력 = 애님 인스턴스의 CurrentPawnSpeed'여야 한다 → 그렇게 하려면 애님 인스턴스의 Tick에서 폰의 속도 정보를 가져온 후 CurrentPawnSpeed에 업데이트한다.

//ABAnimInstance.h

class ARENABATTLE_API UABAnimInstance : public UAnimINstance
{
	...
public:
	virtual void NativeUpdateAnimation(float DeltaSeconds) override;
    //틱마다 호출되는 가상 함수. 애님 인스턴스 클래스 제공
	...
}
//ABAnimInstance.cpp

void UABAnimInstance::NativeUpdateAnimation(float DeltaSeconds)
{
	Super::NativeUpdateAnimation(DeltaSeconds);
    
    auto Pawn = TryGetPawnOwner(); //폰에 접근해 폰의 속력값을 얻어온다.
    if(::IsValid(Pawn))             //폰 객체가 유효한지? 앞에서 제거됐는지?
    {
    	CurrentPawnSpeed = Pawn->GetVelocity().Size();
    }
}

 

※스테이트 : 캐릭터가 반복해서 재생해야 할 애니메이션 동작

<3>. 점프 기능의 구현

폰이 점프를 하게 하는 것은 쉽다. 하지만, 그냥 점프를 구현하면 점프할 때마다 달리기 모션이 재생된다.

→ 폰이 점프 중인지에 대해 IsFalling() 함수를 통해 알 수 있다.

→ IsFalling()을 통해 얻은 폰의 점프 상황을 보관하기 위해 애님 인스턴스에 IsInAir라는 Boolean 속성 선언.

//ABAnimInstance.h

class ARENABATTLE_API UABAnimInstance : public UAnimInstance
{
private:
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Pawn, Meta = AllowPrivateAccess = true)
   	bool IsInAir;
};
//ABAnimInstance.cpp

UABAnimInstance::UABAnimInstance()
{
	IsInAir = false;
}

void UABAnimInstance::NativeUpdateAnimation(float DeltaSeconds)
{
	if(::IsValid(Pawn))
    {
    	...
        auto Character = Cast<ACharacter>(Pawn); //Pawn을 Character로 변환
        if (Character)
        {
        	IsInAir = Character->GetMovementComponent()->IsFalling();
        }
    }
}

 

※스테이트 편집창에서 Ground, Jump 스테이트를 만들고 양방향 트랜지션을 추가해 준다.

Ground → Jump : IsInAir

Jump → Ground : IsInAir - Not

 

<4>. 애니메이션 리타겟

인간형 캐릭터의 경우, 스켈레톤의 구성이 달라도 애니메이션을 공유할 수 있도록 '애니메이션 리타겟' 기능이 있다.

 

<5>. 점프의 구현

점프 애니메이션을 좀 더 사실적으로 표현하기 위해 JumpStart, JumpLoop, JumpEnd로 구분 짓는다.

→2024/6/18 기준 MM_Jump, MM_Fall_Loop, MM_Land라는 이름으로 되어있다.

 

Ground → JumpStart : IsInAir

JumpStart →JumpLoop : Time Remaining(0.1 /  <)

JumpLoop →JumpEnd : IsInAir - Not

JumpEnd →Ground : Time Remaining(0.1 /  <)

 

!! 지금 날짜 기준으로 예제를 실행하면 Land 할 때 땅으로 꺼지는 현상이 발생한다.

이는 MM_Land가 Additive 타입으로 돼있기 때문이다. MM_Land의 에셋 디테일의 '애디티브 세팅'에서 애디티브 애님 타입을 No Additive로 바꿔주면 정상적으로 실행된다.

 

→Time Remaining 노드를 사용하는 과정들은 트랜지션 노드에서 제공하는 'Automatic Rule Based on Sequence Player in State' 옵션으로도 할 수 있다. (둘이 같이하면 오류가 나는 것 같다)

 

 

 

[이득우의 언리얼 C++ 게임개발의 정석] #8. 애니메이션 시스템 활용

애니메이션 몽타주, 노티파이라는 기능을 활용하여연속으로 공격 명령을 내리면 콤보 공격을 순서대로 연계하는,만약 입력이 늦으면 다시 처음부터 공격을 시작하는 콤보 시스템을 구현해 보

lbto.tistory.com

 

반응형