Control
Input Control (入力コントロール) は、値のソースを表します。これらの値は、任意の構造体またはプリミティブ型にすることができます。唯一の要件は、型が blittable であることです。
ノート: Control は入力専用です。Input Device の出力項目と設定項目は Control として表されません。
各 Control は名前 (InputControl.name
) で識別され、必要に応じて、Control 名とは異なる表示名 (InputControl.displayName
) を使用できます。例えば、PlayStation DualShock 4 コントローラーの場合は、タッチパッドのすぐ右横にあるフェースボタンの Control 名が "buttonWest" で、表示名が "Square" (四角) となります。
さらに、Control には、その Control の代替名として使用できる 1 つ以上の別名 (エイリアス) が設定されている場合があります。特定の Control の別名にアクセスするには、InputControl.aliases
プロパティを使用します。
最後に、Control には、短い表示名がある場合もあります。これには、InputControl.shortDisplayName
プロパティを通じてアクセスできます。例えば、左マウスボタンの短い表示名は "LMB" です。
Control の階層
Control は階層構造を形成できます。Control 階層のルートは常に Device です。
この階層構造の設定は レイアウト によってのみ制御されます。
Control の親にアクセスするには、InputControl.parent
を使用します。子にアクセスするには、InputControl.children
を使用します。Device 上のすべての Control のフラットな階層にアクセスするには、InputDevice.allControls
を使用します。
Control のタイプ
すべてのコントロールは InputControl
基本クラスに基づいています。ほとんどの具体的な実装は、InputControl<TValue>
に基づいています。
Input System には、以下のタイプのコントロールが標準で用意されています。
Control タイプ | 説明 | 例 |
---|---|---|
AxisControl |
1D 浮動小数点軸。 | Gamepad.leftStick.x |
ButtonControl |
浮動小数点値として表されるボタン。ボタンが 0 と 1 以外の値を持つことができるかどうかは、その基盤の表現によって異なります。例えば、ゲームパッドのトリガーボタンは 0 と 1 以外の値を持つことができますが、ゲームパッドのフェースボタンはそうでないのが一般的です。 | Mouse.leftButton |
KeyControl |
Keyboard 上のキーを表す特殊なボタン。キーには、keyCode が関連付けられます。他のタイプの Control とは異なり、キーの表示名は、現在アクティブなシステムレベルのキーボードレイアウトに従って変わります。詳細については、Keyboard のドキュメントを参照してください。 |
Keyboard.aKey |
Vector2Control |
2D 浮動小数点ベクトル。 | Pointer.position |
Vector3Control |
3D 浮動小数点ベクトル。 | Accelerometer.acceleration |
QuaternionControl |
3D 回転。 | AttitudeSensor.attitude |
IntegerControl |
整数値。 | Touchscreen.primaryTouch.touchId |
StickControl |
2D スティックコントロール。ゲームパッドのサムスティックや、ジョイスティックのスティックコントロールなどです。 | Gamepad.rightStick |
DpadControl |
4 方向ボタンコントロール。ゲームパッドの D パッドや、ジョイスティックのハットスイッチなどです。 | Gamepad.dpad |
TouchControl |
タッチスクリーン 上のタッチのすべてのプロパティを表すコントロール。 | Touchscreen.primaryTouch |
Input Debugger では、登録されているすべてのコントロールレイアウトのセットを参照できます。
Control の使用法
Control には、1 つ以上の使用法を関連付けることができます。使用法とは、Control で想定されている用途を示す文字列です。Control の使用法の一例は、UI で選択を確定するために一般的に使用される Control にラベルを付ける Submit
です。ゲームパッドでは、この使用法が buttonSouth
Control に適用されるのが普通です。
Control の使用法にアクセスするには、InputControl.usages
プロパティを使用します。
使用法は、任意の文字列にすることができます。ただし、特によく使われる使用法のセットは決まっているため、それらが API で CommonUsages
静的クラスとして事前定義されています。概要については、スクリプティング API の CommonUsages
のページ を参照してください。
Control パス
例:
<Gamepad>/leftStick/x
は、"ゲームパッドの左スティックの X Control" を意味します。
Input System は、テキスト形式のパスを使用して Control を参照します。Input Action の Binding では、この機能を使用して、入力の読み取り元の Control を特定します。これらを使用して、Control と Device を直接検索したり、InputSystem.FindControls
を使用して Input System ですべてのデバイスから Control を検索したりすることもできます。
var gamepad = Gamepad.all[0];
var leftStickX = gamepad["leftStick/x"];
var submitButton = gamepad["{Submit}"];
var allSubmitButtons = InputSystem.FindControls("*/{Submit}");
Control パスはファイルシステムのパスに似ています。それぞれのパスは、以下のように、スラッシュで区切られた 1 つ以上のコンポーネント (component) から構成されます。
component/component...
各コンポーネントには、複数のフィールドから成る、類似した構文が使用されます。各フィールドは省略可能ですが、少なくとも 1 つのフィールドが必要です。どのフィールドでも、大文字と小文字が区別されません。
<layoutName>{usageName}controlName#(displayName)
以下の表では、各フィールドの使用方法について説明します。
フィールド | 説明 | 例 |
---|---|---|
<layoutName> |
このレベルの Control が、特定のレイアウトに基づいている必要があることを指定します。Control の実際のレイアウトは、指定したレイアウトと同じであるか、指定したレイアウトに 基づく レイアウトです。 | <Gamepad>/buttonSouth |
{usageName} |
Control と Device で動作が異なります。 Device (パスの最初のコンポーネント) に使用する場合は、Device に特定の使用法が設定されている必要があることを指定します。詳細については、Device の使用法 を参照してください。 Control の検索については、現在、使用法フィールドの指定場所が、Device 直下のパスコンポーネント (パスの 2 番目のコンポーネント) に制限されています。このフィールドでは、指定の使用法が設定された、Device 上の Control が検索されます。この Control は、Device の Control 階層内のいずれかの場所にあります。 |
Device:<XRController>{LeftHand}/trigger Control: <Gamepad>/{Submit} |
controlName |
このレベルの Control に、特定の名前が必要であることを指定します。"正式" な名前 (InputControl.name ) と別名 (InputControl.aliases ) の両方が考慮されます。このフィールドには、任意の名前に一致するワイルドカード ( * ) を使用できます。 |
MyGamepad/buttonSouth */{PrimaryAction} (任意の名前の Device 上の PrimaryAction 使用法に一致) |
#(displayName) |
このレベルの Control に、特定の表示名 (InputControl.displayName ) がある必要があることを指定します。表示名には、空白や記号が含まれている場合があります。 |
<Keyboard>/#(a) (現在のキーボードレイアウトに "a" 文字を生成するキーがある場合は、それに一致)<Gamepad>/#(Cross) |
特定のコントロールのリテラルパスにアクセスするには、InputControl.path
プロパティを使用します。
必要な場合は、InputControlPath.Parse(path)
API を使用すると、手動でコントロールパスを解釈してコンポーネントに分解することができます。
var parsed = InputControlPath.Parse("<XRController>{LeftHand}/trigger").ToArray();
Debug.Log(parsed.Length); //2 が出力されます。
Debug.Log(parsed[0].layout); //"XRController" が出力されます。
Debug.Log(parsed[0].name); //空の文字列が出力されます。
Debug.Log(parsed[0].usages.First()); //"LeftHand" が出力されます。
Debug.Log(parsed[1].layout); //null が出力されます。
Debug.Log(parsed[1].name); //"trigger" が出力されます。
Control の状態
各 Control は、Control の "状態" を表すメモリブロックに関連付けられます。このメモリブロックのサイズ、形式、位置は、Control から InputControl.stateBlock
プロパティを介して照会できます。
Control の状態は、Input System の内部で処理されるアンマネージメモリに格納されます。Device 上のすべての Control の状態が、アンマネージメモリの 1 ブロックに格納され、そのブロックがシステムに追加されているすべての Device で共有されます。
Control の状態は、その Control にとって自然な形式で格納されるとは限りません。例えば、システムでは、ボタンがビットフィールドとして表され、軸コントロールが、8 ビットまたは 16 ビットの整数値として表されることがよくあります。この形式は、プラットフォーム、ハードウェア、ドライバーの組み合わせによって決まります。各 Control には、そのストレージ形式と必要時に値を解釈する方法がわかっています。Input System は レイアウト を使用して、この表現を理解します。
Control の現在の状態にアクセスするには、ReadValue
メソッドを使用します。
Gamepad.current.leftStick.x.ReadValue();
Control の状態でサポートされている形式の種類の数にかかわらず、各 Control が返す値の型は 1 つです。この値型にアクセスするには、InputControl.valueType
プロパティを使用します。
Control から値を読み取ることで、1 つ以上の値 Processor が適用されることがあります。詳細については、Processor に関するドキュメントを参照してください。
状態履歴の記録
場合によっては、Control の値の変更履歴にアクセスできると便利です (例えば、タッチ解放の出口速度を計算するために使用できます)。
時間経過と共に生じる状態変更を記録するには、InputStateHistory
または InputStateHistory<TValue>
を使用します。後者では、特定の値型に Control が制限されるため、API の一部が簡素化されます。
//Vector2 コントロールの値の変更を記録する履歴を作成します。
//ノート: コントロールを直接渡すか、複数のコントロールに一致するパス
//("<Gamepad>/<Button>" など) を使用することができます。
//ノート: 制約のない InputStateHistory クラスでは、さまざまな値型のコントロールの
//変更を記録できます。
var history = new InputStateHistory<Vector2>("<Touchscreen>/primaryTouch/position");
//履歴が関連付けられたコントロールの状態変更を記録し始めるには、
//StartRecording を呼び出します。
history.StartRecording();
//状態変更の記録を停止するには、StopRecording を呼び出します。
history.StopRecording();
//記録された履歴には配列のようにアクセスできます。
for (var i = 0; i < history.Count; ++i)
{
//記録された各値には、値を変更したコントロールと (複数のコントロールの
//状態が同じ InputStateHistory によって同時に記録される場合のため)、
//値が変更された時刻に関する情報が含まれています。
var time = history[i].time;
var control = history[i].control;
var value = history[i].ReadValue();
}
//記録された履歴を反復処理することもできます。
foreach (var record in history)
Debug.Log(record.ReadValue());
Debug.Log(string.Join(",\n", history));
//手動で状態変更を記録することもできます。この場合、
//任意の履歴を InputStateHistory に格納できます。
//ノート: ここでは、実際にはコントロールで発生していない値の変更を記録します。
history.RecordStateChange(Touchscreen.current.primaryTouch.position,
new Vector2(0.123f, 0.234f));
//状態履歴ではアンマネージメモリが割り当てられるため、状態履歴を破棄する必要があります。
history.Dispose();
例えば、使用可能なゲームパッドの左スティックで最後の 100 サンプルを取得する場合、以下のコードを使用できます。
var history = new InputStateHistory<Vector2>(Gamepad.current.leftStick);
history.historyDepth = 100;
history.StartRecording();
Control の作動
Control がデフォルトの状態ではなくなり、Control の実際の値に影響するようになると、Control が作動していると見なされます。Control が現在作動しているかどうかを照会するには、IsActuated
を使用します。
//leftStick が現在作動しているかどうかを確認します。
if (Gamepad.current.leftStick.IsActuated())
Debug.Log("Left Stick is actuated");
場合によっては、Control が作動しているかどうかだけでなく、作動の大きさを表す量 (マグニチュード) を特定することが役立ちます。例えば、Vector2Control
では、この値がベクトルの長さを表し、ボタンでは、未加工の浮動小数点数の絶対値を表します。
一般に、Control の現在の作動量は常に >= 0 になります。ただし、意味があるほどの作動量が Control にない場合は、-1 が返されます。負の値は、すべて無効な作動量と見なす必要があります。
現在の作動量を照会するには、EvaluateMagnitude
を使用します。
//左スティックが、モーション範囲の 1/4 を超えて作動しているかどうかを調べます。
if (Gamepad.current.leftStick.EvaluateMagnitude() > 0.25f)
Debug.Log("Left Stick actuated past 25%");
以下の 2 つのメカニズムでは、Control の作動が特に活用されます。
- インタラクティブな再バインディング (
InputActionRebindingExceptions.RebindOperation
) では、これを使用して、該当する複数の Control から、作動量が最大の Control を見つけて選択します。 - 同じアクションにバインドされている複数の Control 間の 競合解決 では、これを使用して、どの Control がアクションを駆動するかを決定します。
値が変動する Control
Input System によって、"値が変動する" (noisy) というラベルが Control に付けられることがあります。これを照会するには、InputControl.noisy
プロパティを使用します。
値が変動する Control とは、ユーザーが実際に、または意図的に操作することなく値が変化する可能性のある Control です。わかりやすい例として、携帯電話の重力センサーがあります。携帯電話をまったく動かしていない場合でも、通常、重力の読み取り値に変動が生じます。もう 1 つの例は、HMD からの回転の読み取り値です。
値が変動するとマークされた Control は、以下のように扱われます。
- Control は、インタラクティブな再バインディング の対象と見なされません。
InputActionRebindingExceptions.RebindingOperation
は、デフォルトでこの Control を無視します (この動作は、WithoutIgnoringNoisyControls
を使用することでバイパスできます)。 - プロジェクト設定で有効になっている場合、システムは、追加のイベントフィルタリングを実行してから
InputDevice.MakeCurrent
を呼び出します。Device の入力イベントに、"値が変動する" とマークされていない Control の状態変更が含まれていない場合は、そのイベントに基づいて Device が現在の Device に設定されることはありません。これにより、例えば、PS4 コントローラーが接続されているときに、センサーから絶えずシステムにデータが送信されるために、コントローラーがそれ自体を現在のゲームパッド (Gamepad.current
) に設定しようとし続ける現象が防止されます。 - アプリケーションからフォーカスが失われ、その結果として Device が リセット されても、値が変動する Control の状態は、そのまま維持されます。これにより、センサーの読み取り値がデフォルト値にリセットされず、最後の値が確実に保持されます。
ノート: 値が変動する Control が Device 上にある場合は、Device 自体にも、値が変動するというフラグが付けられます。
Input System は、現在存在しているすべての Device について保持している input state
と default state
と並行して、値変動以外の状態を示すビットだけが設定された noise mask
も保持しています。これを使用すると、非常に効率的に値の変動を入力から除外できます。
シンセティックな Control
シンセティックな Control とは、デバイス上の実際の物理コントロールには対応しない Control (例えば、StickControl
の left
、right
、up
、down
などの子 Control) です。これらの Control は、他の実際の物理 Control からの入力を合成し、異なる方法で表します (前の例では、スティックのそれぞれの向きをボタンとして扱えるようにしています)。
特定の Control がシンセティック Control かどうかは、InputControl.synthetic
プロパティで判別できます。
システムでは、シンセティックな Control が インタラクティブな再バインディング の対象と見なされますが、非シンセティック Control の方が常に優先されます。一致する可能性のある、シンセティック Control と非シンセティック Control の両方が存在する場合は、デフォルトで非シンセティック Control が優先されます。これにより、例えば、<Gamepad>/leftStick/left
にインタラクティブにバインドできますが、スティック上のシンセティックボタンからの干渉を受けずに <Gamepad>/leftStickPress
にバインドすることもできます。