Command 패턴과 입력 버퍼(Input Buffer)로 구현하는 매끄러운 액션 게임 조작감

Command 패턴과 입력 버퍼(Input Buffer)로 구현하는 매끄러운 액션 게임 조작감

액션 게임에서 플레이어의 콤보 스킬 입력 누락을 방지하고 정교한 조작감을 제공하기 위해 Command 패턴 기반의 입력 버퍼(Input Buffer) 시스템을 구축하는 방법을 살펴봅니다.

1. 액션 게임에서 입력 버퍼(Input Buffer)가 필요한 이유

액션 게임이나 격투 게임을 플레이할 때 이전 공격 애니메이션이 끝나기 직전 다음 스킬 키를 눌렀음에도 캐릭터가 반응하지 않고 멈춰버린다면 플레이어는 답답함을 느끼게 됩니다. 60 FPS 환경에서 단 1~2프레임 차이로 입력 타이밍을 놓쳤다고 해서 조작을 무시하는 것은 게임의 쾌적성을 크게 저하시킵니다.

이 문제를 해결하기 위해 도입하는 기법이 선행 입력 수용 및 예약 시스템, 즉 입력 버퍼(Input Buffer)입니다. 애니메이션 재생 중이거나 선후딜레이(Recovery Window) 상태일 때 들어온 입력을 버퍼 메모리에 임시로 보관해 두고 다음 행위가 가능해지는 시점에 즉시 예약된 스킬을 실행합니다.

선행 입력 버퍼링 유무에 따른 프레임 타이밍 비교

버퍼의 유효 시간 TbufferT_{buffer}는 게임의 장르와 리듬감에 따라 다르게 설정됩니다. 너무 길면 의도치 않은 이전 입력이 실행되고 너무 짧으면 버퍼링 효과를 체감하기 어렵습니다. 보통 0.1초에서 0.3초(60 FPS 기준 6~18프레임) 사이의 유효 시간을 제공합니다.

Tvalid=tcurrenttinputTbufferT_{valid} = t_{current} - t_{input} \le T_{buffer}

2. Command 패턴을 통한 입력과 실행의 분리

입력 버퍼를 구현할 때 가장 유용한 디자인 패턴이 바로 Command 패턴입니다. 키 입력 이벤트를 단순한 함수 호출로 직접 처리하면 언제 실행할 것인가와 무엇을 실행할 것인가가 강하게 결합합니다.

Command 패턴은 요청 자체를 하나의 객체(Object)로 캡슐화합니다. 이를 통해 다음과 같은 구조적 이점을 얻을 수 있습니다.

  • 입력 유연성: 키보드, 패드, AI 입력 등 다양한 소스에서 생성된 명령을 동일한 큐(Queue)에 담을 수 있습니다.
  • 지연 실행(Deferred Execution): 명령 객체를 생성한 시점과 실제 Execute를 호출하는 시점을 완전히 분리할 수 있습니다.
  • 유효성 검증(Validation): 버퍼에 저장된 명령이 실행 가능한 상태인지 미리 확인할 수 있습니다.
graph TD
    A[플레이어 입력 발생] --> B[Command 객체 생성]
    B --> C[Input Buffer 큐에 Enqueue]
    C --> D{캐릭터 실행 가능 여부}
    D -->|실행 가능| E[Command Execute 호출 및 Dequeue]
    D -->|대기 중| F{유효 시간 초과 여부}
    F -->|초과| G[Command 파기]
    F -->|유효| C

3. C# 기반 Command 패턴 및 입력 버퍼 구현

Unity C# 환경을 기준으로 Command 패턴과 입력 버퍼를 구현하는 코드 구조를 작성해 봅니다.

Command 인터페이스와 구체 클래스

using UnityEngine;

public interface ICommand
{
    float Timestamp { get; }
    bool CanExecute(GameObject actor);
    void Execute(GameObject actor);
}

public class SkillCommand : ICommand
{
    public float Timestamp { get; private set; }
    private readonly int _skillId;

    public SkillCommand(int skillId)
    {
        _skillId = skillId;
        Timestamp = Time.time;
    }

    public bool CanExecute(GameObject actor)
    {
        var stateMachine = actor.GetComponent<CharacterStateMachine>();
        return stateMachine != null && stateMachine.CanPerformAction;
    }

    public void Execute(GameObject actor)
    {
        var skillSystem = actor.GetComponent<SkillSystem>();
        if (skillSystem != null)
        {
            skillSystem.UseSkill(_skillId);
        }
    }
}

Input Buffer 큐 관리자

입력 버퍼는 Queue 구조를 활용하여 선입선출(FIFO) 방식으로 명령을 처리합니다. 매 프레임 유효 시간이 지난 명령을 정리하고 실행 가능한 상태가 되었을 때 큐의 최우선 명령을 수행합니다.

커맨드 패턴 기반 입력 버퍼 시스템 구조도

using System.Collections.Generic;
using UnityEngine;

public class InputBufferManager : MonoBehaviour
{
    [SerializeField] private float bufferDuration = 0.25f;
    private readonly Queue<ICommand> _buffer = new Queue<ICommand>();

    public void BufferCommand(ICommand command)
    {
        _buffer.Enqueue(command);
    }

    private void Update()
    {
        ClearExpiredCommands();
        ProcessBufferedCommand();
    }

    private void ClearExpiredCommands()
    {
        while (_buffer.Count > 0)
        {
            var top = _buffer.Peek();
            if (Time.time - top.Timestamp > bufferDuration)
            {
                _buffer.Dequeue();
            }
            else
            {
                break;
            }
        }
    }

    private void ProcessBufferedCommand()
    {
        if (_buffer.Count == 0) return;

        var command = _buffer.Peek();
        if (command.CanExecute(gameObject))
        {
            ICommand executable = _buffer.Dequeue();
            executable.Execute(gameObject);
        }
    }
}

4. 실전 적용 시 고려해야 할 심화 요소

입력 버퍼와 Command 패턴을 접목할 때 실무에서 고려해야 하는 핵심 사항들입니다.

입력 우선순위(Priority) 도입

단순한 FIFO 큐는 회피나 방어처럼 즉시 발동해야 하는 위기 탈출 스킬이 일반 공격 명령 뒤에 밀릴 수 있습니다. 우선순위 큐(Priority Queue)를 사용하거나 회피 키 입력 시 기존 큐를 모두 비우고 최우선 실행하도록 처리하는 것이 조작감을 향상시킵니다.

캔슬 윈도우(Cancel Window) 연동

애니메이션 이벤트(Animation Event) 또는 State Machine Behaviour와 연동하여 특정 후딜레이 프레임 구간부터 CanPerformAction 플래그를 true로 켜주어야 버퍼링된 스킬이 끊김 없이 이어서 발동합니다.

Command 패턴과 입력 버퍼를 결합하면 코드의 결합도를 낮추는 동시에 프레임 단위의 세밀한 콤보 판정을 안정적으로 구현할 수 있습니다.

#Design Pattern#Command Pattern#Input Buffer#Unity#Game Architecture

계속 읽어보기

이런 글은 어떠세요?

< Back to Logs