[ InputSystem ]
기존에는 인풋 액션 창에서 키를 직접 설정하고 인풋 시스템 컴포넌트를 추가해서 사용했다. 하지만 코드 작성으로 인풋 시스템을 사용할 수도 있다.
캐릭터가 A키와 D키를 통해 좌우로 움직이는 로직을 코드를 사용해서 구현해보자.
우선 기본적으로 스크립트에서 인풋 시스템을 사용하려면 UnityEngine.InputSystem 네임스페이스가 있어야 한다.
using UnityEngine.InputSystem;
다이렉트
다이렉트 방식은 코드로 모든 입력을 받아오는 로직을 작성한다.
public float dir;
Keyboard keyboard;
void Start()
{
keyboard = Keyboard.current; // 현재 연결된 기기 받아옴
}
void Update()
{
if(keyboard == null) return; // 예외 처리
if (keyboard.akey.isPressed)
{
dir = -1; // A키가 눌리고 있으면 왼쪽 방향
}
else if (keyboard.dkey.isPressed)
{
dir = 1; // D키가 눌리고 있으면 오른쪽 방향
}
else
{
dir = 0; // 아무것도 안 눌리면 정지
}
}
플레이어가 현재 사용하고 있는 기기와 특정 키에 대한 정보를 확인하는 코드로 움직임을 구현했다.
위에서는 키보드의 A키를 누르고 있으면 왼쪽으로 가고, D키를 누르고 있으면 오른쪽 방향으로 이동한다. isPressed 외에도 다양한 상태를 입력 받아올 수 있다.
다른 기기들도 동일하다. 예로 마우스는 mouse.rightButton.wasPressedTihsFrame이나 mouse.scroll.ReadValue() 등이 있다. 이외에도 연결만 되어있다면 지원하는 기기 모두 가능하다.
주의점은 여러 기기를 받아오려 할 때 예외 처리를 해주어야 한다. 그래야 기기가 연결되지 않았을 때 에러가 나지 않고 게임이 정상적으로 실행될 수 있다.
임베디드
임베디드 방식은 액션 단위로 받아올 수 있다. 컴포넌트에 해당 스크립트를 추가하면 Move Action에서 바로 인풋 액션 창에서와 같이 키를 설정할 수 있다.
public float dir;
public InputAction moveAction;
void OnEnable()
{
moveAction.performed += PlayerMove; // 누르고 있는 동안 PlayerMove 이벤트 호출
moveAction.canceled += PlayerStop; // 누르던 키를 떼면 PlayerStop 이벤트 호출
moveAction.Enable(); // moveAction 활성화하여 입력받기 시작
}
void OnDisable()
{
moveAction.performed -= PlayerMove; // PlayerMove 이벤트 삭제
moveAction.canceled -= PlayerStop; // PlayerStop 이벤트 삭제
moveAction.Disable(); // moveAction 비활성화하여 입력받기 중단
}
void PlayerMove(InputAction.CallbackContext value)
{
dir = value.ReadValue<float>(); // moveAction 값을 float 타입으로 전환해서 할당
}
void PlayerStop(InputAction.CallbackContext value)
{
dir = 0;
}
컴포넌트에서 키 설정을 한 후, 코드에서 이벤트를 등록하여 실제로 움직임을 구현할 수 있다. 임베디드 방식을 사용하면 Start 메서드 대신 OnEnable과 OnDisable 메서드를 사용한다. 이는 게임 오브젝트의 활성화 상태를 기준으로 이벤트를 관리하기 때문이다. 게임 오브젝트가 활성화될 때 OnEnable이 호출되고, 비활성화될 때 OnDisable이 호출된다.
예를 들어, 플레이어가 씬에 있을 때만 특정 키 입력을 받기를 원한다면, OnEnable에서 인풋 리스너를 등록하고 OnDisable에서 이를 제거함으로써 필요할 때만 입력을 처리할 수 있다.
moveAction 변수는 키 입력에 따라 Negative일 경우 -1, Positive일 경우 1이 된다. 이 값은 InputAction.CallbackContext 객체를 통해 전달된다. 이벤트로 등록하고자 하는 함수는 InputAction.CallbackContext 타입의 매개변수를 받아야 하며, 이 매개변수를 float 타입으로 변환하기 위해서는 value.ReadValue<float>() 메서드를 호출해야 한다.
임베디드 방식의 장점은 오브젝트마다 키셋팅을 다르게 할 수 있다는 점이다. 인스펙터 창에서 키를 각각 설정할 수 있기 때문에, 캐릭터마다 설정을 다르게 하여 멀티 플레이를 지원할 수 있다. 예를 들어 A 캐릭터는 WASD로, B 캐릭터는 ↑↓→←로 움직일 수 있다. 하지만 여러 캐릭터가 있고 모두 동일한 키 값으로 설정할 거라면 해당 방식은 추천되지 않는다.
액션 에셋
기존에 하던 것처럼 액션 창에서 키를 설정하여 인풋 에셋을 생성한다. 그리고 인스펙터에 추가된 스크립트에 인풋 에셋을 할당하여 코드에서 입력을 처리한다.
public InputActionAsset inputAsset; // 인스펙터 창에서 인풋 액션 에셋 할당받음
InputActionMap player;
InputAction move;
InputAction jump;
void OnEnable()
{
player = inputAsset.FindActionMap("Player"); // 이름이 Player인 액션 맵을 가져옴
move = inputAsset.FindAction("Move"); // 이름이 Move인 액션을 가져옴
jump = inputAsset.FindAction("Jump"); // 이름이 Jump인 액션을 가져옴
move.performed += PlayerMove; // 누르고 있는 동안 PlayerMove 이벤트 호출
move.canceled += PlayerStop; // 누르던 키를 떼면 PlayerStop 이벤트 호출
jump.started += Jump; // 누르는 순간 Jump 이벤트 호출
player.Enable(); // player 액션 맵 활성화하여 입력받기 시작
}
void OnDisable()
{
move.performed -= PlayerMove; // PlayerMove 이벤트 삭제
move.canceled -= PlayerStop; // PlayerStop 이벤트 삭제
jump.started -= Jump; // Jump 이벤트 삭제
player.Disable(); // player 액션 맵 비활성화하여 입력받기 중단
}
맵 외에 액션만 독립적으로 활성화하는 것도 가능하다. 예를 들어, 플레이어가 수영 중일 때 점프 기능을 비활성화하려면, jumpAction.Disable(); 메서드를 호출하여 점프 액션을 비활성화할 수 있다. 하지만 이동 기능은 계속 활성화 상태를 유지할 수 있다.
C# 제너레이트
액션 에셋의 인스펙터 창에서 Generate C# Class 옵션을 활성화하고 Apply를 하면, 액션 에셋과 동일한 이름의 C# 스크립트가 생성된다. 액션 에셋에서 설정했던 키 셋팅 값들이 해당 클래스에 구현되어 있다.
PlayerInput input; // PlayerInput 타입의 input 객체 선언
void OnEnable()
{
input = new PlayerInput(); // PlayerInput 객체 초기화
input.Player.Move.performed += PlayerMove; // 누르고 있는 동안 PlayerMove 이벤트 호출
input.Player.Move.canceled += PlayerStop; // 누르던 키를 떼면 PlayerStop 이벤트 호출
input.Player.Jump.started += Jump; // 누르는 순간 Jump 이벤트 호출
input.Enable(); // 액션 맵 활성화하여 입력받기 시작
}
void OnDisable()
{
input.Player.Move.performed -= PlayerMove; // PlayerMove 이벤트 삭제
input.Player.Move.canceled -= PlayerStop; // PlayerStop 이벤트 삭제
input.Player.Jump.started -= Jump; // Jump 이벤트 삭제
input.Disable(); // 액션 맵 비활성화하여 입력받기 중단
}
액션 에셋 방식은 string으로 가져오면서 오타가 발생하면 제대로 작동하지 않는데, C# 제너레이트 방식은 참조로 가져오기 때문에 오타로 인한 문제가 발생할 일은 없다. 또한 액션이 변경될 때마다 클래스 내의 값들도 자동으로 바뀌기 때문에, 따로 수정하거나 다시 생성할 필요가 없어서 편리하다.
UI
UI를 생성하면 EventSystem이라는 오브젝트도 같이 생성된다. 인풋 시스템 패키지를 추가하면 해당 오브젝트 인스펙터에 문구 창이 뜬다.
Replace 버튼을 누르면 인풋 시스템에서 제공하는 UI 컴포넌트로 바뀐다. Action Asset의 에셋을 눌러보면 패키지 폴더에 설치되어 있는 모습을 볼 수 있다. Actions Asset의 에셋을 클릭하면 UI 액션 맵이 있는 액션 에셋을 확인할 수 있다. UI 액션 맵에 있는 값들은 유니티에서 기본적으로 세팅해 놓은 값이다.
UI 맵을 복사해서 만들어 놨던 인풋 액션에 붙여넣기하면, 원래 사용하고 있던 인풋 액션에 UI도 함께 사용할 수 있다. 만약 함께 사용한다면 EventSystem의 액션 에셋도 변경해 주어야 한다. 그러면 UI에서도 인풋 시스템을 활용할 수 있다. 예를 들어 마우스를 사용하지 않고 키보드로 버튼을 선택할 수 있다.
PlayerInput input;
bool isPlaying = true;
GameObject ui;
private void Start()
{
ui = GameObject.Find("UICanvas");
ui.SetActive(false);
}
void OnEnable()
{
~ 중략
input.Changer.Change.Started += Change; // 누르는 순간 Change 이벤트 호출
input.Player.Enable(); // Player 맵 활성화하여 입력받기 시작
input.Changer.Enable(); // Changer 맵 활성화하여 입력받기 시작
}
void OnDisable()
{
~ 중략
input.Changer.Change.Started -= Change; // Change 이벤트 삭제
input.Player.Disable(); // Player 맵 비활성화하여 입력받기 중단
input.Changer.Disable(); // Changer 맵 비활성화하여 입력받기 중단
}
void Change(InputAction.CallbackContext value)
{
isPlaying = !isPlaying;
if (isPlaying)
{
input.UI.Disable(); // UI 액션 맵 비활성화하여 입력받기 중단
input.Player.Enable(); // Player 액션 맵 활성화하여 입력받기 시작
}
else
{
input.UI.Enable(); // UI 액션 맵 활성화하여 입력받기 시작
input.Player.Disable(); // Player 액션 맵 비활성화하여 입력받기 중단
}
ui.SetActive(!isPlaying); // 게임 중이 아니라면 UI를 활성화
Time.timeScale = isPlaying ? 1f : 0f; // 게임 중이라면 시간 흐름, 아니라면 멈춤
}
액션 에셋의 Tab 키를 사용하여 Changer 맵을 통해 UI와 게임 사이의 전환을 제어하는 코드이다. 플레이어가 탭 키를 누를 때마다 게임의 상태는 isPlaying 변수를 통해 전환되고, 해당 상태에 따라 UI 또는 Player 액션 맵이 활성화되거나 비활성화된다.
예를 들어, 플레이어가 게임 중이 아닐 때(UI 모드)는 UI 액션 맵이 활성화되고 Player 액션 맵이 비활성화된다. 반대로, 게임을 진행 중일 때는 Player 액션 맵이 활성화되고 UI 액션 맵이 비활성화된다.
이외에도 수영, 비행 등과 같은 다른 행동 로직을 가진 경우 새로운 키 값을 셋팅해야 한다면, 액션 맵을 추가해서 활성화, 비활성화하는 것으로 쉽게 키 입력을 선택적으로 받을 수 있다.
Action Properties
인터랙션과 프로세서는 start가 아닌 performed일 때 작동한다.
Interactions
Press Point는 게임 패드 등에서 얼마나 눌렸는지를 감지해서 설정한 값 이상으로 눌려야 입력이 전달된다. 키보드에서는 무조건 눌림/안눌림으로만 판단해서 의미가 없다.
- Hold
- Hold Time은 몇 초 동안 누르고 있어야 동작하게 할 것인지 설정한다. 설정한 시간동안 키가 눌리면 이후 자동으로 동작한다.
- Tap
- Max Tap Duration은 몇 초 안에 눌렀다 떼야 동작하게 할 것인지 설정한다. 설정한 시간 안에 눌렀다 떼면 동작한다. 만약 0.5로 설정해놓고 눌렀다가 1초 뒤에 떼면 동작하지 않는다.
- Slow Tap
- Min Tap Duration은 Tap의 Max Tap Duration과 반대로 몇 초 뒤에 눌렀다 떼야 동작하게 할 것인지 설정한다. 0.5로 설정한다면, 눌렀다가 0.5초 이후에 떼야 동작한다.
- Press
- Press Only: 눌렀을 때 입력을 받음
- Release Only: 뗐을 때 입력을 받음
- Press And Release: 둘 다 입력을 받음
- Multi Tap
- Tap Count: 몇 번 눌러야 동작하게 할 것인지 설정한다.
- Max Tap Spacing: 각 탭 사이의 시간을 설정한다. 0.75로 설정하면 탭 두 번을 0.75 안에 연타해야 동작한다.
- Max Tap Duration: 각 탭을 몇 초 안에 떼야하는지 설정한다.
Processors
- Invert
- 값이 역으로 들어온다. 왼쪽은 -1, 오른쪽은 1 값이 들어온다면, 인버트로 인해 왼쪽이 1, 오른쪽이 -1이 된다.
- Normalize
- 정규화의 범위를 설정한다.
- Clamp
- 실제로 입력되는 값이 지정된 최소값과 최대값의 범위 안에 있도록 제한한다.
- Scale
- 실제로 입력되는 값에 배로 곱해준다.
- Axis Deadzone
- 게임 패드의 조이스틱이 살짝 움직였을 때 입력 값을 받아오지 않도록 설정하는 범위이다. Min과 Max 값을 설정해서 입력을 받아올 조이스틱의 움직임 범위를 정할 수 있다. 키보드에서는 사용되지 않는다.
'Coding > Unity' 카테고리의 다른 글
[내일배움캠프 43일차 TIL] Input System C# 제너레이트 활용 (0) | 2024.06.18 |
---|---|
[내일배움캠프 42일차 TIL] 파티클 시스템, 애니메이션 이벤트, 사운드 (1) | 2024.06.14 |
[내일배움캠프 38일차 TIL] 아이템 데이터 참조 에러 트러블 슈팅 (0) | 2024.06.10 |
[내일배움캠프 35일차 TIL] 컴포넌트에 데이터 자동 할당, Can't add script 에러 (0) | 2024.06.04 |
[내일배움캠프 32일차 TIL] 아이템 사용 트러블 슈팅 (0) | 2024.05.30 |