728x90
반응형

목차

    서론

    Chrono Wave를 기획 및 설계가 어느정도 되었기에 전부터 궁금했던 Unity New Input System을 사용해보고자 한다.

    물론 ChronoWave에 적용할 예정

     

    아래 영상을 따라서 해보자

    https://www.youtube.com/watch?v=Yjee_e4fICc&t=10s 

     

    Installation

    Windows - Package Manager - Unity Registry - Input System 설치

     

    설치가 끝나면 경고창이 하나 뜬다

    New Input System을 사용하기 위해서는 Editor을 재시작해야 한다고 한다.

    Yes를 눌러주자

     

    또는 No를 누르고 Edit - Project Settings - Player - Other Settings - Active Input Handling을 바꿔줘도 된다.

    Both로 설정하는 것이 편하다고 한다.

     

    Input Action Asset

    적당한 폴더에 Input Action을 만든다.

    PlayerInputActions라고 이름을 설정하고 더블클릭하면 팝업창이 뜬다. (대충 InputAction Manager 정도?)

     

    Action Map

    Action Maps은 Action들의 집합이다.

    예를 들어 기본적인 움직임, 차량 탑승이 가능한 플레이어를 만든다고 했을 때

    Movement라는 Action Map, Vehicle이라는 Action Map, 그리고 UI라는 Action Map을 만들어 분리해둘 수 있다고 한다.

    Action

    말 그래도 어떤 동작/행동이다. 가장 간단하게 Move, Jump, Shoot 등을 예시로 들 수 있다.

    영상에 따라 Jump라는 Action을 추가했다.

     

    Properties

    Action을 추가하면 오른쪽에 Properties가 생긴다.

    Action Type

    Action Type은 Value, Button, Pass Through 3가지가 있다.

    • Value: movement를 조작하는 joystick 등 연속적인 값
    • Button: press와 release가 있는 단순 버튼
    • Pass Through: Value와 비슷하지만 disambiguation process를 하지 않는다고 한다. 자세한 설명은 패스...

    Jump는 Button Action Type으로 설정한다.

    Interactions

    Interactions는 좀 더 복잡한 동작들을 쉽게 설정할 수 있도록 한다.

    Hold, Multi Tap, Press, Slow Tap, Tap 등 여러가지 동작들을 설정할 수 있다.

    다만 기본적인 Button에는 딱히 설정할 필요가 없다고 한다.

    Processors

    Processors는 입력값에 대해 계산이나 처리를 해준 뒤에 그 값을 return해준다.

    Axis Deadzone, Clamp, Invert, Normalize, Scale 등이 가능하다.

    마찬가지로 기본적인 Button에는 딱히 설정할 필요가 없다.

     

    Action Bindings

    다시 Actions로 돌아와서 Jump를 우클릭하면 Jump에 대한 Binding을 추가할 수 있다.

     

    <No Binding>을 클릭하면 Binding Properties가 나오고 어떤 입력을 사용할지 설정할 수 있다.

    또한 Interactions과 Processors도 사용가능하다.

     

    Binding의 Path가 바로 어떤 입력을 사용할지 설정하는 것이다.

     

    하나하나 찾는 것 보다는 (특히 키보드라면) 검색 옆에 Listen을 누른 뒤 원하는 키를 입력하는 것이 편하다.

     

    이제 테스트를 위해 저장하고 다시 Scene으로 돌아간다.

    Actions 위에 Save Asset을 누르면 저장된다.

     

    Player Input Component

    테스트를 위해 적당히 Ground와 Player로 사용될 Sphere를 배치하였다.

     

    이 Sphere에 Player Input이라는 Component를 추가한다.

    비어있는 Actions [None (Input Action Asset)]에 방금 만든 PlayerInputActions를 넣어준다.

     

    다른 건 제쳐두고 Behavior가 중요하다.

    Behavior는 Player Input에서 입력을 받았을 때 그것을 전달하는 방식이다.

    • Send Message: 해당 오브젝트에 있는 모든 스크립트에 있는 OnDeviceLost, OnDeviceRegained, OnControlsChaged, On{ActionName} 같은 function를 call한다.
    • Broadcast Message: Send Message와 비슷하지만 해당 오브젝트의 child에 있는 function도 call한다.
    • Invoke Unity Events: UI 등에서 사용하던 Event를 사용한다.

     

    Invoke Unity Events

    Invoke Unity Events를 사용하기 위해 Sphere에 C# Script를 하나 만들어주자.

    InputSystemTest라는 Script를 만들어 넣어주었다.

     

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class InputSystemTest : MonoBehaviour
    {
        public void Jump()
        {
            Debug.Log("Jump");
        }
    }

    확인을 위한 Jump Function과 Debug.Log를 추가했다.

     

    Events - Player - Jump(CallbackContext)에 Event를 추가하고 Jump Funtion을 할당한다.

     

    Log가 왜 3개 찍히는지는 모르겠다...

     

    Input이 들어오는 것을 확인했으니 Rigidbody를 이용해 Jump를 하도록 해주자.

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class InputSystemTest : MonoBehaviour
    {
        Rigidbody rb;
    
        private void Awake()
        {
            rb = GetComponent<Rigidbody>();
        }
        public void Jump()
        {
            Debug.Log("Jump");
            rb.AddForce(Vector3.up * 5f, ForceMode.Impulse);
        }
    }

     

    Input System Phases

    분명 Space를 한번만 눌렀는데 Log는 3번 찍히는 것을 확인할 수 있었다.

    첫번째 Log는 Button pressed

    두번째 Log는 Button pressing

    세번째 Log는 Button released

    해서 Log가 3개가 찍힌다고 한다.

     

    이것을 확인하기 위해 InputSystemTest.cs의 Jump Function을 수정한다.

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.InputSystem;
    
    public class InputSystemTest : MonoBehaviour
    {
        Rigidbody rb;
    
        private void Awake()
        {
            rb = GetComponent<Rigidbody>();
        }
        public void Jump(InputAction.CallbackContext context)
        {
            Debug.Log("Jump " + context.phase);
            rb.AddForce(Vector3.up * 5f, ForceMode.Impulse);
        }
    }

     

    Function의 정보가 바뀌었으니 Event에서 다시 할당해주어야 한다.

    아까와 달리 Jump가 Dynamic CallbackContext에 있는 것을 확인할 수 있다.

     

    이제 다시 실행해서 Log을 확인해보면 각각 Started, Preformed, Canceled라는 Phase가 보인다.

     

    만약 특정 phase에서만 실행하고 싶다면 다음과 같은 조건문을 추가하면 된다.

        public void Jump(InputAction.CallbackContext context)
        {
            if(context.performed)
            {
                Debug.Log("Jump " + context.phase);
                rb.AddForce(Vector3.up * 5f, ForceMode.Impulse);
            }
        }

     

    Invoke C# Events

    C# Events는 Unity Events를 Script에서 작성한다고 생각하면 된다.

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.InputSystem;
    
    public class InputSystemTest : MonoBehaviour
    {
        Rigidbody rb;
        PlayerInput playerInput;
    
        private void Awake()
        {
            rb = GetComponent<Rigidbody>();
            playerInput= GetComponent<PlayerInput>();
    
            playerInput.onActionTriggered += PlayerInput_onActionTriggered;
        }
    
        private void PlayerInput_onActionTriggered(InputAction.CallbackContext context)
        {
            Debug.Log(context);
        }
    
        public void Jump(InputAction.CallbackContext context)
        {
            if(context.performed)
            {
                Debug.Log("Jump " + context.phase);
                rb.AddForce(Vector3.up * 5f, ForceMode.Impulse);
            }
        }
    }

    onActionTriggered는 모든 Action Map의 모든 Action에 대해 call된다.

    확인을 위해 context를 Log로 찍어보자.

     

    action = {ActionMap}/{Action}[{Path}]

    phase = 

    time = (phase 시작 시작)

    ...

    등이 나온다.

     

    이걸로 사용하려면 마치 옛날 Input처럼 수많은 조건문을 사용해야할 것 같다.

    그냥 Unity Events나 사용하자.

     

    Interactions

    아까 잠깐 찾아봤었던 Interactions에 대해 영상에서 간단히 설명해준다.

    Interactions 중 Hold를 추가해보자

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.InputSystem;
    
    public class InputSystemTest : MonoBehaviour
    {
        Rigidbody rb;
        PlayerInput playerInput;
    
        private void Awake()
        {
            rb = GetComponent<Rigidbody>();
            playerInput= GetComponent<PlayerInput>();
        }
    
        public void Jump(InputAction.CallbackContext context)
        {
            Debug.Log(context);
            if(context.performed)
            {
                Debug.Log("Jump " + context.phase);
                rb.AddForce(Vector3.up * 5f, ForceMode.Impulse);
            }
        }
    }

    코드를 위와 같이 다시 수정한 뒤 Space를 눌러보면 phase가 Started와 Canceled 밖에 뜨지 않는다.

    기본 설정으로 Hold는 0.5s 동안 누르고 있어야 하므로 Space를 누르고 0.5s가 지나면 Jump한다.

     

    Interaction은 여러개 할당 할 수 있으며 아래와 같이 조건문을 사용하면 된다.

        public void Jump(InputAction.CallbackContext context)
        {
            if(context.performed)
            {
                if(context.interaction is MultiTapInteraction)
                {
                    Debug.Log("Jump Double Tap");
                }
                else if(context.interaction is HoldInteraction)
                {
                    Debug.Log("Jump Hold");
                }
            }
        }

     

     

    Generate C# Class

    Project에 만들어두었단 PlayerInputActions를 클릭하고 Generate C# Class를 toggle한다.

    일단 Default로 두고 Apply를 눌러보자

     

    그러면 C# Class File에 적힌 Path에 PlayerInputActions.cs가 생성된다.

     

    Sphere에 있는 Player Input Component를 제거하고

    InputSystemTest.cs를 수정한다.

     

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.InputSystem;
    using UnityEngine.InputSystem.Interactions;
    
    public class InputSystemTest : MonoBehaviour
    {
        Rigidbody rb;
        PlayerInput playerInput;
    
        private void Awake()
        {
            rb = GetComponent<Rigidbody>();
            playerInput= GetComponent<PlayerInput>();
    
            PlayerInputActions playerInputActions = new PlayerInputActions();
            playerInputActions.Player.Jump.performed += Jump;
        }
    
        public void Jump(InputAction.CallbackContext context)
        {
            if(context.performed)
            {
                if(context.interaction is MultiTapInteraction)
                {
                    Debug.Log("Jump Double Tap");
                }
                else if(context.interaction is HoldInteraction)
                {
                    Debug.Log("Jump Hold");
                }
            }
        }
    }

     

    동작이 안되는 걸 확인할 수 있다.

    이건 기본적으로 InputAction이 disable되어있기 때문이다.

     

    다시 Script를 수정한다.

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.InputSystem;
    using UnityEngine.InputSystem.Interactions;
    
    public class InputSystemTest : MonoBehaviour
    {
        Rigidbody rb;
        PlayerInput playerInput;
    
        private void Awake()
        {
            rb = GetComponent<Rigidbody>();
            playerInput= GetComponent<PlayerInput>();
    
            PlayerInputActions playerInputActions = new PlayerInputActions();
            playerInputActions.Player.Enable();
            playerInputActions.Player.Jump.performed += Jump;
        }
    
        public void Jump(InputAction.CallbackContext context)
        {
            if(context.performed)
            {
                if(context.interaction is MultiTapInteraction)
                {
                    Debug.Log("Jump Double Tap");
                }
                else if(context.interaction is HoldInteraction)
                {
                    Debug.Log("Jump Hold");
                }
            }
        }
    }

     

    playerInputActions.Enable()을 사용하면 모든 ActionMap를 enable하고 위와 같이 ActionMap을 쓰고 enable할 수도 있다.

     

    Movement Action(Value)

    value는 연속적인 값을 받는데 사용된다.

    다시 PlayerInputActions을 열고 Action에 Movement을 추가한다.

    Action Type은 Value, Control Type은 Vector 2로 설정한다.

     

    그리고 기본으로 있는 <No Binding>을 삭제하고 새로운 Binding을 만든다.

    그리고 각각 WASD를 할당해준다.

     

    InputSystemTest.cs를 아래와 같이 수정한다.

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.InputSystem;
    using UnityEngine.InputSystem.Interactions;
    
    public class InputSystemTest : MonoBehaviour
    {
        Rigidbody rb;
        PlayerInput playerInput;
    
        private void Awake()
        {
            rb = GetComponent<Rigidbody>();
            playerInput = GetComponent<PlayerInput>();
    
            PlayerInputActions playerInputActions = new PlayerInputActions();
    
            playerInputActions.Player.Enable();
            playerInputActions.Player.Jump.performed += Jump;
    
            playerInputActions.Player.Movement.performed += Movement;
        }
    
        public void Movement(InputAction.CallbackContext context)
        {
            Debug.Log(context);
        }
        public void Jump(InputAction.CallbackContext context)
        {
            Debug.Log(context);
            if(context.performed)
            {
                if(context.interaction is TapInteraction)
                {
                    Debug.Log("Jump Tap");
                    rb.AddForce(Vector3.up * 5f, ForceMode.Impulse);
                }
                else if(context.interaction is MultiTapInteraction)
                {
                    Debug.Log("Jump Double Tap");
                }
                else if(context.interaction is HoldInteraction)
                {
                    Debug.Log("Jump Hold");
                    rb.AddForce(Vector3.up * 10f, ForceMode.Impulse);
                }
            }
        }
    }

     

    Log을 확인하면 WASD가 찍히는 것을 확인할 수 있다.

     

    또한 value가 2D Vector 형태로 나오는 것도 확인할 수 있다.

     

    이걸 rigidbody을 이용하여 Sphere을 움직이게 해보자

        public void Movement(InputAction.CallbackContext context)
        {
            Debug.Log(context);
            Vector2 inputVector = context.ReadValue<Vector2>();
            rb.AddForce(new Vector3(inputVector.x, 0, inputVector.y) * 10f, ForceMode.Force);
        }

    context.ReadValue<{Type}>()으로 해당 Type을 읽어올 수 있다.

     

    Sphere가 움직이기는 하지만 performed일 때 한번만 입력되는 것을 확인할 수 있다.

     

    이걸 해결하기 위해서는 ReadValue를 performed가 아니라 Update에서 실행하면 된다.

        private void FixedUpdate()
        {
           Vector2 inputVector = playerInputActions.Player.Movement.ReadValue<Vector2>();
           rb.AddForce(new Vector3(inputVector.x, 0, inputVector.y) * 5f, ForceMode.Force);
        }

    rigidbody를 조작하는 것이기 때문에 FixedUpdate에서 실행해준다.

     

    여기까지의 InputSystemTest.cs는 아래와 같다.

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.InputSystem;
    using UnityEngine.InputSystem.Interactions;
    
    public class InputSystemTest : MonoBehaviour
    {
        Rigidbody rb;
        PlayerInput playerInput;
        PlayerInputActions playerInputActions;
    
        private void Awake()
        {
            rb = GetComponent<Rigidbody>();
            playerInput = GetComponent<PlayerInput>();
    
            playerInputActions = new PlayerInputActions();
    
            playerInputActions.Player.Enable();
            playerInputActions.Player.Jump.performed += Jump;
        }
    
        private void FixedUpdate()
        {
           Vector2 inputVector = playerInputActions.Player.Movement.ReadValue<Vector2>();
           rb.AddForce(new Vector3(inputVector.x, 0, inputVector.y) * 5f, ForceMode.Force);
        }
    
        public void Jump(InputAction.CallbackContext context)
        {
            Debug.Log(context);
            if(context.performed)
            {
                if(context.interaction is TapInteraction)
                {
                    Debug.Log("Jump Tap");
                    rb.AddForce(Vector3.up * 5f, ForceMode.Impulse);
                }
                else if(context.interaction is MultiTapInteraction)
                {
                    Debug.Log("Jump Double Tap");
                }
                else if(context.interaction is HoldInteraction)
                {
                    Debug.Log("Jump Hold");
                    rb.AddForce(Vector3.up * 10f, ForceMode.Impulse);
                }
            }
        }
    }

     

    Multiple Input Types (Control Schemes)

    이제 키보드랑 게임패드 같이 다양한 기기로부터 입력을 받아보자.

    PlayerInputAcitions를 열고 Action Maps 위에 No Control Schemes - Add Control Scheme를 클릭한다.

     

    그리고 이름을 지정한 뒤 Keyboard를 List에 추가하고 Save한다.

     

    그리곤 지금까지 추가한 Binding을 Keyboard Scheme으로 설정한다.

     

    같은 방식으로 Gamepad Scheme을 추가한다.

    Gamepad는 종류가 많지만 특정하지 않고 그냥 Gamepad를 List에 추가한다.

     

    그러면 Binding들이 사라져있다.

     

    테스트를 위해 집에 굴러다니는 Xbox Controller를 주워왔다.

    연결하면 Log에 뜬다.

     

    똑같이 Binding을 Listen으로 추가하려는데 되지 않는다.

    이건 Save Asset - Play - Stop하고 다시 하면 된다고 한다.

     

    또한 Windows - Analysis - Input Debugger에서 지원되는 Device를 확인할 수 있다.

     

    이제 Binding을 해보자

    Xbox Controller의 B 버튼을 눌렀더니 두개가 뜬다.

    Button East로 하면 모든 Gamepad의 오른쪽 버튼으로

    B으로 하면 Xbox Controller의 B로만 동작한다.

     

    Keyboard에서는 WASD를 각각 할당해주어야 했지만 Gamepad는 Stick하나로 해결된다.

    Movement에는 Add Binding을 한 뒤 Listen으로 joystick을 할당 할 수 있다.

     

    이것이 바로 New Input System의 장점이라고 한다.

    따로 Script를 수정하지 않아도 다양한 기기의 입력으로 같은 동작을 할 수 있다.

     

    실행해보면 Keyboard와 Gamepad 모두 동작한다.

     

    Stick Deadzone Processor

    Processor나 Interactions은 Action에 추가해서 모든 Control Schemes에 적용할 수도 있지만

    Binding에 추가해서 해당 Binding에서만, 즉 특정 Control Scheme에서만 적용되도록 할 수 있다.

     

     

    Gamepad의 Movement Action에 할당된 Left Stick Binding에 Stick Deadzone Process을 추가한다.

    Stick Deadzone은 물리적으로 JoyStick이 매우 작은 값을 가질 때 동작하는 것을 방지할 수 있다.

     

    Gamepad를 사용하는 게임이라면 필수적이겠지만 지금은 딱히 쓸 데가 없는 것 같다.

     

    Pass Through, Disambiguation

    Pass Through는 Value와 비슷한 Action Type이지만 disambiguation이라는 과정을 하지 않는다고 잠깐 설명했었다.

    여기서 자세한 설명이 나온다.

    the process which is the input system decides which input is active

    다수의 Gamepad를 연결했을 때 Value를 사용한다면 Active상태인 Gamepad의 입력만 받지만 Pass Through를 사용한다면 모든 Gamepad에서 입력을 받을 수 있다고 한다.

     

    근데 Pass Through로 하면 다수의 Gamepad를 연결했을 때 입력이 교차되는 문제가 있는 것 같은데

    이걸 사용하는 이유가 있을까...

     

    Default Create Input Actions

    위의 과정이 다 귀찮다면 Default Input Actions을 사용할 수 있다고 한다.

    다시 Sphere에 Player Input Component를 추가하고 Create Actions...을 누른다.

     

    기본적으로 FPS나 TPS인 듯 하다.
    Gamepad, Joystick, Keyboard&Mouse, Touch, XR이 Control Scheme으로 있다.

    딱히 쓸모는 없는 것 같다. 그냥 이런 기능이 있다 정도?

     

    Compact Input Test

    좀 더 쉽게 입력을 테스트하는 방법이 있다고 한다.

    Old Input System과 비슷한 방법이다.

     

            if(Mouse.current.leftButton.wasPressedThisFrame)
            {
    
            }
            if(Gamepad.current.aButton.wasPressedThisFrame)
            {
    
            }
            if(Keyboard.current.wKey.wasPressedThisFrame)
            {
    
            }

    {Control Scheme}.current.{ButtonName}.wasPressedThisFrame

    {Control Scheme}.current.{ButtonName}.wasReleasedThisFrame

     

    정말 급하게 해야하거나 생각하기 귀찮을 때 사용하면 될 것 같은 방법이다.

     

    Change Action Map

    Default Action Map을 만들었을 때 Player와 UI, 2개의 Action Map이 있었다.

    이걸 바꾸는 방법인 것 같다.

     

    PlayerInputActions를 열고 UI라는 새로운 Action Map을 추가한다.

    그리고 Submit이라는 Action을 만든다.

    테스트를 위해 Player Action Map에 Binding되어있는 Key인 Space를 할당해보자.

     

    만약 Player Input Component의 Unity Event을 사용하고 있다면 Inspector에 나뉘어 있는 것을 볼 수 있다.

     

    저번에 했던 것과 같이 함수를 만들고 추가해주면 된다.

     

    그리고 Action Map을 바꾸는 방법은 간단하다.

    Player Input Component을 가져온 다음 SwitchCurrentActionMap을 사용하면 된다.

        private void Update()
        {
            if(Keyboard.current.tKey.wasPressedThisFrame)
            {
                playerInput.SwitchCurrentActionMap("UI");
            }
            if(Keyboard.current.yKey.wasPressedThisFrame)
            {
                playerInput.SwitchCurrentActionMap("Player");
            }
        }

    t를 누르면 ActionMap을 UI로, y를 누르면 ActionMap을 Player로 바꾼다.

     

    현재 Script는 아래와 같다.

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.InputSystem;
    using UnityEngine.InputSystem.Interactions;
    
    public class InputSystemTest : MonoBehaviour
    {
        Rigidbody rb;
        PlayerInput playerInput;
        PlayerInputActions playerInputActions;
    
        private void Awake()
        {
            rb = GetComponent<Rigidbody>();
            playerInput = GetComponent<PlayerInput>();
    
            playerInputActions = new PlayerInputActions();
    
            playerInputActions.Player.Enable();
            //playerInputActions.Player.Jump.performed += Jump;
        }
    
        private void Update()
        {
            if(Keyboard.current.tKey.wasPressedThisFrame)
            {
                playerInput.SwitchCurrentActionMap("UI");
            }
            if(Keyboard.current.yKey.wasPressedThisFrame)
            {
                playerInput.SwitchCurrentActionMap("Player");
            }
        }
    
        private void FixedUpdate()
        {
           Vector2 inputVector = playerInputActions.Player.Movement.ReadValue<Vector2>();
           rb.AddForce(new Vector3(inputVector.x, 0, inputVector.y) * 5f, ForceMode.Force);
        }
    
        public void Jump(InputAction.CallbackContext context)
        {
            Debug.Log(context);
            if(context.performed)
            {
                Debug.Log("Jump " + context.phase);
                rb.AddForce(Vector3.up * 5f, ForceMode.Impulse);
            }
        }
    
        public void Submit(InputAction.CallbackContext context)
        {
            Debug.Log("Submit " + context);
        }
    }

     

    확인해보면 ActionMap이 Player일 때와 UI일 때 Space의 동작이 다른 것을 확인할 수 있다.

    C#으로만 사용하려면 적당히 Enable과 Disable을 하면 된다.

     

    Input Debugger

    저번에 잠깐 나왔던 Input Debugger

    Windows - Analysis - Input Debugger

     

    Input Debugger에서 각 Devices를 더블클릭하면 자세한 내용을 볼 수 있다.

     

    Button Remapping

    New Input System을 사용하려는 가장 큰 이유

    Key Remapping이 간단하다.

     

            playerInputActions.Player.Jump.PerformInteractiveRebinding().Start();

    이렇게 하면 다음에 입력되는 Key를 Jump로 할당한다고 한다.

     

    그러나 이렇게 하면 Error가 하나 뜨는데 Enable 상태에서는 rebind가 불가능 하다고 한다.

     

    Disable을 한 뒤에 callback까지  받아서 Log에 찍어보자.

            playerInputActions.Player.Disable();
            playerInputActions.Player.Jump.PerformInteractiveRebinding()
                .OnComplete(callback =>
                {
                    Debug.Log(callback);
                })
                .Start();

     영상에서는 Memory Leak 관련 Error가 떴지만 나는 뜨지 않았다.

    일단 혹시모르니 따라해본다.

     

    또한 Enable를 다시 해준다.

            playerInputActions.Player.Disable();
            playerInputActions.Player.Jump.PerformInteractiveRebinding()
                .OnComplete(callback =>
                {
                    Debug.Log(callback);
                    callback.Dispose();
                    playerInputActions.Player.Enable();
                })
                .Start();
        }

     

    플레이하고 누르는 키가 Jump로 Rebind되고 Space를 눌러도 동작하지 않는다.

    또 .WithControlsExcluding을 사용해서 Rebing하는 동안 그 입력은 받지 않도록 할 수도 있다.

                .WithControlsExcluding("Mouse")

     

    하지만 이 상태로는 다시 Play했을 경우 저장되지 않는다.

    PlayerPrefs에 JSON으로 Save/Load 하면 된다고 한다.

     

    그래서 대충 만든 Script는 아래와 같다.

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.InputSystem;
    using UnityEngine.InputSystem.Interactions;
    
    public class InputSystemTest : MonoBehaviour
    {
        Rigidbody rb;
        PlayerInput playerInput;
        PlayerInputActions playerInputActions;
    
        private void Awake()
        {
            rb = GetComponent<Rigidbody>();
            playerInput = GetComponent<PlayerInput>();
    
            var rebinds = PlayerPrefs.GetString("rebinds");
            playerInput.actions.LoadBindingOverridesFromJson(rebinds);
            Debug.Log(rebinds);
    
            playerInputActions = new PlayerInputActions();
    
            //playerInputActions.Player.Enable();
            playerInputActions.Player.Jump.performed += Jump;
        }
    
        private void Update()
        {
            if(Keyboard.current.tKey.wasPressedThisFrame)
            {
                playerInput.SwitchCurrentActionMap("UI");
            }
            if(Keyboard.current.yKey.wasPressedThisFrame)
            {
                playerInput.SwitchCurrentActionMap("Player");
            }
            if(Keyboard.current.escapeKey.wasPressedThisFrame)
            {
                playerInputActions.Player.Disable();
                playerInputActions.Player.Jump.PerformInteractiveRebinding()
                    .WithControlsExcluding("Mouse")
                    .OnComplete(callback =>
                    {
                        Debug.Log(callback.action.bindings[0].overridePath);
                        var rebinds = callback.action.SaveBindingOverridesAsJson();
                        PlayerPrefs.SetString("rebinds", rebinds);
                        Debug.Log(rebinds);
                        callback.Dispose();
                        playerInputActions.Player.Enable();
                    })
                    .Start();
            }
        }
    
        private void FixedUpdate()
        {
           Vector2 inputVector = playerInputActions.Player.Movement.ReadValue<Vector2>();
           rb.AddForce(new Vector3(inputVector.x, 0, inputVector.y) * 5f, ForceMode.Force);
        }
    
        public void Jump(InputAction.CallbackContext context)
        {
            Debug.Log(context);
            if(context.performed)
            {
                Debug.Log("Jump " + context.phase);
                rb.AddForce(Vector3.up * 5f, ForceMode.Impulse);
            }
        }
    
        public void Submit(InputAction.CallbackContext context)
        {
            Debug.Log("Submit " + context);
        }
    }

     

    Touch Controls

    간단한 Touch Control를 만들어보자

    UI에 circle을 추가하고 On-Screen Stick Component를 추가한다.

    그리고 Control Path를 Left Stick으로 해주면 이 UI는 Controller의 Left Stick과 같은 입력을 준다.

     

    비슷한 방식으로 Image를 하나 추가하고 On-Screen Button Component를 추가하고 Control Path를 Button East로 해준다.

     

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.InputSystem;
    using UnityEngine.InputSystem.Interactions;
    
    public class InputSystemTest : MonoBehaviour
    {
        Rigidbody rb;
        PlayerInput playerInput;
        PlayerInputActions playerInputActions;
    
        private void Awake()
        {
            rb = GetComponent<Rigidbody>();
            playerInput = GetComponent<PlayerInput>();
    
            var rebinds = PlayerPrefs.GetString("rebinds");
            playerInput.actions.LoadBindingOverridesFromJson(rebinds);
            Debug.Log(rebinds);
    
            playerInputActions = new PlayerInputActions();
    
            //playerInputActions.Player.Enable();
            playerInputActions.Player.Jump.performed += Jump;
        }
    
        private void Update()
        {
            if(Keyboard.current.tKey.wasPressedThisFrame)
            {
                playerInput.SwitchCurrentActionMap("UI");
            }
            if(Keyboard.current.yKey.wasPressedThisFrame)
            {
                playerInput.SwitchCurrentActionMap("Player");
            }
            if(Keyboard.current.escapeKey.wasPressedThisFrame)
            {
                playerInputActions.Player.Disable();
                playerInputActions.Player.Jump.PerformInteractiveRebinding()
                    .WithControlsExcluding("Mouse")
                    .OnComplete(callback =>
                    {
                        Debug.Log(callback.action.bindings[0].overridePath);
                        var rebinds = callback.action.SaveBindingOverridesAsJson();
                        PlayerPrefs.SetString("rebinds", rebinds);
                        Debug.Log(rebinds);
                        callback.Dispose();
                        playerInputActions.Player.Enable();
                    })
                    .Start();
            }
        }
    
        private void FixedUpdate()
        {
        }
    
        public void Jump(InputAction.CallbackContext context)
        {
            Debug.Log(context);
            if(context.performed)
            {
                Debug.Log("Jump " + context.phase);
                rb.AddForce(Vector3.up * 5f, ForceMode.Impulse);
            }
        }
    
        public void Movement(InputAction.CallbackContext context)
        {
            Vector2 inputVector = context.ReadValue<Vector2>();
            rb.AddForce(new Vector3(inputVector.x, 0, inputVector.y) * 5f, ForceMode.Force);
        }
    
        public void Submit(InputAction.CallbackContext context)
        {
            Debug.Log("Submit " + context);
        }
    }