有兩種方式回應Action
Polling(輪詢):Polling方法是不斷的檢查你感興趣Action目前的狀態,通常會在MonoBehaviour
script中的Update()
方法去做輪詢。
Event-driven(事件驅動):Event-driven則是建立要執行的方法,當Action執行時,就會自動呼叫並執行。
Polling 在大部分的場景,特別是動作遊戲,使用者的輸入必須平滑順暢的控制遊戲中的角色,使用Polling
通常是比較容易去實作。
使用InputAction提供的ReadValue<>()
便可以取得目前Action的值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 using UnityEngine;using UnityEngine.InputSystem;public class Example : MonoBehaviour { InputAction moveAction; private void Start () { moveAction = InputSystem.actions.FindAction("Move" ); } void Update () { Vector2 moveValue = moveAction.ReadValue<Vector2>(); } }
以下程式碼使用了預設的Interact Action,它包含一個「按住」(Hold)交互(interaction),使得只有綁定的控件被按住一段時間 (例如 0.4 秒) 後才會執行該動作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 using UnityEngine;using UnityEngine.InputSystem;public class Example : MonoBehaviour { InputAction interactAction; private void Start () { interactAction = InputSystem.actions.FindAction("Interact" ); } void Update () { if (interactAction.WasPerformedThisFrame()) { } if (interactAction.WasCompletedThisFrame()) { } } }
以下方法可以用來輪詢按鈕是否被按下或是釋放
以下範例有三個Actions,分別為Shield
,Teleport
與Submit
(它們不是預設的action)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 using UnityEngine;using UnityEngine.InputSystem;public class Example : MonoBehaviour { InputAction shieldAction; InputAction teleportAction; InputAction submitAction; private void Start () { shieldAction = InputSystem.actions.FindAction("Shield" ); teleportAction = InputSystem.actions.FindAction("Teleport" ); submitAction = InputSystem.actions.FindAction("Submit" ); } void Update () { if (shieldAction.IsPressed()) { if (teleportAction.WasPressedThisFrame()) { } if (submit.WasReleasedThisFrame()) { } } }
Event-driven 可以為Action註冊一些callback,當某些輸入發生時,Action就會通知你,讓你可以做出相應的回應。 有以下方式可以使用callback
透過PlayerInput Component 註冊callback
使用Action
的started ,performed 與canceled callback
使用ActionMap
的actionTriggered callback
使用Input System
的全域InputSystem.onActionChange callback
InputActionTrace 可以記錄Actions上的改變
使用PlayerInput component
可以在inspector
中直接設定callback。此外也可以透過程式碼來設定
Phase(階段)
描述
Disabled
此Action已停用且無法接收輸入
Waiting
此Action已啟用且等待輸入
Started
Input System已接收到輸入並開始與Action互動
Performed
與Action的互動已經完成
Canceled
與Action的互動已經被取消
Action callbacks 可以透過 InputAction.phase
來取得目前的階段,Started,Performed與Canceled階段各有與之關聯的callback函數
1 2 3 4 5 var action = new InputAction();action.started += context => {}; action.performed += context => {}; action.canceled += context => {};
每個callback都會接收一個InputAction.CallbackContext
結構,該結構包含上下文信息,您可以利用它來查詢Action的當前狀態,以及讀取觸發該動作的裝置控制的值。
注意:此結構的內容只在callback期間中有效,請勿把它暫存並在callback之外使用 。
callback函數的觸發時機和方式取決於綁定中存在的交互 (Interactions )。如果綁定沒有適用的交互,則會應用預設的交互(Default Interaction )。
除了監聽個別Action,你也可以使用InputActionMap.actionTriggered
監聽該Map中的全部Action,使用這種方式,該單一個callback會收到started,performed與canceled,且其InputAction.CallbackContext
結構與使用個別started,performed與canceled的結構相同。
1 2 3 4 5 6 var actionMap = new InputActionMap();actionMap.AddAction("action1" , "<Gamepad>/buttonSouth" ); actionMap.AddAction("action2" , "<Gamepad>/buttonNorth" ); actionMap.actionTriggered += context => { ... };
InputSystem.onActionChange
與InputSystem.onDeviceChange
類似,讓你可以全域(globally)監聽任何與Action相關的變更
1 2 3 4 5 6 7 8 9 10 11 12 13 InputSystem.onActionChange += (obj, change) => { switch (change) { case InputActionChange.ActionStarted: case InputActionChange.ActionPerformed: case InputActionChange.ActionCanceled: Debug.Log($"{((InputAction)obj).name} {change} " ); break ; } }
,你可以使用InputActionTrace 為某些特定Action集合建立日誌(log),讓你可以追蹤它們。
注意:InputActionTrace會使用不受管理的記憶體,因此在使用後要去處理(disposed)它,避免造成記憶體洩漏(memory leak)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 var trace = new InputActionTrace();trace.SubscribeTo(myAction); trace.SubscribeTo(myActionMap); trace.SubscribeToAll(); myAction.performed += ctx => { if (ctx.ReadValue<float >() > 0.5f ) trace.RecordAction(ctx); }; Debug.Log(string .Join(",\n" , trace)); foreach (var record in trace ){ Debug.Log($"{record .action} was {record .phase} by control {record .control} " ); Debug.Log("Value: " + record .ReadValue<float >()); Debug.Log("Value: " + record .ReadValueAsObject()); } trace.Clear(); trace.UnsubscribeFromAll(); trace.Dispose();
一旦紀錄完成,只要不同時進行寫入操作,並且主線程上不會同時變更Action設置,則trace可以安全地從多個線程讀取。
Action types Action有三種類型,這些類型會影響Input System如何處理操作Action的狀態。
Action Type
描述
Value
預設的Action Type。適用於任何需要追踪控制項(Control)狀態連續變化的輸入。Value
action Type 會持續監控所有綁定到該動作(Action)的控制項(Control),然後選擇最活躍(actuated)的控制項作為驅動該動作的控制項,並在值發生變化時觸發回調函式(callback)報告該控制項的值。如果另一個綁定控制項的活躍程度更高,那麼該控制項就會成為驅動動作的控制項,動作會開始報告該控制項的值。這個過程稱為 衝突解決 (conflict resolution)。如果您希望允許遊戲中不同的控制項控制一個動作,但僅同時從一個控制項接收輸入,那麼這將非常有用。當動作(action)首次啟用時,它會對所有綁定控制項(Control)執行 初始狀態檢查(initial state check)。如果任何一個控制項被激活,則動作會觸發一個回調,傳遞當前值。
Button
Button
類型非常類似於Value
類型,但有以下幾點不同:Button
類型的動作只能綁定到「按鈕控制項」(ButtonControl)。與Value
類型的動作不同,Button
類型的動作不會執行初始狀態檢查。「按鈕」類型的動作適用於每次按下時觸發一次操作的輸入。在這種情況下,初始狀態檢查通常沒有用,因為它可能會在啟用動作時,因之前按住按鈕而觸發動作 (即按鈕仍然處於按下狀態)。
PassThrough
PassThrough
類型的動作與上面描述的Value
動作不同,它繞過了衝突解決。PassThrough
類型的動作不會選擇一個特定的控制項作為驅動動作的來源。
相反,任何綁定的控制項會變更都會觸發一個回調,並將該控制項的目前值傳遞給回調函數。PassThrough
類型的動作適用於您想要處理來自一組控制項的所有輸入的情況。
使用Input Debugger ,可以觀察目前啟用的Action以及與他們綁定的Control。 也可以使用InputActionVisualizer 在螢幕上即時可視化動作的值和交互狀態。
上一篇:Actions概念
Reference: https://docs.unity3d.com/Packages/com.unity.inputsystem@1.8/manual/RespondingToActions.html