<InputManager>

Unity 작업시 기존부터 사용되어왔던 입력처리 패키지로 비교적 쉽게 사용가능하다는 장점이 있다.

Unity-ProjectSetting에 접근시 기본적으로 설치되어 있는 패키지 중 하나이다.

*Inspect를 통한 간단한 키매핑
    public KeyCode Up;
    public KeyCode Down;
    void Update()
    {
        movement = 0f;
        if(Input.GetKey(Up)) { movement += 1f; }
        if(Input.GetKey(Down)) { movement -= 1f; }
        rigidbody.velocity = new Vector2(0, movement * speed);    
    }

InputManager를 이용하여 키 맵핑과 오브젝트 조작까지 동작하게하는 코드이다.

 

위에 코드 적용시 Inspector에서 이런식으로 키맵핑이 가능해진다.

 

<InputManager 를 지양하게 된이유>

1.다양한 입력 장비 대응 : 현 게임시장은 다양한 플랫폼이 제공되고 있고 , 키맵핑도 매우 자유롭다.
하지만 InputManager는 해당 문제를 대응하기 부적절하다.
2.입력 처리와 실제 로직 실행주체의 분리의 목적
InputManager의 자체 문제는 아니지만, 기능 별로 class를 나누는 작업을 해야 유지 보수 및 확장성이 많이 상승한다.

-> OPP(객체지향프로그래밍) 에서 이런 설계 원칙을 단일책임원칙(SRP)라고 부름

 

위에 이렇게 단점이 있지만 실제로도 간단한 프로젝트를 만들때는 아직도 많이 사용된다.

 

<InputSystem>

위에 말한 InputManager의 단점을 극복하기 위해 새로 나온 패키지이다.

해당 패키지는 Unity 패키지에 포함되어있지 않기에 ProjectManager를 통해서 설치해야한다.

window - packageManager-inputsystem 설치 ( 현재 설치되있는상태라 Remove로 뜸)

 

InputSystem의 추가로 Unity에서 사용하는 모습

Input Action : 해당 동작에 대한 키를 맵핑 데이터를 모아두는 파일

Player Input 컴포넌트 : 자동으로 입력 행동을 처리하고 해당 게임 오브젝트에 메시지를 보내는 역할

 

Player Input의 Behaviour은 무엇인가?

해당 Input 입력 발생시 해당 게임 오브젝트에 메시지 보내는 여러 방식중 하나 라고 생각하면 된다.

https://docs.unity3d.com/Packages/com.unity.inputsystem@1.3/manual/Components.html#notification-behaviors

 

GameObject components for input | Input System | 1.3.0

GameObject components for input The Input System provides two MonoBehaviour components that simplify how you set up and work with input: Component Description PlayerInput Represents a single player, and that player's associated Input Actions. PlayerInputMa

docs.unity3d.com

해당 글음 참고하여 작성하였음

이름 특징
SendMessages  PlayerInput을 컴포넌트로 가지고있는 GameObject에 코드내용을 전달
MonoBehaviour 의 update,start와 같은 이벤트함수처럼 사용이 가능해짐
BroadCastMessages PlayerInput을 컴포넌트로 가지고있는 GameObject와 그 자식들에게 코드내용을 전달
성능적인 면에서 하자가 있기에 잘 사용되진 않음
InvokeUnityEvents UI의 button 컴포넌트처럼 Inspector 에서  Onclick() 이벤트 함수처럼 메서드를 추가해서 사용하는것 
Invoke CSharp Events 위의 기능과 비슷하지만 PlayerInput API에서 코드를 호출하여 사용이 가능하고 , inspector 표시되지 않고 오직 스크립트 코드로 동작한다.

onActionTriggered (collective event for all actions on the player)
onDeviceLost
onDeviceRegained 

해당 메서드들이 API에 등록되있다고 함

 

<InputSystem 사용>

Input Action Asset 에서 맵 추가하는 방법

 

 

그 외에도 기능이 많다. 예를들어 키를 홀드하고있는지 키를 몇번 더블 클릭하는지 설정이 가능하지만 기본적인것만 적어보았다.

Action Type 이름 : 입력을 어떻게 받을 것인지 설명
Value 일반적인 상태에 사용함 , 눌렀을 때, 누르고 있을때, 뗄 떼 등 다양한 상황에 대응이 가능해짐
Button 눌렀을때 발생하는 액션에 활용 되며 , Control 타입이 Button으로 고정 됨
Pass-Through 명확화(Disambiguation)을 거치지 않은 Value.
Value 쓰시면 됩니다.

입력이 들어오면 그 즉시 처리 되기에 중복 입력에 대한 필터링이 없음(액션 상태에 따른 시작,중지)가 이루어지지 않음

연속적인 입력 처리가 필요할때 주로 사용,동일한 입력에서 여러 프레임에 걸쳐 이벤트를 받을 수 있음

언제 사용하면 됨??
축(axis)기반 입력이나 마우스의 움직임 , 복잡한 키 조합
: 축적된 입력을 매 프레임마다 처리해야 하는 경우
ex)조이스틱 축, 마우스 이동 -> 연속적인 입력으로 매 프레임마다 값을 처리해야함
ex2) 멀티 터치 입력 -> 여러 입력을 동시에 수집하고 처리해야됨, 중복입력 필터링이 없기에 모든 입력을 받음

 

<InputSystem 예제>

using UnityEngine.InputSystem;

public void OnMove(InputValue value)
  {
      Debug.Log("OnMove" + value.ToString());
      Vector2 moveInput = value.Get<Vector2>().normalized;
      CallMoveEvent(moveInput);
      //실제 움직이는 처리는 여기서 작업하는게 아님 
      //PlayerMovement에서 작업을 하는것
  }

  public void OnLook(InputValue value)
  {
      //Debug.Log("OnLook" + value.ToString());
      Vector2 newAim = value.Get<Vector2>();
      //마우스 위치를 정규화 해버리면 제대로된 좌표가 반환되지 않음
      Vector2 worldPos = _camera.ScreenToWorldPoint(newAim);//화면 좌표계에 있기에 월드 좌표로 변환
      //벡터의 뺼셈으로 원하는 방향벡터를 계산
      newAim = (worldPos - (Vector2)transform.position).normalized;

      if (newAim.magnitude >= .9f)
      {
          CallLookeEvent(newAim);
      }
  }

 

수업 듣는 InputController의 구조를 살짝 떼왔다. Event Action으로 각 기능의 함수를 동작시키는 형태인데,

InputValue 타입의 변수 인자값을 받아 해당 동작에서 사용하는 간단한 예시이다.

 

하지만 게임 프로젝트 규모가 커질수록 Input Map들도 많아지게되며, map에 직접 접근해서 사용해야되는 경우가 존재한다.

 

ChatGPT - 참고 코드 : 사용시 꼭 읽어보고 사용할것 

using UnityEngine;
using UnityEngine.InputSystem;

public class ActionMapSwitcher : MonoBehaviour
{
    // InputActionAsset은 Unity의 Input Actions Asset 파일을 연결할 때 사용됩니다.
    public InputActionAsset inputActions;

    // Action Maps
    private InputActionMap playerActionMap;
    private InputActionMap uiActionMap;

    private void Awake()
    {
        // InputActionAsset에서 각 Action Map을 가져옴
        playerActionMap = inputActions.FindActionMap("Player");
        uiActionMap = inputActions.FindActionMap("UI");

        // Player Action Map에서 'Move' 액션을 가져와 이벤트 등록
        InputAction moveAction = playerActionMap.FindAction("Move");
        moveAction.started += OnMoveStarted;
        moveAction.performed += OnMovePerformed;
        moveAction.canceled += OnMoveCanceled;

        // UI Action Map에서 'Navigate' 액션을 가져와 이벤트 등록
        InputAction navigateAction = uiActionMap.FindAction("Navigate");
        navigateAction.started += OnNavigateStarted;
        navigateAction.performed += OnNavigatePerformed;
        navigateAction.canceled += OnNavigateCanceled;
    }

    private void OnEnable()
    {
        // 기본적으로 Player Action Map 활성화
        playerActionMap.Enable();
    }

    private void OnDisable()
    {
        // 비활성화 시 모든 액션맵을 비활성화
        playerActionMap.Disable();
        uiActionMap.Disable();
    }

    // 'Move' 액션 관련 콜백 함수들
    private void OnMoveStarted(InputAction.CallbackContext context)
    {
        Debug.Log("Move started");
    }

    private void OnMovePerformed(InputAction.CallbackContext context)
    {
        Vector2 moveInput = context.ReadValue<Vector2>();
        Debug.Log($"Move performed: {moveInput}");
    }

    private void OnMoveCanceled(InputAction.CallbackContext context)
    {
        Debug.Log("Move canceled");
    }

    // 'Navigate' 액션 관련 콜백 함수들
    private void OnNavigateStarted(InputAction.CallbackContext context)
    {
        Debug.Log("Navigate started");
    }

    private void OnNavigatePerformed(InputAction.CallbackContext context)
    {
        Vector2 navigateInput = context.ReadValue<Vector2>();
        Debug.Log($"Navigate performed: {navigateInput}");
    }

    private void OnNavigateCanceled(InputAction.CallbackContext context)
    {
        Debug.Log("Navigate canceled");
    }

    // Action Map 전환 예시
    public void SwitchToUI()
    {
        // Player Action Map 비활성화
        playerActionMap.Disable();

        // UI Action Map 활성화
        uiActionMap.Enable();
        Debug.Log("Switched to UI Action Map");
    }

    public void SwitchToPlayer()
    {
        // UI Action Map 비활성화
        uiActionMap.Disable();

        // Player Action Map 활성화
        playerActionMap.Enable();
        Debug.Log("Switched to Player Action Map");
    }

    private void Update()
    {
        // 예시: Escape 키를 누르면 UI로 전환, 다시 누르면 Player로 전환
        if (Keyboard.current.escapeKey.wasPressedThisFrame)
        {
            if (playerActionMap.enabled)
            {
                SwitchToUI(); // UI로 전환
            }
            else
            {
                SwitchToPlayer(); // Player로 전환
            }
        }
    }
}

 

해당 코드는 게임내에서의 Player와 UI창의 키 맵핑을 다르게 하고 사용하는 모습이다.

Esc 키를 누르면 UI로 전환 한번 더 누르면 Player로 전환하는 방식이다. 

 


결론 : 

1.InputManager는 간단한 프로젝트 진행시 사용하기 좋다.

 

2.InputSystem은 규모가 커지고 다양한 플랫폼에 제공 및 키 맵핑 변경을 한다면 좋은 선택지이다.

 

3.InputSystem의 핵심 개념은 , InputAction , Input Action Asset , Player Input 컴포넌트 이 세가지이다.

 

InputAction : 프로젝트 창에서 생성 가능한 Input 맵핑 데이터가 있는 파일

Input Action Asset : 여러 개의 입력 행동을 그룹화하는 방법, InputAction을 열면 설정창에서 ActionMaps,Actions,AcitonProperites를 수정할 수 있다.

Player Input 컴포넌트 : 입력 행동을 받아 해당 게임오브젝트에 메시지로 전달해주는 역할

+ Recent posts