콘텐츠로 이동

UE5 게임플레이 상태 머신 구현

캐릭터의 행동(Idle, Run, Attack, Stun, Dead)은 서로 충돌하는 전환 규칙이 있습니다. 조건문을 직접 관리하면 상태가 늘어날수록 복잡도가 기하급수적으로 증가합니다. 상태 머신은 전환 규칙을 명시적으로 정의해 이 복잡도를 제어합니다.

소규모 프로젝트에 적합한 직접 구현 방식입니다.

UENUM(BlueprintType)
enum class ECharacterState : uint8 {
Idle, Run, Jump, Attack, Stun, Dead
};
UCLASS()
class AMyCharacter : public ACharacter {
GENERATED_BODY()
ECharacterState CurrentState = ECharacterState::Idle;
public:
bool TryTransitionTo(ECharacterState NewState);
void OnStateEnter(ECharacterState State);
void OnStateExit(ECharacterState State);
};
bool AMyCharacter::TryTransitionTo(ECharacterState NewState) {
// 전환 가능 여부 검사 테이블
static const TMap<ECharacterState, TSet<ECharacterState>> ValidTransitions = {
{ ECharacterState::Idle, { ECharacterState::Run, ECharacterState::Attack, ECharacterState::Jump } },
{ ECharacterState::Run, { ECharacterState::Idle, ECharacterState::Jump, ECharacterState::Attack } },
{ ECharacterState::Attack, { ECharacterState::Idle } },
{ ECharacterState::Jump, { ECharacterState::Idle } },
{ ECharacterState::Stun, { ECharacterState::Idle } },
{ ECharacterState::Dead, { } },
};
const auto* Allowed = ValidTransitions.Find(CurrentState);
if (!Allowed || !Allowed->Contains(NewState)) return false;
OnStateExit(CurrentState);
CurrentState = NewState;
OnStateEnter(NewState);
return true;
}
void AMyCharacter::OnStateEnter(ECharacterState State) {
switch (State) {
case ECharacterState::Attack: BeginAttack(); break;
case ECharacterState::Dead: Die(); break;
default: break;
}
}

GAS를 사용하는 프로젝트에서 권장합니다. 태그로 상태를 표현해 시스템 간 결합도를 낮춥니다.

State.Movement.Idle
// 상태 태그 정의
// State.Movement.Run
// State.Combat.Attacking
// State.Status.Stunned
// 상태 전환
void UMyCombatAbility::ActivateAbility(...) {
// 스턴 상태면 활성화 불가 (Activation Required/Blocked Tags로 설정)
// AbilityTags: Ability.Combat.Attack
// ActivationBlockedTags: State.Status.Stunned, State.Movement.Dead
AbilitySystemComponent->AddLooseGameplayTag(
FGameplayTag::RequestGameplayTag("State.Combat.Attacking"));
}
void UMyCombatAbility::EndAbility(...) {
AbilitySystemComponent->RemoveLooseGameplayTag(
FGameplayTag::RequestGameplayTag("State.Combat.Attacking"));
}
// 상태 조회
bool IsAttacking() const {
return AbilitySystemComponent->HasMatchingGameplayTag(
FGameplayTag::RequestGameplayTag("State.Combat.Attacking"));
}

UE5.1에서 도입된 고수준 상태 머신으로, 에디터에서 시각적으로 구성합니다.

// StateTree 태스크 구현
USTRUCT()
struct FAttackStateTask : public FStateTreeTaskBase {
GENERATED_BODY()
using FInstanceDataType = FAttackStateTaskData;
EStateTreeRunStatus EnterState(FStateTreeExecutionContext& Context,
const FStateTreeTransitionResult& Transition) const {
auto& Data = Context.GetInstanceData(*this);
Data.Character->BeginAttack();
return EStateTreeRunStatus::Running;
}
EStateTreeRunStatus Tick(FStateTreeExecutionContext& Context, float DeltaTime) const {
auto& Data = Context.GetInstanceData(*this);
return Data.Character->IsAttackComplete()
? EStateTreeRunStatus::Succeeded
: EStateTreeRunStatus::Running;
}
void ExitState(FStateTreeExecutionContext& Context, ...) const {
Context.GetInstanceData(*this).Character->EndAttack();
}
};
방식적합 규모장점단점
Enum FSM소규모단순, 빠른 구현상태 증가 시 복잡도 폭발
GAS Tag중~대규모시스템 분리, 확장성GAS 셋업 필요
StateTree중~대규모시각적 편집, 조건 내장UE5.1+, 학습 곡선

상태 전환 시 애니메이션 동기화

섹션 제목: “상태 전환 시 애니메이션 동기화”
void AMyCharacter::OnStateEnter(ECharacterState State) {
static const TMap<ECharacterState, FName> StateMontages = {
{ ECharacterState::Attack, "Attack_Montage" },
{ ECharacterState::Stun, "Stun_Montage" },
};
if (const FName* Montage = StateMontages.Find(State))
GetMesh()->GetAnimInstance()->Montage_Play(
GetMontageByName(*Montage));
}
  • 소규모: Enum + 전환 테이블 방식으로 빠르게 구현
  • GAS 사용 시: Gameplay Tag로 상태 표현, Activation Blocked Tags로 충돌 방지
  • UE5.1+: StateTree로 에디터 기반 복잡한 AI/캐릭터 상태 관리
  • 상태 진입/이탈 이벤트에서 애니메이션, 사운드, 물리 처리를 중앙화