<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 컴포넌트 : 입력 행동을 받아 해당 게임오브젝트에 메시지로 전달해주는 역할

인자값이 타입이 다름

Unity로 작업을 할때 충돌처리를 하기 위해서 많이 쓰던 메서드이다.

 

하지만, 수업을 듣는 도중 Unity 패키지의 자동완성으로 나오는 충돌처리 메서드의 인자값의 타입이 다르다는 말을 듣고나서 찾아보니 진짜 달랐다.

그 둘의 차이점을 알고싶어 따로 알아보았다.

 

<OnCollison~(Collison )>

using UnityEngine;

    private void OnCollisionEnter(Collision collision)
    {
        
    }

OnCollison~()메서드는 두개의 물체가 충돌 할때 충돌 지점,충돌한 물체,물리적 힘 등의 정보를 처리해야된다.

Collsion 객체는 충돌관 관련된 위의 정보를 담고 있기에 해당 타입을 사용하는 것이다.

 

Collision Class 내부 모습 , Impulse,Velocity 등등 물리와 관련된 변수가 보인다.

<OnTrigger~(Collider )>

using UnityEngine;

 	private void OnTriggerEnter(Collider other)
 	{
     
 	}

OnTrigger()는 객체들이 충돌 했을때 물리적 충돌은 발생하지 않고 , Collider로 들어온 객체의 Data를 인자값으로 들고온다. 즉 쉽게 말하기 위해 충돌이라고 하는거지 해당 트리거 영역에 객체가 들어왔을때 동작하는 메서드인것이다. 

하지만 물리동작을 하지 않는다고 Rigidbody를 빼놓으면 안된다.

(움직이는 쪽이 물리 시뮬레이션을 하면서 충돌체크를 하기 때문)

 

Collider Class의 내부 모습, 물리 충돌 변수가 존재하지 않는다 그 대신에 isTrigger 변수가 눈에 보인다.

 

결론 : OnCollison() 메서드와 OnTrigger()메서드의 자동완성 인자값이 다르기에 실제로 사용할때 주의하여 사용하자

+ Recent posts