상세 컨텐츠

본문 제목

1016 TIL - Unity InputSystem 심화

스파르타 코딩캠프/'24 Today I Learned

by lucar 2024. 10. 16. 20:57

본문

금일 강의 내용 정리

 

저번에 InputSystem에 대해 간단하게 정리했는데

보통 5가지의 방법으로 InputSystem이 사용된다.

 

아래의 내용은 동일하게 2D 평면 상 움직이는 캐릭터가 좌, 우로 이동할 때를 가정한다.

 

1. 레거시 방식

public class PlayerInputManager : MonoBehaviour
{
    public float dir;

    private void Update()
    {
        if (Input.GetKey(KeyCode.A))
        {
            dir = -1;
        }
        else if (Input.GetKey(KeyCode.D))
        {
            dir = 1;
        }
        else
        {
            dir = 0;
        }
    }
}

 

가장 먼저 볼 것은 Input.GetKey를 이용한 레거시 방식이다.

InputSystem을 사용하지 않는다.

 

구조도 단순하고 Input.GetKey를 입력하고 괄호를 열어보면

키보드에 있는 온갖 키들이 대응되는 걸 확인 할 수 있어서

스크립트로 직접 작성한다면 편리하다.

 

하지만 Update구문을 통해 if 검증을 하기 때문에

매 프레임마다 연산을 반복해 굉장히 무거워지는 특징이 있다.

 

2. 다이렉트 방식

public class PlayerInputManager : MonoBehaviour
{
    // 인풋시스템 - 다이렉트

    public float dir;

    Keyboard keyboard;
    Mouse mouse;
    Gamepad gamepad;

    private void Start()
    {
    //.current = 현재 연결된 장치
        keyboard = Keyboard.current; 
        mouse = Mouse.current;
        gamepad = Gamepad.current;
    }

    private void Update()
    {
        if (keyboard.aKey.isPressed)
        {
            dir = -1;
        }
        else if (keyboard.dKey.wasPressedThisFrame)
        {
            dir = 1;
        }
        else
        {
            dir = 0;
        }

        //keyboard.aKey.wasPressedThisFrame; 눌렀을 때 단 한 프레임 실행 됨
        //keyboard.aKey.isPressed; 누르고 있는 동안 실행 됨
        //keyboard.aKey.wasReleasedThisFrame; 누르다가 뗀 순간 실행 됨
        //mouse.leftButton.isPressed; 마우스 클릭
        //mouse.scroll.ReadValue(); 마우스 스크롤 값을 읽음
        //gamepad.dpad.ReadValue(); 게임패드 D패트 값을 읽음
    }
}

 

위의 레거시 방식을 InputSystem을 이용해 작성하는 방식이다.

 

using UnityEngine.InputSystem;을 사용한다면

Keyboard.current 등을 사용하여 현재 연결되어 있는 장치에 바로 연결된다.

 

레거시와 비슷하나 Input.GetKey(KeyCode)대신 keyboard.?Key.isPressed를 사용한다.

 

마찬가지로 Update문에서 실행되기 때문에 많은 연산을 필요로 할 수 있다.

 

3. 임베디드 방식

public class PlayerInputManager : MonoBehaviour
{
    // 인풋시스템 - 임베디드 방식

    public float dir;

    public UnityAction jumpEvent; // ≒ event Action

    public InputAction moveAction;
    public InputAction jumpAction;

    private void OnEnable() //이 객체가 SetActive(true)상태가 되었을 때 (Awake보다 우선)
    {
        moveAction.performed += PlayerMove; //moveAction에 맞는 값이 입력되는 동안 ≒ isPressed
        moveAction.canceled += PlayerStop; //moveAction에 값 입력이 취소될 때 ≒ wasReleasedThisFrame

        jumpAction.started += PlayerJump; //jumpAction에 맞는 값이 최초 입력 될 때
                                          // ≒ wasPressedThisFrame

        moveAction.Enable(); //moveAction을 활성화
        jumpAction.Enable(); //jumpAction을 활성화
        //활성화되지 않으면 실행되지 않음
    }

    private void OnDisable() //이 객체가 SetActive(false)상태가 되었을 때 (Destroy포함)
    {
        moveAction.Disable();
        jumpAction.Disable();

        jumpAction.started -= PlayerJump;
        moveAction.performed -= PlayerMove;
        moveAction.canceled -= PlayerStop;
    }

    void PlayerMove(InputAction.CallbackContext value)
    {
        dir = value.ReadValue<float>();
    }

    void PlayerStop(InputAction.CallbackContext value)
    {
        dir = 0;
    }

    void PlayerJump(InputAction.CallbackContext value)
    {
        jumpEvent?.Invoke();
    }
}

 

우선 InputAction형의 변수를 선언하면 유니티 내부에 해당 칸이 나타나게 된다.

우선 좌우로 이동과 점프를 추가해보자.

 

추가해 주었다면 

다시 스크립트에서 제일 아래 3개의 메소드를 살펴보자.

 

 PlayerMove메소드는 moveAction타입에 추가되기 위해서 CallbackContext라는 매개변수를 필요로 한다.

키 바인딩은 이미 위에서 해주었기 때문에

A와 D라는 value가 입력되면 dir에 각각 Positive(양수)나 Negative(음수)가 들어가게 되고,

해당 값을 PlayerMoveManager와 같은 클래스를 제작하여 dir값으로 이동시키게 되는 것이다.

 

Jump의 경우도 Space바를 입력받게 되면 jumpEvent를 실행하게 되고

PlayerMoveManager와 같은 클래스에서 RigidBody에 Addforce(Vector2.up, impulse)등을 통해 점프를 구현하게 된다.

 

 

4. 액션 에셋 방식

public class PlayerInputManager : MonoBehaviour
{
    // 인풋시스템 - 액션 에셋 방식

    public float dir;
    public UnityAction jumpEvent;

    public InputActionAsset inputAsset; //InputAction을 직접 받아옴

    InputActionMap basic; //ActionMap 변수 선언

    InputAction moveAction; //Action 변수 선언
    InputAction jumpAction;


    private void OnEnable()
    {
        basic = inputAsset.FindActionMap("basic"); //  !!대소문자 구분!!

        moveAction = basic.FindAction("move"); //  !!대소문자 구분!!
        jumpAction = basic.FindAction("jump"); //  !!대소문자 구분!!

        moveAction.performed += PlayerMove;
        moveAction.canceled += PlyerStop;
        jumpAction.started += PlayerJump;

        basic.Enable(); //액션맵을 통해 한 번에 활성화 해도 되고
        //moveAction.Enable(); //액션에 접근해 각각 활성화 해도 된다.
        //jumpAction.Enable();
    }

    private void OnDisable()
    {
        basic.Disable();
        //moveAction.Disable();
        //jumpAction.Disable();

        jumpAction.started -= PlayerJump;
        moveAction.performed -= PlayerMove;
        moveAction.canceled -= PlyerStop;
    }

    void PlayerMove(InputAction.CallbackContext value)
    {
        dir = value.ReadValue<float>();
    }

    void PlyerStop(InputAction.CallbackContext value)
    {
        dir = 0;
    }

    void PlayerJump(InputAction.CallbackContext value)
    {
        jumpEvent?.Invoke();
    }
}

 

 

우선 InputSystem을 하나 작성해보자.

 

 

 public InputActionAsset inputAsset; 을 작성하게 되면 컴포넌트에 InputSystem을 직접 삽입할 수 있게 된다.

 

 

위에 작성한 InputSystem을 보면 Action Maps와 Actions로 나눠져 있는 것을 볼 수 있다.

 

각각 InputActionMap과 InputAction으로 변수를 선언해 준 후

OnEnable에서 InputSystem과 각각의 액션들을 연결해 줄건데

 

반드시 대소문자까지 맞춰서 입력해야한다.

basic = inputAsset.FindActionMap("basic");
moveAction = basic.FindAction("move");
jumpAction = basic.FindAction("jump");

한 글자라도 틀리거나 대소문자 구분이 엇나가면 작동이 안된다. 명심하자

 

임베디드 방식은 컴포넌트에서 직접 바인딩을 했다면

액션 에셋 방식은 바인딩 된 InputAction을 직접 가져와 사용하게 된다.

 

이 방법을 통해 바인딩을 하게된다면

ActionMap을 여러개 만들고 경우에 따라 다르게 동작하게 만들어 줄 수 있다.

 

자동차에 탑승했을 때

w는 가속, s는 감속, a,d는 회전이라고 했을 때

 

car라는 바인딩을 새로 만들고 Enable과 Disable을 번갈아 사용해주면?

void Change(InputAction.CallbackContext value)
{
    if (isPlaying)
    {
        basic.Enable();
        car.Disable();
    }
    else
    {
        basic.Disable();
        car.Enable();
    }
}

basic이 아닌 car로써 작동하게 된다.

 

5. C#제너레이트 방식

 

강사님 피셜 가장 진화된 InputSystem이라고 한다.

실제로도 그래 보이는게

 

작성된 InputSystem을 눌러보면 Generate C# class라는 체크박스가 있다.

 

누르고 Apply 버튼을 누른다면 InputSystem과 같은 이름의 스크립트가 하나 생성되는데

 

이게 뭐람

요약하자면 자동으로 해당 바인딩에 맞는 스크립트를 작성해준다.

 

TTE

관련글 더보기