목차
- 죽었을 때 유지할 애니메이션 만들기
- 스테이트 머신 만들기
- 캐릭터의 죽음 자세를 구분할 열거형
- 애니메이션 블루프린트에서 열거형으로 상태 구분하기
- 멀티스레드 활용하기
- 열거형에 맞는 데스애니메이션 재생하기
1. 죽었을 때 유지할 애니메이션 만들기
현재 에너미가 죽으면 데스 애니메이션이 재생은 되지만 애니메이션 상태가 유지되지는 못한다. 당연한 것이 에너미의 애니메이션 블루프린트는 에너미가 죽었는지 살았는지 알 방법이 없기 때문이다.
이것을 알려줘 에너미가 죽은 상태를 유지하도록 해보자.
가장 먼저 죽었을 때 데스애니메이션이 끝나고 캐릭터가 가지고 있을 애니메이션을 만들 것이다.
데스 애니메이션에 들어가 가장 마지막 프레임을 이용해서 새로운 애니메이션을 만들 것이다.
상단의 Create Asset -> Create Animaion -> Current Pose 를 누르도록 하자.
이렇게 하면 싱글 프레임 애니메이션을 얻을 수 있다. 에너미가 죽어있는 상태(체력이 0이랑 같은 상태)이면 데스 몽타주 재생후 이 애니메이션이 나오도록 하면 된다.
이렇게 싱글 프레임 애니메이션들을 만들어주었다.
2. 스테이트 머신 만들기
이제 이러한 상태로 어떻게 전환해주어야 할까?
먼저 에너미의 애니메이션 블루프린트로 들어가자
그 다음 스테이트 머신을 새로 만든다.
이제 이 main states에서 어떤 애니메이션을 재생할 지 정할 것이다.
메인 스테이트로 들어간 뒤
이렇게 스테이트들을 만들고 재생할 애니메이션을 연결시켜놓자.
idle 애니메이션이야 하나밖에 없어서 재생하기 쉽지만 데스 애니메이션의 경우 5개가 있다. 이 중 어떤 걸 재생해야할 지 어떻게 알 수 있을까? 또 애초에 죽은 상태인지 알아보려면 어떻게 해야할까?
죽은 여부를 알기 위해서는 불리언을 이용할 수도 있지만 여기서는 열거형을 사용해 이를 구분지어보려고 한다.
3. 캐릭터의 죽음 자세를 구분할 열거형
UENUM(BlueprintType)
enum class EDeathPose: uint8
{
EDP_Alive UMETA(DisplayName = "Alive"),
EDP_Death1 UMETA(DisplayName = "Death1"),
EDP_Death2 UMETA(DisplayName = "Death2"),
EDP_Death3 UMETA(DisplayName = "Death3"),
EDP_Death4 UMETA(DisplayName = "Death4"),
EDP_Death5 UMETA(DisplayName = "Death5"),
EDP_MAX UMETA(DisplayName = "MAX")
};
위와 같이 열거형을 만들어 죽음자세를 구분하도록 하겠다.
이제 이걸 에너미 클래스에서 쓸 것이다. 에너미 헤더파일로 가보자
#include "CharacterTypes.h"
UCLASS()
class PRACTICE_API AEnemy : public ACharacter, public IHitInterface
{
GENERATED_BODY()
// 생략
protected:
UPROPERTY(BlueprintReadOnly)
EDeathPose DeathPose = EDeathPose::EDP_Alive;
// 생략
}
에너미의 처음상태는 당연이 Alive일것이다.
이제 Die 함수에서 어떤 자세로 죽는 지를 알아보고 그에 맞는 자세를 취하면 된다.
이제 Die 함수에서 데스포즈에 맞는 열거형으로 교체해주면 된다.
void AEnemy::Die()
{
UAnimInstance* AnimInstance = GetMesh()->GetAnimInstance();
if (AnimInstance && DeathMontage)
{
const int32 SectionCount = DeathMontage->GetNumSections() - 1;
const int32 Selection = FMath::RandRange(0, SectionCount);
const FName SectionName = DeathMontage->GetSectionName(Selection);
if (Selection < static_cast<int32>(EDeathPose::EDP_MAX))
{
DeathPose = static_cast<EDeathPose>(Selection + 1);
}
AnimInstance->Montage_Play(DeathMontage);
AnimInstance->Montage_JumpToSection(SectionName, DeathMontage);
}
}
열거형은 정수로 바꿀 수 있고 정수는 열거형으로 바꿀 수 있기 때문에 캐스트해서 이를 적용하기로 하였다.
이때 Selection의 경우 0부터 시작하지만 열거형에서 0은 Alive이므로 +1을 해서 올바른 애니메이션과 매칭되도록 하였다.
참고로 굳이 이렇게 안하고 노티파이를 이용해 아주 간단하게 마지막 애니메이션을 구현하는 방법이 있긴 하다는 걸 알게되었는데 현재 강의에서는 이런 방식을 사용하니 이렇게 하였지만 나중에 실제로 프로젝트를 만들면 다른 방법을 사용하는 게 좋을 것으로 보인다.
이제 애니메이션 블루프린트에서 지금이 어떤 상태인지 알고 애니메이션을 재생해야할 것이다.
4. 애니메이션 블루프린트에서 열거형으로 상태 구분하기
에너미의 상태를 알기 위해서는 에너미 클래스에 접근할 수 있어야 한다.
애니메이션 블루프린트의 이벤트 그래프로 들어가자. 캐릭터 클래스는 매 프레임마다 가져올 필요없이 가장 처음 한번만 실행하면 될것이다.
애니메이션 블루프린트에도 Begin Play가 있다.
원래 존재하던 Try Get Pawn Owner 를 BP_Enemy에 캐스팅하고 성공할 경우 이 반환 값을 변수로 저장하면 된다.
그리고 이제 이곳에서 에너미 상태를 확인하고 업데이트 하면 될 것이다.
Death Pose 변수를 만들고 매 프레임마다 이 포즈가 어떤 포즈인지 체크를 해준다.
이 모든 걸 이벤트 그래프에서 처리하였는데 언리얼 5에 새롭게 도입된 멀티스레딩 기능을 이용해보자.
5. 멀티스레드 활용하기
마지막으로 추가한 노드를 전부 삭제하고 다시 구현하자
위와 같은 함수를 만들어준다.
그럼 다음과 같은 함수 내부로 이동한다. 해당 함수 또한 매프레임마다 업데이트가 일어난다.
해당 스레드는 충돌을 방지하기 위해 변수에 직접 접근하지 않고 캐시를 이용해 사본에 접근하는 방식을 취한다고 한다.
사본에 접근하는 법을 알아보자.
Propery Access 변수를 만들자.
그리고 여기서 사본을 원하는 프로퍼티와 바인딩을 해준다.
그리고 이 사본을 애니메이션 블루프린트에 존재하는 변수인 DeathPose와 연결해주자.
만들다가 헷갈려서 다시 쓰면 Set Death Pose의 Death Pose는 에너미 클래스에 있는 데스포즈가 아니라 여기 애니메이션 블루프린트에서 만든 변수이다.
이제 데스포즈가 어떤 것인지 확인할 수 있으니 정말로 알맞는 애니메이션을 재생하도록 하자.
6. 열거형에 맞는 데스애니메이션 재생하기
Idle에서 Death로 넘어가는 트랜지션으로 들어가자.
Alive가 아니면 Death 스테이트로 넘어갈 것이다.
이제 이 열거형에 맞는 애니메이션을 보여주도록 하자.
블랜드 포즈를 이용해 이를 구현할 것이다.
다음과 같이 올바른 애니메이션들을 연결해준다.
한가지 더
이 부분 연결하는 것을 잊으면 안된다.
애니메이션이 잘 유지 되는 것을 볼 수 있다.
애니메이션 재생을 위해 굉장히 긴 포스팅을 했는데 사실 노티파이를 이용하면 블루프린트 단에서 바로 해결할 수 있다고 한다. 해당 방법이 언리얼 5.1 버전부터 추가되었다고 하는데 강의가 5.0 버전을 기준으로 만들어져 이런 방식으로 만든듯 하다.
어차피 실 구현보다는 배우는게 강의의 목적이므로 여러 방법을 아는 게 좋으니 이런 방식으로도 구현하는게 맞을 듯 하다.
'게임프로그래밍 > 실습1' 카테고리의 다른 글
[언리얼 실습] 53. 원할 때만 체력바가 보이게 하기 (0) | 2024.11.04 |
---|---|
[언리얼 실습] 52. 에너미 사망시 이것저것 수정하기 (0) | 2024.11.04 |
[언리얼 실습] 50. 사망시 애니메이션 재생하기 (0) | 2024.11.02 |
[언리얼 실습] 49. 데미지 시스템 사용하기 (0) | 2024.11.02 |
[언리얼 실습] 48. 실제 체력바 퍼센트 조절하기 (0) | 2024.11.02 |