본 게시글은 Unity 22.3.x LTS 버전을 기준으로 설명합니다.
이 글은 가급적 실제 사용 내용만을 다룰 것이기 때문에 유니티 설치 과정 등에 대해서는 다루지 않습니다.
이 문서는 유니티의 기존 입력 관리자인 Input Manager를 사용하여 입력을 처리하는 프로세스를 구현 할 줄 아는 사람을 대상으로 합니다.
가장 자세한 것은 공식 문서를 참고하면 됩니다만, 개인적으로는 튜토리얼은 별 도움이 안됬습니다.
기존의 유니티 입력은 Edit > Project Settings > InputManager 에 등록된 인풋을 C# 코드 내에서 Input 클래스
를 통해 그 값을 읽어오는 방식으로 이루어졌습다.
float x = Input.GetAxis("Horizontal");
float y = Input.GetAxis("Vertical");
transform.position += new Vector3(x, y) * Time.deltaTime;
매우 간편합니다. 별도의 설정도 특별히 필요가 없습니다.
하지만 Input System
은 별도의 설정이 필요하며, 처음하는 사람들은 매우 좀 번거롭고 복잡하게 느낄 수 있습니다.
우선 Input System을 사용하기 위해 Window > PackageManager를 엽니다.
그 다음 상단의 Packages에서 Unity Registry를 선택한 다음, 아래로 내려서 Input System을 찾아 설치해줍니다.
설치를 완료하였다면, 우선 Input System을 사용하기 위해 Input Actions
에셋을 만들어야 합니다.
프로젝트 탭에서 우클릭 후 가장 하단에 보면 Input Actions를 찾을 수 있습니다.
적당한 곳에 파일을 생성해주고, 이름을 정해줍니다.
그리고 해당 파일을 더블클릭하여 열어줍니다.
기존의 Unity Input Manager는 기본적으로 매핑이 되어 있고, 우리는 그것을 가져다 쓰거나 수정해서 사용하지만, Input System은 우리가 직접 매핑을 해주어야 합니다. 이러한 점이 불편하다면 불편하겠지만, 그 나름대로의 이점도 있습니다.
우선 매핑을 위해 방금 생성한 파일을 열어보면 다음과 같은 창이 보일텐데(현재 창은 이미 완료한 창), 여기서 상단의 All Control Scheme을 눌러서, Add Control Scheme을 합니다.
그 다음 List를 눌러보면 다음과 같이 표시가 뜨는 것을 볼 수 있습니다.
이로서 우리는 이것이 뭘 의미하는지 유추 해 볼 수 있습니다.
Scheme
는 사용할 컨트롤의 종류를 지정하는 것이고(주로 플랫폼 별로 입력을 구분하기 위해),
ActionMaps
는 그 컨트롤을 이용할 개체의 컨트롤들의 집합을,
Actions
는 그 개체의 컨트롤들을 의미합니다.
이것만으로는 무슨 말인지 알기 어려우니 차차 해보면서 더 알아봅시다.
위의 상황에서 키보드와 마우스 두 가지를 추가한 후에 저장합시다.
(Usage는 프리셋같은 것으로, 특정 상황에서 사용하는 입력 세팅을 자동으로 지정해줍니다. Optional과 Required는 옵션 혹은 필수 여부를 말합니다.)
그 다음에 방금 위의 사진처럼 한번 만들어봅시다.
먼저 Action Maps의 +버튼을 클릭하여 Action Maps를 만들고, Player라고 이름지어 줍시다.
그리고 Actions로는 Move
, Look
, Fire
이 세 가지를 만들어 줍니다.
그러면 다음과 같이 표시되어 있을 텐데, 이제 오른쪽의 Action Properties에 집중해줍시다.
Action Type
은 입력의 방식이 어떤 방식으로 전달 될 것인가를 정하는 것입니다.
Value
는 가장 기본값으로, 변화하는 모든 값을 추적합니다.Button
은 눌렀을 때에만 그 변화를 추적합니다. (그래서 ButtonControls에만 지정이 가능합니다.)Pass Through
는 여러 개의 바운딩 된 값을 동시에 처리하기 위해 주로 사용합니다.다른 것은 그다지 신경 쓸 것 없이 대부분의 일반적인 입력은 Value를 사용한다고 보면 됩니다.
그러면 곧바로 Move를 지정해봅시다. Move의 Action Type을 Value로 지정하고, Control Type을 봅시다.
Control Type
은 해당 Action Type으로부터 전달된 값을 어떠한 형식으로 받을지 지정하는 역할을 합니다.
자세한 타입들은 너무 많으니 필요 한 때에 따라 알아봅시다.
우선 우리는 Move에서 상하좌우로 움직일 예정이므로 Vector2로 지정합니다.
그리고 우선은 Move에 있는 No Binding을 보면서 그 요소를 차근차근 살펴봅시다.
Path
는 어떤 입력장치의 어떤 값을 가져올지 정하는 것입니다. 예를 들어 '마우스의 위치를 가져온다'와 같이 입력장치 > 값을 찾아서 지정합니다.
하지만 막상 보면 몇 가지 없는 것을 확인 할 수 있는데,
그것은 우리가 Move에서 Control Type을 Vector2로 지정하였기 때문에, Vector2 형식으로 반환 해주는 입력들만 지정이 가능합니다.
여기서 만약 Path를 지정한다면 이제 매핑은 끝난 것입니다.
그 전에 우선 Move의 옆에 있는 +버튼을 눌러보면 다음과 같은 화면을 볼 수 있습니다.
여기서도 역시 Vector2 형식으로 반환해주는 입력들의 목록을 표시해주는데, 우리는 상하좌우 이동을 사용할 것이므로 Add Up\Down\Left\Right Composite를 선택해줍니다.
그리고 원래 있던 No Binding은 지워줍니다.
그러면 다음과 같은 화면을 볼 수 있는데, 여기서 Up에 들어가서 아까 봤던 대로 Path를 지정해줘야 합니다. Keyboard > 키보드 배열(US등) > 해당 액션으로 사용할 키를 지정해줍니다.
나머지 키들도 마찬가지로 지정해줍니다.
그러면 이제 상하좌우 움직임에 대한 매핑은 끝났습니다.
한 가지 조금 더 눈여겨 볼 것이 있다면, 저기서 Move의 2D Vector를 클릭하면 다음과 같은 화면을 볼 수 있습니다.
Composite Type은 놔두고, 아래 Mode를 보면 Digital Normalized
라는 글자를 볼 수 있습니다.
이것은 입력을 Digital로 읽어 들일 것이며, 그 값을 Normalized 한 값으로 표현하겠다라는 것입니다.
즉, 우리가 상하좌우키로 입력한 이동 입력은 2D Vector 형이며, 그 크기는 1인 벡터로 표현다는 것입니다.
그러면 매핑이 끝났으니, 나머지 매핑 방법은 나중에 알아보고, 우선은 매핑한 값을 사용하는 법을 알아봅시다.
이제 매핑을 완료한 Input Actions 에셋을 해당 입력을 사용할 게임 오브젝트에 붙여줍시다.
저는 이미 만들었던 Player 객체에 이를 붙였습니다.
여기서 봐야 하는 부분이 여럿 있는데, 우선은
Default Scheme
입니다. 처음에 Input Actions에서 만들었던 그 Scheme 입니다. 해당 입력 장치에 알맞은 Scheme을 지정합니다.
Auto-Switch
는 가장 최적화된 Scheme을 자동으로 지정합니다. 예를 들어 입력장치 A,B를 사용하는 Scheme과 B,C를 사용하는 Scheme이 있는데, 사용자가 B,C를 사용하고 있다면 자동으로 B,C를 사용하는 Scheme으로 전환합니다.
Default Map
역시 Input Actions에서 만들었던 그 Map입니다. 플레이어의 움직임을 사용 할 것이므로 Player를 지정합니다.
UI Input Module
과 Camera
는 생략합니다. 주로 UI의 입력을 처리 할 때에 사용하므로 우선 여기서는 다루지 않습니다.
정말 중요한 것은 이제 그 아래에 있는 Behavior
입니다.
Send Messages
, Broadcast Messages
, Invoke Unity Event
, Invoke C# Event
총 4가지 옵션이 있는데,
Send Messages
는 현재 이 Input이 들어가 있는 객체에만 메시지를 한정하는 것이고, Broadcast Messages
는 그 하위 객체에서도 사용하게 하는 것입다.
Invoke Unity Event
나 Invoke C# Event
는 우리가 UI에서 Button 눌렀을 때 이벤트를 지정해주는 것과 동일하다고 보면 됩니다다.
그럼 Send와 Broadcast에는 어떤 것들이 전달이 되느냐,
바로 Behavior 아래에 적힌 문장을 보면 됩니다.
Will SendMessage() to GameObject: OnDeviceLost, OnDeviceRegistered, OnControlsChanged, OnMove, OnLook, OnFire
On~형식의 메시지가 전달된다고 합니다.
이게 어떤 의미인가 하면, 우리가 Unity에서 OnTriggerEnter2D와 같이 전달된다는 뜻입니다.
만약 SendMessages로 해놓았다고 가정하고, Player의 아무 스크립에서나 OnDeviceLost 등에 대해서 처리하고 싶다면 이를 구현해서 쓰면 된다는 것입니다.
하나 더 알아야 하는 것은 앞의 OnDeviceLost
, OnDeviceRegistered
, OnControlsChanged
는 기본적으로 생성되는 것들이고, 뒤에 OnMove
, OnLook
, OnFire
는 우리가 이름을 만든 것들입니다.
그러면 Player의 PlayerController에서 OnMove를 구현봅시다.
public void OnMove(InputValue inputValue)
{
moveVector = inputValue.Get<Vector2>().normalized;
}
OnTriggerEnter2D가 Collision 타입을 받는 것처럼, InputValue 타입을 받고, 여기서 Get<타입>()을 통해 값을 가져옵니다. 만약 타입이 일치하지 않으면 런타임에서 경고 혹은 에러가 뜹니다.
이런 식으로 Player가 움직일 방향에 대한 입력을 받고, FixedUpdate 등에서 실제로 움직이는 처리를 하면 됩니다.
이것은 사실상 InputManager의 이 코드와 동작은 크게 다르지 않습니다.
float x = Input.GetAxis("Horizontal");
float y = Input.GetAxis("Vertical");
moveVector = new Vector3(x, y);
그러면 왜 Input Manager에 비해 이렇게 이렇게 복잡한 방법을 사용하냐?
Input System을 사용하는 이유는 여러 가지가 있는데, 우선 입력 장치의 대응에 있어서 조금 더 유연해진다. 코드 기반으로 처리하는 양을 크게 줄일 수 있고, 해당 대응에 대해 프로그래머가 작성해야 할 것도 줄어듭니다. 그에 따라 입력에 변화가 생겼을 때에 유연하게 대응하기도 좋고, 여러 입력에 한꺼번에 들어올 때에 처리를 하기도 조금 더 수월합니다.
이런 것은 이렇게 말로만 하면 어떤지 사실 감이 잘 오지 않습니다. 그래서 직접 해보는게 가장 좋으므로 앞으로 프로젝트를 할 때에 적극 도입해서 사용해보도록 합시다.
추가로 공식 문서를 한번쯤은 읽어봐도 좋습니다. 아키텍처나 작동구조 등에 대해 설명도 써져 있어서 봐둬서 나쁠 것은 없는 것 같습니다.