[UE5] PickUpDefense #4 - 캐릭터 애니메이션 및 데이터 테이블 관리

ChangJin·2024년 9월 5일
0

Unreal Engine5

목록 보기
102/114
post-thumbnail

2024.09.06

이번 글에서는 PickUpDefense 게임에서 캐릭터의 애니메이션을 C++ 코드로 관리하고, 데이터 테이블을 활용하여 다양한 캐릭터의 애니메이션을 설정하는 방법을 다룹니다. 애니메이션을 관리하는 과정에서 발생한 문제들과 해결 방법을 자세하게 설명하며, 코드 구현 과정을 공유합니다.


진행상황

상단 스테이지 UI

  • ✅ UI 위치
  • ⬜ 상단 UI 시간 시스템
  • ⬜ 상단 UI 스테이지 시스템

하단 상점 UI

  • ✅ UI 위치
  • ✅ 마우스 입력 처리
  • ⬜ 드래그 앤 드롭으로 카드 옮기기

캐릭터 애니메이션 시스템

  • ✅ 애니메이션 데이터 테이블 관리
  • ✅ C++ 기반 애니메이션 관리
  • ✅ 캐릭터별 애니메이션 설정 및 실행

1. 애니메이션 데이터 테이블 설정

이번 작업에서는 각 캐릭터의 애니메이션을 관리하기 위해 데이터 테이블을 활용했습니다. 데이터 테이블을 통해 다양한 캐릭터의 걷기, 달리기, 대기(idle) 상태에 대한 애니메이션을 설정하고, 각 캐릭터가 가지고 있는 애니메이션 데이터를 쉽게 참조할 수 있도록 하였습니다.

1.1 데이터 테이블 구조

먼저 캐릭터의 애니메이션 정보를 담을 데이터 테이블을 정의해야 합니다. 이 데이터 테이블에는 걷기, 달리기, idle 애니메이션을 정의해서 각 캐릭터의 애니메이션을 구분했습니다.

USTRUCT(BlueprintType)
struct FUnitAnimationData : public FTableRowBase
{
    GENERATED_BODY()

    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    UAnimSequence* WalkAnim;

    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    UAnimSequence* RunAnim;

    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    UAnimSequence* IdleAnim;
};

2. 캐릭터 애니메이션 로딩 및 설정

각 캐릭터의 애니메이션은 데이터 테이블에서 로드한 후, UCharacterAnimInstance에 있는 변수에 애니메이션을 할당하는 방식으로 관리됩니다. 캐릭터의 애니메이션은 캐릭터가 스폰될 때마다 동적으로 설정되며, 각 캐릭터의 속성에 맞는 애니메이션이 적용됩니다.

2.1 캐릭터 애니메이션 로딩 코드

캐릭터가 스폰될 때 해당 캐릭터의 속성에 맞는 애니메이션을 로드하고, 이를 캐릭터의 애니메이션 인스턴스에 반영하는 코드입니다. UCharacterAnimInstance는 애니메이션 블루프린트에서 사용할 수 있도록 설정된 C++ 클래스입니다.

void ABaseUnit::InitializeUnit(const EUnitAttributeType InType, const int32 InLevel)
{
    UnitAttributeType = InType;
    CurrentLevel = InLevel;

    // 데이터 테이블에서 애니메이션 데이터를 읽어옵니다.
    UDataTable* AnimDataTable = LoadObject<UDataTable>(nullptr, TEXT("/Game/DataTable/Anim/AnimData.AnimData"));
    if (!AnimDataTable) return;

    // 캐릭터 속성에 맞는 애니메이션 데이터를 찾습니다.
    FName RowName(*UEnum::GetDisplayValueAsText(UnitAttributeType).ToString());
    FUnitAnimationData* AnimData = AnimDataTable->FindRow<FUnitAnimationData>(RowName, TEXT("Unit Animation Context"));

    if (AnimData)
    {
        if (UCharacterAnimInstance* AnimInstance = Cast<UCharacterAnimInstance>(BaseSkeletalMeshComponent->GetAnimInstance()))
        {
            AnimInstance->WalkAnimation = AnimData->WalkAnim;
            AnimInstance->RunAnimation = AnimData->RunAnim;
            AnimInstance->IdleAnimation = AnimData->IdleAnim;
        }
    }
}

이 코드는 UnitAttributeType에 맞는 애니메이션 데이터를 AnimDataTable에서 찾아, 해당 애니메이션 데이터를 UCharacterAnimInstance의 변수에 할당하는 구조입니다.

해당하는 부분은 Enum 클래스에서 해당하는 Value를 String으로 바꾸어서 FName에 넣는 코드입니다. Enum에서 특성을 구분하는 경우가 많은데 다음처럼 특성의 이름을 그대로 가져다 사용할 수 있으니 매우 편리합니다.

FName RowName(*UEnum::GetDisplayValueAsText(UnitAttributeType).ToString());

2.2 문제 해결 과정

이 코드를 작성하면서 다음과 같은 문제들이 발생했습니다.

  • 문제 : 스켈레탈 메시 변경 시 애니메이션 반영 실패
    • 캐릭터가 스폰될 때마다 SkeletalMesh를 변경할 경우, 그에 맞는 애니메이션 블루프린트가 적용되지 않는 문제를 발견했습니다. 이를 해결하기 위해, 각 캐릭터의 SkeletalMesh에 맞는 애니메이션 블루프린트를 로드하고 설정하는 과정을 추가했습니다.

이 데이터 테이블을 통해 각 캐릭터의 메시와 애니메이션 블루프린트를 설정할 수 있으며, 애니메이션의 동적 설정이 가능해집니다.

const TMap<EUnitAttributeType, FUnitMeshData> FUnitStats::UnitMeshData = {
    {EUnitAttributeType::Ahri, {
        LoadObject<USkeletalMesh>(nullptr, TEXT("/Game/Assets/Ahri/Ahri_gltf.Ahri_gltf")),
        LoadObject<UClass>(nullptr, TEXT("/Game/Characters/ABP_Ahri.ABP_Ahri_C")),
        FVector(1.f)
    }},
    {EUnitAttributeType::Jinx, {
        LoadObject<USkeletalMesh>(nullptr, TEXT("/Game/Assets/Jinx/Jinx_gltf.Jinx_gltf")),
        LoadObject<UClass>(nullptr, TEXT("/Game/Characters/ABP_Jinx.ABP_Jinx_C")),
        FVector(1.f)
    }}
};

3. 데이터 테이블을 통한 애니메이션 동적 설정

캐릭터마다 다른 애니메이션을 설정해야 하는 경우, 데이터 테이블을 통해 캐릭터별로 설정된 애니메이션을 동적으로 적용할 수 있습니다. 이를 통해 코드 중복을 최소화하고, 각 캐릭터의 특성에 맞는 애니메이션을 손쉽게 설정할 수 있습니다.

3.1 애니메이션 데이터 테이블 구조

다음은 각 캐릭터의 애니메이션을 데이터 테이블로 정의한 예시입니다. 이 데이터 테이블은 각각의 캐릭터가 스폰될 때 참조되어 애니메이션을 설정합니다. 각 캐릭터마다 본과 애니메이션이 다르기 때문에 각각의 캐릭터에 있는 애니메이션을 적용하고자 했습니다. 즉 애니메이션 모션은 전부 다르지만, 달리기에 해당하는 기능, Idle에 해당하는 기능은 다들 동일하니 최대한 관리를 편하게 만들고자 했습니다.

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"

#include "AnimData.generated.h"

/**
 * 
 */
USTRUCT(BlueprintType)
struct FAnimData : public FTableRowBase
{
	GENERATED_BODY()

	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	UAnimSequence* WalkAnim;

	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	UAnimSequence* RunAnim;

	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	UAnimSequence* IdleAnim;

	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	TArray<UAnimSequence*> AttackAnims;

	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	TArray<UAnimSequence*> AttackAnimCriticals;
};

4. 최종 정리

이번 작업을 통해 캐릭터의 애니메이션을 동적으로 설정하고, 데이터 테이블을 활용하여 각 캐릭터마다 다른 애니메이션을 할당하는 방법을 구현했습니다. 이를 통해 코드 중복을 최소화하고, 다양한 캐릭터의 애니메이션을 효과적으로 관리할 수 있게 되었습니다.

향후 작업에서는 캐릭터의 전투 애니메이션을 추가하고, UI와 상호작용하는 부분을 더 개선할 계획입니다. 이번 글이 언리얼 엔진에서 애니메이션을 다루는 데 도움이 되길 바랍니다.

감사합니다!

profile
게임 프로그래머

0개의 댓글