Action
関連ページ:
Input Action は、入力の論理的意味を、入力を生成する物理的手段 (つまり、入力デバイス上のアクティビティ) から切り離すように設計されています。そのため、以下のような入力コードを書く必要がありません。
var look = new Vector2();
var gamepad = Gamepad.current;
if (gamepad != null)
look = gamepad.rightStick.ReadValue();
var mouse = Mouse.current;
if (mouse != null)
look = mouse.delta.ReadValue();
代わりに、以下のように入力元のデバイスに依存しないコードを記述できます。
myControls.gameplay.look.performed +=
context => look = context.ReadValue<Vector2>();
マッピングの設定には、視覚的なエディターを使用できます。
このため、プレイヤーが ランタイムにバインディングをカスタマイズ することも容易になります。
ノート:
- Action はゲーム時専用の機能です。
EditorWindow
のコードで使用することはできません。- このページで使用されている用語の要約については、用語と概念 を参照してください。
概要
API には、Action に関連する主要なクラスが 3 つあります。
クラス | 説明 |
---|---|
InputActionAsset |
1 つ以上の Action Map と、オプションとして一連 Control Scheme を含むアセット。これらのアセットの作成、編集、操作を行う方法の詳細については、Action アセット を参照してください。 |
InputActionMap |
Action の名前付きコレクション。 |
InputAction |
入力への応答としてコールバックをトリガーする名前付き Action。 |
Action では、InputBinding
を使用して、収集する入力を参照します。Bindingとその使用方法の詳細については、Action Binding を参照してください。
各 Action には名前 (InputAction.name
) があります。Action が Action Map に属している場合 (InputAction.actionMap
を参照)、名前は、その Action Map の中で一意でなければなりません。また、各 Action には一意の ID (InputAction.id
) があり、これを使用して、その Action を参照することができます。ID は、Action の名前を変更しても変わりません。
各 Action Map には名前 (InputActionMap.name
) があります。Action Map が Action アセットに属している場合 (InputActionMap.asset
を参照)、名前は、そのアセットの中で一意でなければなりません。また、各 Action Map には一意の ID (InputActionMap.id
) があり、これを使用して、その Action Map を参照することができます。ID は、Action Map の名前を変更しても変わりません。
Action の作成
Action は、以下のいずれかの方法で作成できます。
.inputactions
アセット専用のエディターを使用する。- MonoBehaviour コンポーネントに埋め込む。
- 手動で JSON からロードする。
- コードで直接作成する。
Action エディターの使用
専用エディターで Input Action アセットの作成と編集を行う方法については、Action アセット を参照してください。
MonoBehaviour への Action の埋め込み
InputAction
と InputActionMap
は、MonoBehaviour
コンポーネント内にフィールドとして直接埋め込むことができます。
public MyBehavior :MonoBehaviour
{
public InputAction fireAction;
public InputAction lookAction;
public InputActionMap gameplayActions;
}
Unity エディターでは、これらのフィールドにカスタムエディター UI が適用されます。
ビジュアルエディターの動作は Action Asset エディター と同様です。
- Action や Binding の追加または削除を行うには、ヘッダーにある追加 (+) アイコンまたは削除 (-) アイコンをクリックします。
- Binding を編集するには、Binding をダブルクリックします。
- Action を編集するには、Action Map で Action をダブルクリックするか、個々の Action のプロパティに表示される歯車アイコンをクリックします。
- 各エントリーを右クリックすると、コンテキストメニューが表示されます。エントリーをドラッグすることもできます。Alt キーを押しながらエントリーをドラッグすると、エントリーが複製されます。
MonoBehaviour コンポーネントに埋め込まれた Action と Action Map は、手動で 有効化と無効化 を行う必要があります。
public class MyBehavior :MonoBehaviour
{
// ...
void Awake()
{
fireAction.performed += OnFire;
lookAction.performed += OnLook;
gameplayActions["fire"].performed += OnFire;
}
void OnEnable()
{
fireAction.Enable();
lookAction.Enable();
gameplayActions.Enable();
}
void OnDisable()
{
fireAction.Disable();
lookAction.Disable();
gameplayActions.Disable();
}
}
JSON からの Action のロード
Action は、JSON からの Action Map のセット、または InputActionAsset
全体としてロードできます。これは、プレイヤーでのランタイムにも機能します。
//JSON からアクションマップのセットをロードします。
var maps = InputActionMap.FromJson(json);
//JSON から InputActionAsset 全体をロードします。
var asset = InputActionAsset.FromJson(json);
コードでの Action の作成
Action は、手動で作成して設定することができます。これは、プレイヤーでのランタイムにも機能します。
//独立した Action を作成します。
var lookAction = new InputAction("look", binding:"<Gamepad>/leftStick");
var moveAction = new InputAction("move", binding:"<Gamepad>/rightStick");
lookAction.AddBinding("<Mouse>/delta");
moveAction.AddCompositeBinding("Dpad")
.With("Up", "<Keyboard>/w")
.With("Down", "<Keyboard>/s")
.With("Left", "<Keyboard>/a")
.With("Right", "<Keyboard>/d");
//Action を含む Action Map を作成します。
var map = new InputActionMap("Gameplay");
var lookAction = map.AddAction("look");
lookAction.AddBinding("<Gamepad>/leftStick");
//Action アセットを作成します。
var asset = ScriptableObject.CreateInstance<InputActionAsset>();
var gameplayMap = new InputActionMap("gameplay");
asset.AddActionMap(gameplayMap);
var lookAction = gameplayMap.AddAction("look", "<Gamepad>/leftStick");
この方法で再生モード中に作成したアクションはいずれも、再生モードの終了後には Input Action アセットに残りません。このため、アセットを誤って変更することを心配せずに、実環境と同様にエディター内でアプリケーションをテストできます。
デフォルトの Action
Input System パッケージには、Action のデフォルト設定を含む DefaultInputActions.inputactions
という アセット が付属しています。他の Unity アセットと同様に、このアセットをプロジェクト内で直接参照できます。このアセットは、DefaultInputActions
クラスを介してコードで使用することもできます。
void Start()
{
//デフォルトのアクションのインスタンスを作成します。
var actions = new DefaultInputActions();
actions.Player.Look.performed += OnLook;
actions.Player.Move.performed += OnMove;
actions.Enable();
}
Action の使用
Action で何らかの動作を実行するには、まず、Action を有効にする必要があります。これを行うには、Action を個別に有効にする方法と、Action Map を介して一括で有効にする方法があります。どのようなシナリオでも、後者の方がより効率的です。
//1 つのアクションを有効にします。
lookAction.Enable();
//アクションマップ全体を有効にします。
gameplayActions.Enable();
Action を有効にすると、既に解決されている場合を除き、Input System によってそのバインディングが解決されます。つまり、Action で使用できるデバイスセットが変更されていない場合は解決されます。このプロセスの詳細については、Binding の解決 に関するドキュメントを参照してください。
Action Binding などの一部の設定は、Action が有効になっている間は変更できません。Action または Action Map が入力呼び出しに応答することを停止するには、Disable
を呼び出します。
有効になっているAction は、バインド先の Control をアクティブに監視します。バインドされた Control の状態が変更されると、その変更が Action で処理されます。Control の変更が Interaction (Interaction) の変更を表している場合、Action は応答を作成します。これらはすべて、Input System の更新ロジックの中で行われます。この動作は、入力設定で選択されている 更新モード に応じて、フレームごとに 1 回、固定更新ごとに 1 回、または更新が手動に設定されている場合は手動のタイミングで発生します。
Action への応答
Action は、入力への実際の応答を単独で表すわけではありません。Action は、特定のタイプの入力が発生したことをコードに通知します。その後、コードがこの情報に応答します。
これを行う方法は、いくつかあります。
- 各 Action には、
started
、performed
、canceled
コールバック があります。 - 各 Action Map には、
actionTriggered
コールバック があります。 - Input System には、グローバルな
InputSystem.onActionChange
コールバック があります。 - 必要に応じて、いつでも Action の 現在の状態をポーリング できます。
InputActionTrace
では、Action で発生している変更を記録できます。
この他にも、Action から入力を取得する、より高レベルで合理化された方法が 2 つあります。PlayerInput
を使用する方法と、Input Action をラップアラウンドする スクリプトコードを生成 する方法です。
Action のコールバック
すべての Action には、入力を受け取ると通過する、一連の明確なフェーズがあります。
フェーズ | 説明 |
---|---|
Disabled |
Action が無効であり、入力を受け取ることができません。 |
Waiting |
Action が有効であり、入力をアクティブに待機しています。 |
Started |
Input System が、Action との Interaction を開始する入力を受け取りました。 |
Performed |
Action との Interaction が完了しました。 |
Canceled |
Action との Interaction がキャンセルされました。 |
Action の現在のフェーズは、InputAction.phase
を使用して読み取ることができます。
Started
、Performed
、Canceled
の各フェーズにはそれぞれ、コールバックが関連付けられています。
var action = new InputAction();
action.started += ctx =>/*Action が開始されました */;
action.performed += ctx =>/*Action が実行されました */;
action.canceled += ctx =>/*Action がキャンセルされました */;
各コールバックは InputAction.CallbackContext
構造体を受け取ります。この構造体にはコンテキスト情報が含まれ、これを使用して、Action の現在の状態を照会したり、Action をトリガーした Control から値を読み出したり (InputAction.CallbackContext.ReadValue
) することができます。
ノート: 構造体の内容が有効なのは、コールバックの存続期間中のみです。特に、受け取ったコンテキストを保存し、そのプロパティに後でコールバックの外部からアクセスすることは安全ではありません。
コールバックがトリガーされるタイミングと方法は、それぞれの Binding に設定された Interaction によって異なります。Binding に適用される Interaction がない場合は、デフォルトの Interaction が適用されます。
InputActionMap.actionTriggered
コールバック
個々のアクションをリッスンする代わりに、Action Map 全体をリッスンすると、その Action Map 内の任意の Action の状態変更を検知できます。
var actionMap = new InputActionMap();
actionMap.AddAction("action1", "<Gamepad>/buttonSouth");
actionMap.AddAction("action2", "<Gamepad>/buttonNorth");
actionMap.actionTriggered +=
context => { ...};
引数として渡されるのは InputAction.CallbackContext
構造体であり、started
、performed
、canceled
の各コールバック に渡されるものと同じです。
ノート: Input System は、Action の 3 つのコールバックのそれぞれについて
InputActionMap.actionTriggered
を呼び出します。つまり、1 つのコールバックに、started
、performed
、canceled
のすべてが通知されます。
InputSystem.onActionChange
コールバック
InputSystem.onDeviceChange
と同様に、アクション関連の変更をアプリケーションでグローバルにリッスンできます。
InputSystem.onActionChange +=
(obj, change) =>
{
//change に応じて、obj は、InputAction と InputActionMap の
//いずれかになります。
switch (change)
{
case InputActionChange.ActionStarted:
case InputActionChange.ActionPerformed:
case InputActionChange.ActionCanceled:
Debug.Log($"{((InputAction)obj).name} {change}");
break;
}
}
Action のポーリング
コールバックを使用する代わりに、コード内で必要になったときに Action の状態をポーリングする方が簡単である場合があります。
InputAction.ReadValue<>()
を使用すると、Action の現在の値をポーリングできます。
public InputAction moveAction;
public float moveSpeed = 10.0f;
public Vector2 position;
void Start()
{
moveAction.Enable();
}
void Update()
{
var moveDirection = moveAction.ReadValue<Vector2>();
position += moveDirection * moveSpeed * Time.deltaTime;
}
値型は、その値が読み取られるコントロールの値型と一致している必要があります。
アクションが現在のフレームで実行されたかどうかを判別するには、InputAction.WasPerformedThisFrame()
を使用できます。
private InputAction action;
void Start()
{
//A ボタンが 1 秒間押されたときにトリガーされる
//アクションを設定します。
action = new InputAction(
type:InputActionType.Button,
binding:"<Gamepad>/buttonSouth",
interactions:"hold(duration=1)");
action.Enable();
}
void Update()
{
if (action.WasPerformedThisFrame())
Debug.Log("A button on gamepad was held for one second");
}
最後に、ボタンの押下と解放をポーリングするには、以下の 3 つのメソッドを使用できます。
メソッド | 説明 |
---|---|
InputAction.IsPressed() |
アクションの 作動 レベルが pressPoint を超えていて、まだ 解放しきい値 以下になっていない場合は、true を返します。 |
InputAction.WasPressedThisFrame() |
現在のフレーム内の任意の時点で、アクションの 作動 レベルが pressPoint 以上に達した場合は、true を返します。 |
InputAction.WasReleasedThisFrame() |
現在のフレーム内の任意の時点で、アクションの 作動 レベルが pressPoint 以上から 解放しきい値 以下に変化した場合は、true を返します。 |
例を以下に示します。
public PlayerInput playerInput;
public void Update()
{
//IsPressed
if (playerInput.actions["up"].IsPressed())
transform.Translate(0, 10 * Time.deltaTime, 0);
//WasPressedThisFrame
if (playerInput.actions["teleport"].WasPressedThisFrame())
Teleport();
//WasReleasedThisFrame
if (playerInput.actions["submit"].WasReleasedThisFrame())
ConfirmSelection();
}
InputActionTrace
Action をトレースして、特定の Action セットで発生したすべてのアクティビティのログを生成することができます。そのためには、InputActionTrace
を使用します。これは、イベント用の InputEventTrace
と同様に動作します。
ノート:
InputActionTrace
はアンマネージメモリを割り当てるため、破棄して、メモリリークが発生しないようにする必要があります。
var trace = new InputActionTrace();
//トレースを単一の Action にサブスクライブします。
//(サブスクリプションを解除するには UnsubscribeFrom を使用)。
trace.SubscribeTo(myAction);
//トレースを Action Map 全体にサブスクライブします。
//(サブスクリプションを解除するには UnsubscribeFrom を使用)
trace.SubscribeTo(myActionMap);
//システム内のすべての Action にトレースをサブスクライブします。
trace.SubscribeToAll();
//Action が 1 回トリガーされたことを記録します。
myAction.performed +=
ctx =>
{
if (ctx.ReadValue<float>() > 0.5f)
trace.RecordAction(ctx);
};
//トレースをコンソールに出力します。
Debug.Log(string.Join(",\n", trace));
//記録されたすべての Action を処理し、トレースを消去します。
foreach (var record in trace)
{
Debug.Log($"{record.action} was {record.phase} by control {record.control}");
//値を読み出すには、その値型を知っているか、そうでない場合は、汎用バイト
//バッファとして読み出す必要があります。ここでは、値型が float であると
//想定します。
Debug.Log("Value:" + record.ReadValue<float>());
//GC の発生を許容する場合は、値をオブジェクトとして読み出すこともできます。
//この場合、値型を知っている必要はありません。
Debug.Log("Value:" + record.ReadValueAsObject());
}
trace.Clear();
//トレースのサブスリプションをすべて解除します。
trace.UnsubscribeFromAll();
//トレースに割り当てられているメモリを解放します。
trace.Dispose();
いったん記録されたトレースは、同時に書き込みが行われたり、Action 設定 (トレースによってアクセスされる設定データ) がメインスレッドで同時に変更されたりしない限り、複数のスレッドから安全に読み取ることができます。
Action タイプ
各 Action は、3 つの異なる Action タイプ のいずれかに設定できます。Action タイプを設定するには、Input Action エディターウィンドウで選択するか、InputAction()
コンストラクターの呼び出し時に type
パラメーターを指定します。Action タイプは、その Action での Input System による状態変更の処理方法に影響します。デフォルトの Action タイプは Value
です。
Value (値)
デフォルトの Action タイプです。これは、Control の状態に対する継続的な変更を追跡する必要がある入力に使用します。
Value
タイプのアクションは、Action にバインドされたすべての Control を継続的に監視し、作動量が最も大きい Control を Action の駆動元として選択して、その Control の値をコールバックで報告します。コールバックは、値が変更されるたびにトリガーされます。バインドされている別の Control の作動量の方が大きくなると、それが Action を駆動する Control になり、Action はその Control の値を報告し始めます。このプロセスは 競合解決 と呼ばれます。これは、ゲーム内の Action を複数の Control で制御できるようにしつつ、一度に 1 つの Control からのみ入力を受け取る場合に便利です。
Action が最初に有効になったときに、バインドされているすべての Control の 初期状態のチェック が実行されます。いずれかの Control が作動している場合、Action は、その現在の値でコールバックをトリガーします。
Button (ボタン)
Value
と似ていますが、Button
タイプの Action にバインドできるのは、ButtonControl
Control のみです。また、Value
タイプの Action で行われるような初期状態のチェック (上記の Value セクションを参照) は実行されません。これは、ボタンが押されるたびに 1 回ずつ Action をトリガーする入力に使用します。このような場合には通常、初期状態のチェックが有用ではありません。Action が有効になったときに、その前からボタンが押されたままであると、初期状態のチェックによってアクションがトリガーされる可能性があるためです。
Pass-Through (パススルー)
Pass-Through
Action では、上記の Value
Action で説明した 競合解決 のプロセスはバイパスされ、特定の Control が Action を駆動するという概念は使用されません。バインドされているいずれかの Control に何らかの変更があると、その Control の値と共にコールバックがトリガーされます。これは、一連の Control からの入力をすべて処理する必要がある場合に便利です。
Action のデバッグ
現在有効になっている Action と、それらにバインドされている Control を確認するには、Input Debugger を使用します。
Visualizers サンプルの InputActionVisualizer
コンポーネントを使用して、Action の値と Interaction の状態を画面上でリアルタイムに可視化することもできます。
複数プレイヤーでの Action の使用
同じ Action 定義を、(ローカル協力ゲームなどで) 複数のローカルプレイヤーに使用できます。詳細については、Player Input Manager コンポーネントのドキュメントを参照してください。
用語と概念
Input Action システムでは、以下の用語と概念が使用されます。
概念 | 説明 |
---|---|
Action | "Jump" (ジャンプ) や "Fire" (発射) のような論理的な入力。つまり、プレイヤーが 1 つ以上の入力デバイスを介してトリガーでき、その応答として、ゲームロジックの一部が実行される Input Action を意味します。 |
Binding | 1 つのAction と、コントロールパス で表される 1 つ以上の Control との接続。ランタイムには、0 個以上の Control に Binding が解決され、その Control が Input System によって Action に接続されます。 |
Interaction | Control 上で認識できる固有の入力パターン。Interaction は、Input System でパターンが認識された場合にのみ Action をトリガーします。 例えば、"hold" Interaction に関連付けられた Action をトリガーするには、Control を作動させた後、一定の時間だけ押したままにする必要があります。 |
Processor | Input System によって入力値に適用される操作。例えば、"invert" Processor は、浮動小数点値の符号を反転します。 |
Phase | Interaction の現在の状態を説明する enum。 |
Control Scheme | 異なる Binding のサブセットを Action で使用できるようにするしくみ。Binding のマッピングを別々の Control Scheme に定義して、異なる Control Scheme 間で Action Map を切り替えることができます。Control Scheme には、Device タイプを関連付けることができます。これにより、ユーザー が使用している Device タイプに応じて、ゲームで自動的に適切な Control Scheme を有効にできます。 |
Action Map | Action の名前付きコレクション。アクションマップ内の Action はすべて同時に有効または無効にすることができるため、Action Map 内の Action を、関連のあるコンテキスト ("gameplay" など) に従ってグループ化すると便利です。 |
Action Asset | 1 つ以上の Action Map と、オプションとして一連の Control Scheme を含むアセット。 |