Unity - Input System

MothorMoth·2024년 5월 13일
0

Unity

목록 보기
1/5
post-thumbnail

Unity - Input System (1.7.0)

Unity 2019.4+ 및 .NET 4 부터 사용할 수 있는 Unity의 새로운 입력 처리 시스템.

1. 패키지 설치

Package Manager에서 Input System을 설치하면 사용할 수 있다.


2. 기본 개념

기본 개념은 Unity 공식 문서에 다음과 같이 작성되어 있다.


3. Input System 워크플로

3-1. device 상태 직접 읽기


가장 간단하고 직접적인 워크플로이지만 유연성이 가장 낮으며, 한 가지 유형의 장치로 빠른 구현을 원하는 경우 유용하다.

장치 컨트롤을 참조하고 현재 생성 중인 값을 읽어 연결된 장치에서 값을 직접 읽을 수 있다.

using UnityEngine;
using UnityEngine.InputSystem;
public class MyPlayerScript : MonoBehaviour
{
    void Update()
    {
        var gamepad = Gamepad.current;
        if (gamepad == null)
        {
            return; // 연결된 게임패드 없음
        }

        if (gamepad.rightTrigger.wasPressedThisFrame)
        {
            // 'Use' code 구현
        }

        Vector2 move = gamepad.leftStick.ReadValue();
        {
            // 'Move' code 구현
        }
    }
}

위 예시에서는 현재 연결된 게임패드의 오른쪽 트리거와 왼쪽 스틱에서 직접 값을 읽는다.

Input System의 "Action" 클래스를 사용하지 않고, 대신 "Use" 및 "Move"와 같은 개념적 동작이 입력에 대한 응답으로 코드가 수행하는 작업에 의해 암시적으로 정의된다.

해당 워크플로의 단점

  • Unity의 Action 및 interaction으로부터 이점을 얻을 수 없다.
  • 여러 유형의 입력 장치에서 작동하도록 만드는 것이 더 어렵다.
  • 입력 바인딩은 스크립트에 하드 코딩되어 있으므로 바인딩을 변경하려면 코드를 변경해야 한다.
  • 사용자가 런타임에 자신의 컨트롤을 다시 매핑하도록 허용하는 것은 더 어렵다.

3-2. Actions 사용


스크립트에서 InputAction 클래스를 사용하여 Action을 정의할 수 있다.

device 상태를 직접 읽어 코드에서 어떤 컨트롤(예: 게임패트 트리거 또는 스틱)이 수행해야 하는지 명시적으로 지정하는 대신 Action을 만들어 컨트롤에 바인딩하고 코드에 있는 Action의 상태나 값에 응답한다.

MonoBehaviour 스크립트에서 public InputAction 필드를 만들면 인스펙터에 구성 가능한 필드로 표시된다. 구성 가능한 필드 UI를 사용하면 Action에 대한 바인딩을 만들 수 있다.

using UnityEngine;
using UnityEngine.InputSystem;

public class ExampleScript : MonoBehaviour
{
    public InputAction move;
    public InputAction jump;
}


위 예시처럼 InputAction 클래스를 사용하여 Action을 정의하면 인스펙터에서 Action을 구성할 수 있다.

인스펙터에서 장치의 컨트롤에 Action을 바인딩하면 스크립트에서 특정 장치에 대한 참조를 하드 코딩하지 않고도 Action이 수행될 때 응답하도록 스크립트를 디자인할 수 있다. 이 추상화 계층은 코드를 변경할 필요 없이 여러 바인딩을 수정하거나 추가할 수 있는 유연성을 제공한다.

Action에서 값을 읽으려면 Action을 활성화 한 다음 게임 루프에서 Action의 현재 값이나 상태를 반복적으로 읽거나 Action에 이벤트 핸들러를 추가해야 한다. 입력이 더 이상 이벤트 핸들러를 트리거하지 않게 하려면 Action을 비활성화 해야한다.

위 예시에서 정의된 Action을 사용할 때 다음과 같이 스크립트를 작성할 수 있다.

using UnityEngine;
using UnityEngine.InputSystem;

// 콜백과 함께 Action을 사용하거나, 각 프레임마다 Action의 값을 읽는다

public class ExampleScript : MonoBehaviour
{
    // InputAction 클래스를 사용하면 인스펙터에서 구성할 수 있다
    public InputAction moveAction;
    public InputAction jumpAction;

    public void Awake()
    {
        // 'jump' Action에 대한 콜백 할당
        jumpAction.performed += callbackContext => { OnJump(callbackContext); };
    }

    public void Update()
    {
        // 매 프레임마다 'move' Action의 값을 읽는다
        Vector2 moveAmount = moveAction.ReadValue<Vector2>();
    }

    public void OnJump(InputAction.CallbackContext context)
    {
        // 'jump' code 구현
    }

    // Action 활성화와 비활성화
    public void OnEnable()
    {
        moveAction.Enable();
        jumpAction.Enable();
    }

    public void OnDisable()
    {
        moveAction.Disable();
        jumpAction.Disable();
    }
}

3-3. Actions Asset 사용


Actions Asset은 Action 세트를 직접 정의하는 대신 에셋에 저장된 데이터로 정의, 그룹화, 관리한다.

Actions Asset을 사용하면 Action에 응답해야하는 GameObject와 별도로 Action을 정의하는 데이터를 유지할 수 있어 코드에 직접 Action 정의를 삽입하는 것과 비교하여 더 높은 수준의 추상화 및 구성을 제공한다.
또한 스크립트 및 프리팹과 별도로 단일 자산 파일로 저장된다는 점에서 개념적으로 관리하기가 더 간편하다.

Control Schemes을 사용하면 아래 예시의 '게임패드' 와 '키보드' 같이 정의한 Control Scheme에 속하는 여러 바인딩을 지정할 수 있다.


스크립트에서 Actions Asset에 접근

스크립트에서 Actions Asset에 접근하는 두 가지 방법이 있다.

  1. Actions Asset에 대한 인스펙터 참조 사용
  2. Actions Asset을 래핑하는 C# 클래스 생성

인스펙터 참조를 사용하면 문자열을 사용하여 이름별로 Action을 읽어야 하고, C# 클래스 생성을 사용하면 Unity는 래퍼 역할을 하는 새로운 .cs 스크립트 자산 으로 동반 클래스를 생성한 뒤 코드에서 생성된 래퍼 클래스의 인스턴스를 만들고 구성한 Action 이름을 따라 이름이 지정된 해당 API 멤버를 직접 사용할 수 있다.

인스펙터에서 Actions Asset 참조

  1. 스크립트에 public InputActionsAsset 선언
  2. 인스펙터에서 참조 할당
  3. 문자열을 사용하여 이름으로 스크립트의 작업에 접근
using UnityEngine;
using UnityEngine.InputSystem;

public class ExampleScript : MonoBehaviour
{
    // 인스펙터에서 해당 필드에 Actions Asset 할당
    public InputActionAsset actions;

    // 'move' Action 참조를 저장하는 private 필드
    private InputAction moveAction;

    void Awake()
    {
        // Update에서 사용할 수 있도록 'move' Action을 찾아 참조 유지
        moveAction = actions.FindActionMap("gameplay").FindAction("move");

        // 'jump' Action이 수행될 때를 위한 'OnJump' 콜백 메서드 추가
        actions.FindActionMap("gameplay").FindAction("jump").performed += OnJump;
    }

    void Update()
    {
        // Update에서 매 프레임마다 'move' Action 값을 읽는다
        Vector2 moveVector = moveAction.ReadValue<Vector2>();
    }

    // 'jump' Action 콜백 메서드
    private void OnJump(InputAction.CallbackContext context)
    {
        Debug.Log("Jump!");
    }

    // Action 활성화와 비활성화
    void OnEnable()
    {
        actions.FindActionMap("gameplay").Enable();
    }
    void OnDisable()
    {
        actions.FindActionMap("gameplay").Disable();
    }
}

참고: 'move' Action에 대한 참조는 발견된 후 moveAction 변수에 저장되어 매프레임 마다 문자열로 엑세스하는 것을 방지한다. 성능에 좋지 않기 때문.

C# 래퍼를 통해 Actions Asset 참조

  1. 프로젝트 창에서 Actions Asset 선택
  2. 인스펙터에서 C# 클래스 생성을 활성화 하고 적용을 선택(프로젝트 창에 Actions Asset과 동일한 이름의 C# 자산이 표시되어야 함)
  3. 스크립트에서 Actions C# 클래스의 인스턴스 생성
  4. Actions C# 클래스의 API를 사용하여 스크립트의 작업에 접근
using UnityEngine;
using UnityEngine.InputSystem;

public class DocsExampleActionsAssetCsWrapper : MonoBehaviour
{
    // Action 래퍼 인스턴스
    ExampleActions actions;

    void Awake()
    {
        // Action 래퍼 클래스 인스턴스화
        actions = new ExampleActions();

        // 'jump' Action이 수행될 때를 위한 'OnJump' 콜백 메서드 추가
        actions.gameplay.jump.performed += OnJump;
    }

    void Update()
    {
        // Update에서 매 프레임마다 'move' Action 값을 읽는다
        Vector2 moveVector = actions.gameplay.move.ReadValue<Vector2>();
    }

    // 'jump' Action 콜백 메서드
    private void OnJump(InputAction.CallbackContext context)
    {
        Debug.Log("Jump!");
    }

    // Action 활성화와 비활성화
    void OnEnable()
    {
        actions.gameplay.Enable();
    }
    void OnDisable()
    {
        actions.gameplay.Disable();
    }
}

3-4. Actions Asset 및 PlayerInput 컴포넌트 사용


PlayerInput 컴포넌트는 Actions Asset에 대한 참조를 취하고 해당 자산에 정의된 Action과 자체 MonoBehaviour 스크립트의 C# 메서드를 연결하는 방법을 제공하므로 사용자가 입력 작업을 수행할 때 원하는 C# 메서드가 호출된다.

연결을 만들기 위한 코드를 작성하는 대신 인스펙터를 통해 연결을 설정하고, 해당 메서드가 호출되는 방식을 선택할 수 도 있다.

일반적으로 Action에 대한 응답을 처리해야하는 메서드가 포함된 MonoBehaviour 스크립트와 동일한 GameObject에 PlayerInput 컴포넌트를 추가한다.


위 예시에서는 Unity 이벤트를 통해 "move", "jump" Action을 스크립트의 OnMoveOnJump 메서드에 매핑하도록 설정된 PlayerInput 컴포넌트를 볼 수 있다.

using UnityEngine;
using UnityEngine.InputSystem;

public class ExampleScript : MonoBehaviour
{
    Vector2 moveAmount;

    public void OnMove(InputAction.CallbackContext context)
    {
        // 각 이벤트 호출마다 'move' Action의 값을 읽는다
        moveAmount = context.ReadValue<Vector2>();
    }

    public void OnJump(InputAction.CallbackContext context)
    {
        // jump code 구현
    }

    public void Update()
    {
        // 매 프레임 마다 'move' Action에서 Vector2 값을 사용하려면
        // 'moveAmount' 변수 사용
    }
}

PlayerInput 컴포넌트 사용 장단점

다른 워크플로와 비교하면 Actions Asset을 참조하거나 이벤트 핸들러 메서드를 설정할 필요가 없기 때문에 더 적은 코드를 작성할 수 있지만,
에디터에서 더 많은 설정이 필요하며 작업과 코드간의 연결이 하드 코딩되지 않기 때문에 디버깅이 더 어려워 질 수 있다.

PlayerInput 컴포넌트를 사용하면 코드를 작성하지 않고도 GameObject 컴포넌트의 public 메서드에서 모든 Action을 연결할 수 있는 유연성이 제공되며 코드를 수정하지 않고도 연결을 수정할 수 있다.

그러나 코드를 적게 작성하더라도 PlayerInput 컴포넌트에서 연결을 설정하고 추척하는 것보다 스크립트에서 코드로 연결해 주는 것이 더 간단하고 빠르다는 것을 알 수 있다.

0개의 댓글

관련 채용 정보