사용자가 플레이어를 움직이기 위해서 키보드 방향키를 눌렀다. 그러면 플레이어는 이동에 대한 입력을 받은 것이다. 우리는 입력이 들어오면 이동과 관련된 모든 메서드들이 실행되기를 원한다. 이는 이벤트 핸들링을 통해 해결할 수 있다.
잠깐! 근데 그냥 입력된 이동을 처리하는 메서드에서 관련된 메서드를 바로 호출하면 되는 것 아닌가요🤔?
이 의문을 해결하기 위해서는 New Input System을 알아야 한다.
[ New Input System ]
기존에 사용되던 InputManager에서 모든 기능들이 하나의 클래스에 존재하여 발생하는 문제점을 보완한 것이다. 기능별로 클래스를 나눠서 구현하는 것은 객체 지향 프로그래밍에서 매우 중요한 요소이기 때문에, New Input System을 이용하면 수월하게 로직을 실행할 수 있다.
New Input System은 크게 Input Action, Input Action Asset, Player Input Component 세 가지 개념이 있다.
Input Action
개별적인 입력 행동을 정의하고, 이러한 행동을 실행하는 특정 이벤트를 트리거할 수 있다. 예를 들어 '점프'라는 행동을 정의하고, '점프' 입력이 들어왔을 때 '점프'를 실행하는 키나 버튼을 트리거한다.
Input Action Asset
여러 개의 Input Action을 하나로 그룹화하여 관리하는 것이다. 위에서는 Player 액션 맵에서 Move, Look, Fire 액션을 추가하여 각 액션에 대해 특정 키나 버튼을 바인딩한다. 각 액션들은 키보드와 마우스의 입력을 받고 있고, 새로운 액션 맵을 생성해서 다른 컨트롤러나 플랫폼에서의 입력을 받는 에셋을 새로 생성할 수도 있다. Assets > Create > Input Actions(맨 아래)에서 생성하고, 생성 후엔 Project 창에서 직접 더블 클릭하여 열 수도 있다.
Player Input Component
Player Input이 컴포넌트에 추가되어 입력을 자동으로 처리하고, 해당 오브젝트에 메시지를 보낸다.
장점
요즘에는 PC, 모바일, 콘솔 등 게임을 할 수 있는 다양한 플랫폼과 기기가 존재한다. 하지만 각각의 기기는 입력받는 키나 버튼이 다르다. 예를 들어 키보드의 Ctrl 키와 콘솔의 X 버튼을 같은 '공격' 액션에 바인딩할 수 있다. 이처럼 Input Action Asset을 사용해서 서로 다른 입력 장치에 동일한 기능을 쉽게 할당할 수 있다.
비슷하게 사용자가 직접 키 설정을 변경하는 경우, 왼쪽 뱡향 키를 '←'에서 'A'로 변경하면 Player Input 컴포넌트가 이를 자동으로 감지하고 새로운 키 설정을 반영하여 Input Action Asset에서 정의된 액션을 실행한다.
또한 2인 협동 게임을 할 때 각 플레이어가 서로 다른 입력 장치를 사용하거나, 같은 입력 장치에서 다른 키를 사용할 때도 개별적으로 처리되어 다른 입력 설정을 적용할 수 있다.
만약 처음에서의 물음처럼 하나의 입력에 액션이 바로 연결되어 있다면, 모든 키에 대한 설정을 해야 할 뿐만 아니라 키 설정 같은 자유도가 떨어지게 된다. 따라서 입력과 액션을 따로 구분하여 설정해야 효율적으로 처리할 수 있다.
[ 이벤트 처리 ]
로직
이렇게 받은 입력을 바탕으로 실제 이동을 구현해야 한다. 이동 이벤트를 처리하기 위해서 이벤트를 등록하고 호출하는 이벤트 핸들링을 해야 한다. 이벤트 처리는 이벤트 핸들링의 포괄적인 의미로, 이벤트의 관리와 제거까지 포함한다.
//TopDownMovement 클래스
private void Start()
{
controller.OnMoveEvent += Move;
}
TopDownMovement 클래스에서는 플레이어의 이동을 실행한다. OnMoveEvent 이벤트에 Move 메서드를 이벤트 핸들러로 등록한다. 이를 이벤트 구독이라고 하며, 이동 입력이 발생했을 때 Move 메서드가 호출되게 한다.
//PlayerInputController 클래스
public void OnMove(InputValue value)
{
Vector2 moveInput = value.Get<Vector2>().normalized;
CallMoveEvent(moveInput);
}
PlayerInputController 클래스에서는 입력 장치로부터 받은 이동 입력을 처리한다. 사용자로부터 받은 입력을 벡터 값으로 변환하여 CallMoveEvent 이벤트에 전달한다.
//TopDownController 클래스
public event Action<Vector2> OnMoveEvent;
public void CallMoveEvent(Vector2 direction)
{
OnMoveEvent?.Invoke(direction);
}
TopDownController 클래스에서는 입력에 반응하여 실행되는 이벤트를 정의한다. CallMoveEvent 이벤트는 OnMoveEvent 이벤트를 트리거한다. OnMoveEvent 이벤트는 등록된 핸들러(구독자)에게 입력을 전달하고, 핸들러가 없으면 아무런 동작도 하지 않는다.
//TopDownMovement 클래스
private void Move(Vector2 direction)
{
movementDirection = direction;
}
핸들러인 Move 메서드가 OnMoveEvent에 의해 호출되어 실제 이동을 실행한다.
이외 Unity Docs에서 자세한 내용을 확인할 수 있다.
https://docs.unity3d.com/kr/2020.3/Manual/UIE-Events-Handling.html
델리게이트와의 차이
델리게이트와 이벤트 모두 콜백 메커니즘을 구현한다는 공통점이 있다. 하지만 사용 방법, 목적, 접근성, 안전성에서 차이가 있다.
델리게이트는 메서드를 참조하는 타입으로, 메서드를 변수처럼 저장하고 전달할 수 있다. 따라서 특정 메서드를 호출하는 것이 가능하다. 델리게이트는 어디서든 직접 호출될 수 있어 유연성이 높지만, 이는 즉 안전성이 낮다는 것을 의미한다.
이벤트는 델리게이트를 기반으로 하며, 특정 상황이 발생했을 때 메시지를 전달한다. 이벤트를 구독한 이벤트 핸들러에게만 메시지가 전달되고 실행된다. 이벤트는 선언된 클래스 내부에서만 사용할 수 있어서 외부 객체는 이를 직접 조작할 수 없고, 델리게이트보다 더 안전하다.
특정 전화번호로 전화를 거는 것을 예시로 들어 보자. 델리게이트는 010-****-**** 형태의 전화번호를 모두 저장해 놓고, 그중 하나의 전화번호를 선택해서 전화를 거는 것이다. 이벤트는 저장된 특정 전화번호의 통화 버튼을 누르면 상대에게 전화를 거는 동작이 실행되는 것이다.
얼핏 보기에는 차이가 없어 보인다. 하지만 둘 다 최종적으로 동일한 동작을 수행했지만, 어떤 것에 더 집중하느냐에 차이가 있다. 델리게이트는 어떤 전화번호로 전화를 걸지 결정할 수 있는 주소록의 느낌이 강하고, 이벤트는 통화 버튼을 눌러서 전화를 연결한다는 행동이 중요시된다.
델리게이트에 대한 추가 설명은 아래 글의 '델리게이트' 목차에서 자세히 확인할 수 있다.
2024.04.30 - [Coding/C#] - [내일배움캠프 12일차 TIL] 인터페이스, enum, 델리게이트, 람다, Func, Action, LINQ, Nullable
[ 회고 ]
델리게이트를 배울 때에도 이해하는데 오랜 시간이 걸렸는데, 이벤트도 마찬가지였다. 그래도 델리게이트를 알고 있으니까 상대적으로 공부하는 데 조금이나마 더 수월했다. 처음에는 델리게이트의 코드 구조를 까먹어서 기능이 유사하다 보니 이벤트가 곧 델리게이트라고 착각하고 있었는데, 찾아보니 둘은 엄연히 다른 기능임을 알게 됐다. 둘의 메커니즘이 거의 똑같다 느껴졌던 이유는 이벤트가 델리게이트를 기반으로 하고 있었기 때문이었다. 시각적으로 도식화의 형태로 나타나지 않고, 클래스별로 나뉘어 있다 보니까 한눈에 구조가 보이지 않아서 아직은 많이 헷갈린다. 그래서 코드를 많이 눈으로 익히고 많이 구현해 봐야겠다.
'Coding > Unity' 카테고리의 다른 글
[내일배움캠프 21일차 TIL] 애니메이션 적용, 이름 입력, 씬 전환 시 데이터 유지, DontDestroyOnLoad (0) | 2024.05.14 |
---|---|
[내일배움캠프 20일차 TIL] 카메라 추적, 보간 적용 (0) | 2024.05.13 |
[내일배움캠프 18일차 TIL] 쿼터니언, 아크탄젠트 (0) | 2024.05.09 |
[내일배움캠프 17일차 TIL] 게임 엔진, PPU, 계층 구조, Input.GetAxis, 직렬화 (0) | 2024.05.08 |
[내일배움캠프 5일차 TIL] 복습, 체력바, isKinematic, isTrigger, 레벨 (1) | 2024.04.19 |