XR플밍 - 9. UnityEngine2D 인터렉티브 프로그래밍 - 개인 프로젝트 1일차(5/28)

이형원·2025년 5월 28일
0

XR플밍

목록 보기
87/215
post-thumbnail

0. 들어가기에 앞서

  • 프로젝트 진행 전날, 삽질의 흔적

우리는 이전 수업으로 다양한 내용을 배웠고, 그 중에서는 MVC/MVP 패턴과 상태 패턴이 있었다.

여기서 이 MVC/MVP 패턴과 상태 패턴을 혼합하여 사용하면 더 구조적으로 관리가 쉽고 체계적인 시스템을 만들 수 있지 않을까 싶어서 시도해 보았다. 왜냐하면 상태란 것의 로직을 나타내는 거라면, 상태 패턴 자체를 PlayerController 로직을 정리하는 용도로 사용하고, 실제 스크립트에 붙이는 Player를 Presenter로 쓰면 되기 때문이다.

결론부터 말하자면 이 시도는 오히려 구조를 더 복잡하게 만들게 되어, 사용을 보류하기로 했다. 가능하면 두 가지 패턴 다 써 보는 방향으로 가고 싶었지만, 어쩔 수 없이 선택을 할 수밖에 없었다.

그러면 두 가지 패턴 중 어떤 게 이번 프로젝트에 더 필요할까, 고민한 끝에 상태 패턴을 사용하는 방식을 채택하기로 했다. 이번에 만들 캐릭터의 특성상 상태가 많기도 하고, 이걸 분리하는 작업은 필요해 보이기 때문이다.

1. 1일자 작업 정리

어설프지만 설계도 대략 정리해놓았다.

작업량이 적었던 건 아니었지만, 생각보다 여러모로 난관을 겪었다.
현재까지 이와 같이 작업하여, 어설프긴 하지만 플레이어에 관한 기능은 대부분 구현했다.

플레이어의 이동과 점프, 근접 공격과 원거리 공격까지 애니메이션이 정상 출력되도록 하는 것까지는 구현했다.

2. 문제의 발생과 해결과정

2.1 InputSystem에 대한 이해

InputSystem을 이용해 이번 조작을 하기로 한 만큼 아직 익숙하지 않은 면모가 많이 드러났다.

가장 어이없었던 실수는 InputSystem을 사용한 코드로 작성해 놓고선, 막상 플레이어 프리팹에 InputSystem을 안 붙여놔서 왜 작동 안하냐고 골머리를 싸맸었다.

그리고 무엇보다도 중요한 특징은, InputSystem을 쓰면 딱히 Update를 통해서 따로 입력을 받을 필요가 없다는 것이었다.

  • PlayerController의 코드 중 일부

어떻게 Update나 다른 과정을 거치지 않고 입력이 들어오는 건지 직관적으로 보이지 않다 보니 습관적으로 Update문에다가 또 입력을 넣으려고 했었다. 하지만 InputValue 값이 뭐가 들어가야 하는지 정할 수 없으니 Update문에 넣을 수 없었다.

전에는 InputSystem이랑 MVC 패턴을 결합해서 썼다 보니 물리 계산은 전부 상태 패턴 쪽으로 넘어가서 진행해야 한다는 사실을 알게 되었고, 그래서 입력만 여기서 받고 InputValue에 대한 물리 연산 부분은 PlayerStatus로 넘어가서 진행했다.

public class Player_Walk : PlayerState
{
    public Player_Walk(PlayerController _player) : base(_player)
    {
        HasPhysics = true;
    }

    public override void Enter()
    {
        m_player.Anim.SetBool("IsMove", m_player.IsMove);
    }

    public override void Update()
    {
        base.Update();
        if(Mathf.Abs(m_player.InputX.x) < 0.1f)
        {
            m_player.IsMove = false;
            m_player.StateMach.ChangeState(m_player.StateMach.StateDic[EState.Idle]);            
        }

        if(m_player.InputX.x < 0)
        {
            m_player.SpriteRenderer.flipX = true;
        }
        else if(m_player.InputX.x > 0)
        {
            m_player.SpriteRenderer.flipX = false;
        }        
    }

    public override void FixedUpdate()
    {
        m_player.Rigid.velocity = new Vector2(m_player.InputX.x * m_player.MoveSpeed, m_player.Rigid.velocity.y);
    }

    public override void Exit()
    {
        m_player.Anim.SetBool("IsMove", m_player.IsMove);
    }
}

2.2 애니메이션의 부자연스러움 문제

이게 아무래도 오늘 제일 시간을 제일 많이 투자한 부분이 아닐까 싶다.

강의를 들을 당시에는 모든 행동을 전부 애니메이션으로 재생하는 방식으로 했지만, 나는 그 방법은 애니메이션이 부자연스러워질 거라고 판단하여 연결하여 진행하기로 했다.

다만 문제가 발생한 것은 행동이 늘어나면 늘어날수록 세팅해야 하는 변수의 개수가 많아지고, 테스트를 진행하면 특정 행동이 다른 행동의 애니메이션을 끊거나 변수 오작동 같은 것들을 일으켜서 애니메이션이나 변수가 꼬이는 현상을 일으키는 것이다.

이것 때문에 차라리 모든 애니메이션을 다시 재생시키는 방식으로 젼환해야 하나 고민하던 찰나에, 차라리 두 가지를 섞어보는 방식으로 하는 게 어떨까 라는 방식에 도달했다.

  • 애니메이션 재생 방식에 대한 비교
항목SetBool + 트랜지션animator.Play()
CPU 성능약간 더 무거움 (조건 평가, 블렌딩 등 추가 연산)비교적 가벼움 (직접 상태 전환)
유연성매우 높음 (트랜지션 블렌딩, 상태 제어 등)낮음 (즉시 재생, 제어 제한적)
제어성Animator Controller에 의존코드로 직접 제어
복잡한 로직 구현쉬움 (조건, 블렌딩, 이벤트 등 설정 가능)복잡함 (모든 로직 수동 처리 필요)
디버깅 난이도낮음 (Animator 창 시각화)높음 (상태 추적 어려움)

  • 장단점 비교
  1. SetBool("IsJump", true) 방식

장점

  • 상태 전환이 직관적이며, 블렌딩이 자연스럽게 이루어짐.
  • Animator Controller의 전이 조건, 타이밍 제어, Exit Time 등을 활용할 수 있음.
  • 복잡한 애니메이션 로직을 시각적으로 구성 가능 (디자이너와 협업 용이).

단점:

  • 성능 측면에서는 약간의 오버헤드가 있음.
  • 파라미터가 많아지면 관리가 복잡해짐.
  • 빠르게 애니메이션을 전환하거나 덮어씌우기에는 부적절.
  1. animator.Play() 방식

장점

  • 즉각적인 애니메이션 전환 가능 (지연 없음).
  • 조건 없이 강제로 상태 진입 가능 (테스트 및 스크립트 기반 제어에 유리).
  • 불필요한 Animator Controller 구성을 줄이고 코드로 통제할 수 있음.

단점

  • 블렌딩 제어 불가 (갑작스러운 전환으로 어색할 수 있음).
  • Animator Controller 상태 흐름 무시 → 디버깅 및 상태 추적 어려움.
  • 루트 모션이나 트랜지션 이벤트 등 작동 안 할 수 있음.
  • 사용처
상황추천 방식
자연스러운 상태 전환이 중요할 때 (e.g., 걷기 → 점프 → 착지)SetBool, SetTrigger 등 트랜지션 기반 방식
특정 애니메이션을 빠르게 강제 재생해야 할 때 (e.g., 공격, 피격 등)animator.Play(JUMP_HASH)
디자이너와 협업하며 시각적으로 애니메이션 구성SetBool 방식
애니메이션을 코드에서 완전히 제어하려는 경우Play() 방식

이와 같이 내용을 정리하고서, Idle, Walk, Jump는 파라미터를 조작하여 애니메이션을 작동시키고, MeleeAttack, Charge, RangedAttack은 애니메이션 재생 방식으로 구현하기로 하였다.

이와 같이 적용하니 조작이 약간 뻑뻑한 감은 있어도, 실제로 애니메이션 상 오류도 줄고 더 자연스러워졌다.

3. 수정해야 할 점과 과제

3.1 스펠 공격에 쿨타임이 필요

현재 위의 시연 영상만 봐도 알 수 있지만 스펠 공격이 무한정 연사되는 문제가 있다. 이전에 테스트를 진행할 때에는 쿨타임도 만들기는 했지만, 이번에는 상태 패턴을 채용하면서 아무래도 구조가 달라졌기 때문에 쿨타임을 만들 방법이 조금 난감하다.

스킬 쿨타임을 만들 방법에 대한 고민이 필요해 보인다.

3.2 오브젝트 풀 패턴 구현

스펠에 대한 부분이 어느 정도 갖추어졌으니, 이 스펠을 저장하여 계속된 파괴로 인한 성능 저하를 막는 방법이 필요할 것이다. 3.1의 과제가 빨리 해결되어야지 진행할 수 있는 영역이기도 해서, 빠른 시일 내로 해결해보자.

3.3 몬스터 구현

플레이어에 대한 틀을 잡긴 했지만 무엇보다도 몬스터와의 상호작용이 있어야지 비로소 게임다운 무언가를 만들 수 있을 것이다. 몬스터의 구현에 대한 걸 우선순위로 세워 빠른 구현이 필요하다.

profile
게임 만들러 코딩 공부중

0개의 댓글