유니티의 새 인풋 시스템은 대리자 기반의 이벤트 시스템으로 구현되어 있다.
간단하고 빠른 구현을 위해 같은 오브젝트에 소속된 컴포넌트들에게 메시지 전달을 하거나 UnityEvent
를 이용하는 방식도 있지만, 가장 방대하고 자유로운 입력 시스템 사용 방식은 인풋 시스템의 C# 스크립트를 생성한 후 해당 스크립트의 이벤트에 함수를 구독시키는 것이다.
즉 스크립트 상으로 특정 입력 액션에 개발자가 원하는 액션을 구독시키면 플레이어가 특정 입력을 입력할 시 구독된 액션이 실행되게 된다.
이와 비슷하게 스크립트를 이용해서 특정 입력 액션에 연결된 키의 바인딩 또한 자유롭게 바꿀 수 있다.
키 입력 <--> 액션 이벤트 <--> 함수 실행
스크립트 상에서 이벤트 구독으로 입력 시스템에 기능을 추가할 수 있다.
이말인즉슨 런타임 도중 특정 상황에 따라 이벤트 구독을 변경함으로써 같은 입력이지만 다른 기능을 수행할 수 있다는 뜻이다.
키보드와 마우스를 사용하는 PC게임을 개발할 때에는 입력에 대한 부족함을 느낄 수 없지만
많아야 손가락 한 두개를 사용하는 모바일 게임은 수행되는 기능에 비해 입력의 종류가 매우 제한적이다.
즉 손가락 하나로 터치를 하더라도 상황에 따라 수행되는 기능이 달라야 한다.
그런데 이를 하나의 거대한 함수 안에서 if
혹은 switch
문으로 나누어 버리면 개발이 진행될수록 새로운 기능 추가와 유지보수가 어려워진다.
이럴 때 필요한 것이 상태 패턴이다. 상태 패턴은 동일한 오브젝트가 특정 상황에 따라 다른 행동을 취할 수 있게 해준다.
이를 입력 시스템과 조합하면 특정 상황에 따른 입력을 간단하게 분류 및 수행할 수 있고, 새로운 상황에 대한 행동 추가를 용이하게 할 수 있다.
다음의 게시글에는 모바일 인풋 시스템의 기초와 상태 패턴에 대한 기본적인 이론이 작성되어 있다.
위의 글을 읽고 오면 다음 내용을 이해하는 데 도움이 되리라 생각한다.
상태 머신을 상속받고, C# 클래스로 생성된 인풋 시스템 레퍼런스를 가진 커스텀 입력 상태 머신을 구현하는 방법이다.
/// <summary>
/// 첫 번째 손가락이 터치되었을 때 발생하는 이벤트
/// </summary>
internal delegate void PrimaryTouch();
internal event PrimaryTouch PrimaryTouchEvent;
/// <summary>
/// 첫 번째 손가락의 터치가 종료되었을 때 발생하는 이벤트
/// </summary>
internal delegate void PrimaryTouchEnd();
internal event PrimaryTouchEnd PrimaryTouchEndEvent;
private void OnPrimaryTouch(InputAction.CallbackContext ctx)
{
if (PrimaryTouchEvent != null
&& ctx.action.phase == InputActionPhase.Started
&& !EventSystem.current.IsPointerOverGameObject()
&& !IsPointerOverUIObject(PrimaryScreenPosition()))
{
PrimaryTouchEvent();
}
}
private void OnPrimaryTouchEnd(InputAction.CallbackContext ctx)
{
if (PrimaryTouchEndEvent != null
&& ctx.action.phase == InputActionPhase.Canceled)
{
PrimaryTouchEndEvent();
}
}
private void OnEnable()
{
_input.Enable();
_input.Touch.PrimaryTouch.started += OnPrimaryTouch;
_input.Touch.PrimaryTouch.canceled += OnPrimaryTouchEnd;
}
상태 머신의 참조를 갖고 있는 각각의 상태에서 상태 머신에 선언해 둔 이벤트 델리게이트에 접근하는 방식이다.
public override void OnEnter()
{
context.PrimaryTouchEvent += IdlePrimaryTouch;
context.SecondaryTouchEvent += IdleSecondaryTouch;
}
public override void OnExit()
{
context.PrimaryTouchEvent -= IdlePrimaryTouch;
context.SecondaryTouchEvent -= IdleSecondaryTouch;
}
private void IdlePrimaryTouch()
{
Debug.Log("IdlePrimaryTouch");
// Go to Swipe state
stateMachine.ChangeState<OnSwipeAndTouch>();
}
private void IdleSecondaryTouch()
{
Debug.Log("IdleSecondaryTouch");
// Go to Pinch state
stateMachine.ChangeState<OnPinch>();
}
감사합니다. 이런 정보를 나눠주셔서 좋아요.