오늘은 Unity에서 상태 패턴(State Pattern)을 사용하여 캐릭터의 상태를 관리하는 방법을 배웠다. 상태 패턴은 게임에서 캐릭터의 다양한 상태(Idle, Run, Hit 등)를 효율적으로 관리하고, 상태 전환을 매끄럽게 처리할 수 있도록 도와주는 중요한 디자인 패턴이다.
오늘 한 작업 :
State Pattern 도입: 플레이어의 상태를 관리하기 위해 State Pattern을 도입함.
IState 인터페이스를 정의하고, 각 상태(IdleState, RunState)를 개별 클래스로 구현.
상태 전환을 관리하는 StateMachine을 구현하여 상태 간의 전환을 처리하도록 구성함.
상태에 따른 애니메이션 처리:
각 상태 진입 시 애니메이션을 전환하기 위해 Animator를 사용.
상태의 Enter() 메서드에서 적절한 애니메이션을 재생하도록 구성함.
주요 클래스:
Player: 입력을 받아 StateMachine에 전달하고, 애니메이션 및 이동 처리를 담당.
IState: 상태 패턴의 기본 인터페이스로, Enter(), Execute(), Exit() 메서드를 포함.
StateMachine: 현재 상태를 관리하며 상태 전환을 처리.
IdleState: 플레이어가 움직이지 않는 상태를 정의.
RunState: 플레이어가 이동 중일 때의 상태를 정의하고, 이동 속도에 따른 애니메이션을 재생.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
public class Player : MonoBehaviour
{
private Vector2 playerVector; // 플레이어의 이동 방향을 저장할 변수
[SerializeField] private float playerSpeed; // 플레이어 이동 속도
private Rigidbody2D rb; // 물리 연산에 사용할 Rigidbody2D
private SpriteRenderer sprite; // 스프라이트 방향 변경에 사용할 SpriteRenderer
private Animator animator; // 애니메이션 제어에 사용할 Animator
private StateMachine stateMachine; // 상태를 관리할 상태 머신
private void Awake()
{
// 컴포넌트 참조 초기화
rb = GetComponent<Rigidbody2D>();
sprite = GetComponent<SpriteRenderer>();
animator = GetComponent<Animator>();
// 상태 머신 초기화 및 IdleState로 시작
stateMachine = new StateMachine();
stateMachine.SetState(new IdleState(stateMachine, animator));
}
private void FixedUpdate()
{
// 플레이어 이동 처리 (상태에 따라 이동 로직이 변경될 수 있음)
Vector2 movement = playerVector.normalized * playerSpeed * Time.fixedDeltaTime;
rb.MovePosition(rb.position + movement);
}
public void OnMove(InputValue value)
{
// 이동 입력을 받아 상태 머신에 전달
playerVector = value.Get<Vector2>();
}
public void OnHit()
{
// 플레이어가 피격되었을 때 상태를 HitState로 전환
//stateMachine.SetState(new HitState(stateMachine, stateMachine.CurrentState, animator));
}
private void Update()
{
// 매 프레임마다 상태 머신을 업데이트
stateMachine.Update(playerVector);
}
private void LateUpdate()
{
// 플레이어 스프라이트 방향 처리
if (playerVector.x != 0)
sprite.flipX = playerVector.x < 0;
}
}
using UnityEngine;
public interface Istate
{
void Enter(); // 상태에 진입할 때 호출
void Execute(Vector2 playerVector); // 상태가 활성화 되어 있을때 매 프레임 호출
void Exit(); // 상태에서 벗어날때 호출
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
public class IdleState : Istate
{
private StateMachine stateMachine;
private Animator animator;
public IdleState(StateMachine machine, Animator animator)
{
stateMachine = machine;
this.animator = animator;
}
public void Enter()
{
Debug.Log("대기상태");
animator.Play("Idle");
}
public void Execute(Vector2 playerVector)
{
if(playerVector.magnitude > 0)
{
stateMachine.SetState(new RunState(stateMachine, animator)); // 상태전환
}
}
public void Exit()
{
Debug.Log("대기 상태 종료");
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RunState : Istate
{
private StateMachine stateMachine;
private Animator animator;
public RunState(StateMachine machine, Animator animator)
{
stateMachine = machine;
this.animator = animator;
}
public void Enter()
{
Debug.Log("뛰는상태");
animator.Play("Run");
}
public void Execute(Vector2 playerVector)
{
if(playerVector.magnitude == 0)
{
stateMachine.SetState(new IdleState(stateMachine, animator));
}
}
public void Exit()
{
Debug.Log("뛰는상태 종료");
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class StateMachine
{
private Istate currentState;
public void SetState(Istate newState)
{
if(currentState != null)
currentState.Exit();
currentState = newState;
currentState.Enter();
}
public void Update(Vector2 PlayerVector)
{
if (currentState != null)
currentState.Execute(PlayerVector);
}
}
문제: 애니메이션이 제대로 전환되지 않음.
원인: Animator에 미리 설정된 트랜지션 및 파라미터가 존재했기 때문에 상태 패턴에서 직접 애니메이션을 재생해도 트랜지션 조건을 충족하지 않아서 애니메이션이 변경되지 않았음.

해결 방법:
Animator의 트랜지션 설정을 제거하고, 상태 패턴에서 Animator.Play("AnimationName")을 사용하여 직접적으로 애니메이션을 제어함으로써 즉시 애니메이션이 전환되도록 처리.

결과: 상태에 맞는 애니메이션이 즉각적으로 반응하고, 트랜지션 설정에 의해 애니메이션 전환이 지연되거나 무시되는 문제가 해결됨.
