Input Binding
InputBinding
は、1 つの Action と、Control パス で識別される 1 つ以上の Control との接続を表します。Action には、その Action を指す、任意の数の Bindingを作成できます。同じ Control を複数の Binding が参照することもできます。
各 Binding には以下のプロパティがあります。
プロパティ | 説明 |
---|---|
path |
Action に入力を送信するコントロールを識別する Control パス。 例: "<Gamepad>/leftStick" |
overridePath |
path をオーバーライドする Control パス。path とは異なり、overridePath は永続化されないため、これを使用して、Binding でパスを非破壊的にオーバーライドすることができます。null 以外の値に設定すると有効になり、path がオーバーライドされます。現在有効なパス (path または overridePath ) を取得するには、effectivePath プロパティを照会します。 |
action |
この Binding が発する Action の名前または ID。null または空にすることができます (合成 の場合など)。大文字と小文字は区別されません。 例: "fire" |
groups |
この Binding が属する Binding グループの、セミコロン区切りのリスト。null または空にすることができます。任意の Binding グループを含めることができ、それらは主に Control Scheme (Control Scheme) に使用されます。大文字と小文字は区別されません。 例: "Keyboard&Mouse;Gamepad" |
interactions |
この Binding の入力に適用する Interaction (Interaction) の、セミコロン区切りのリスト。Action 自体に適用される Interaction (存在する場合) が、このリストの末尾に追加されます。大文字と小文字は区別されません。 例: "slowTap;hold(duration=0.75)" |
processors |
この Binding の入力に適用する Processor の、セミコロン区切りのリスト。Action 自体に適用される Processor (存在する場合) が、このリストの末尾に追加されます。大文字と小文字は区別されません。 Binding に設定した Processor は、値を提供する Control の Processor に加えて適用されます。例えば、Binding に stickDeadzone Processor を設定し、それを <Gamepad>/leftStick にバインドすると、デッドゾーンが 2 回適用されます。これは、leftStick Control に設定されたデッドゾーンの Processor と Binding から 1 回ずつ適用されるためです。例: "invert;axisDeadzone(min=0.1,max=0.95)" |
id |
Binding の一意の ID。例えば、Binding のオーバーライドをユーザー設定に保存する場合に、これを使用して Binding を識別することができます。 |
name |
Binding の名前 (任意)。合成 内でのパーツ名を識別します。 例: "Positive" |
isComposite |
この Binding が 合成 として動作するかどうかを示します。 |
isPartOfComposite |
この Binding が 合成 のパーツかどうかを示します。 |
特定の Action に対する Binding を照会するには、InputAction.bindings
を使用します。Action Map 内のすべての Action の Binding を、階層のないリストとして照会するには、InputActionMap.bindings
を使用します。
合成 Binding
ときには、複数の Control を同時に作動させて、別のタイプの Control と同様の動作を実現しようとする場合があります。この例として最も多いのが、キーボードの W、A、S、D の各キーを、マウス移動やゲームパッドのスティックと同等の 2D ベクトル Control として使用する場合です。また、2 つのキーを使用して、マウスのスクロール軸と同等の 1D 軸を形成する場合もあります。
これを通常の Binding で実装することは困難です。Vector2
を想定しているアクションに ButtonControl
をバインドすることはできます。しかし、そうすると、Input System が float
しか返すことができない Control から Vector2
を読み取ろうとすると、ランタイムに例外が発生します。
この問題を解決するのが、合成 Binding (他の複数の Binding で構成される Binding) です。合成 Binding は、それ自体が Control に直接バインドされるのではなく、直接バインドされる他の Binding から値を取得し、それらの値から動的に入力を合成します。
エディター UI で合成を作成する方法については、合成 Binding の編集 に関するドキュメントを参照してください。
コードで合成を作成するには、AddCompositeBinding
構文を使用できます。
myAction.AddCompositeBinding("Axis")
.With("Positive", "<Gamepad>/rightTrigger")
.With("Negative", "<Gamepad>/leftTrigger");
各合成は、InputBinding.isComposite
が true に設定された 1 つの Binding と、その後に続く、InputBinding.isPartOfComposiste
が true に設定された 1 つ以上の Binding で構成されます。言い換えると、InputActionMap.bindings
または InputAction.bindings
内の、連続する複数のエントリーによって、1 つの合成が形成されます。
合成の各パーツは何度でもバインドできます。
//ショルダーとトリガーの両方を、軸に沿って移動させます。
myAction.AddCompositeBinding("Axis")
.With("Positive", "<Gamepad>/rightTrigger")
.With("Positive", "<Gamepad>/rightShoulder")
.With("Negative", "<Gamepad>/leftTrigger");
.With("Negative", "<Gamepad>/leftShoulder");
Interaction や Processor と同様に、合成 Binding にはパラメーターを加えることができます。
myAction.AddCompositeBinding("Axis(whichSideWins=1)");
システムには現在、そのまま使用できる合成タイプとして、1D Axis (1D 軸)、2D Vector (2D ベクトル)、3D Vector (3D ベクトル)、One Modifier (1 モディファイア)、Two Modifiers (2 モディファイア) の 5 つが用意されています。さらに、独自のタイプの合成を追加 することもできます。
1D Axis
2 つのボタンで構成される合成です。ボタンの 1 つで 1D 軸が負の方向に移動され、もう 1 つでは、正の方向に移動されます。AxisComposite
クラスに実装されています。結果は float
です。
myAction.AddCompositeBinding("1DAxis") //または単に "Axis"
.With("Positive", "<Gamepad>/rightTrigger")
.With("Negative", "<Gamepad>/leftTrigger");
軸合成には、2 つのパーツ Binding があります。
パーツ | タイプ | 説明 |
---|---|---|
positive |
Button |
正方向に (maxValue に向かって) 移動させる Control。 |
negative |
Button |
負方向に (minValue に向かって) 移動させる Control。 |
軸合成には、以下のパラメーターを設定できます。
パラメーター | 説明 |
---|---|
whichSideWins |
positive と negative の両方が作動した場合の動作。下の表を参照してください。 |
minValue |
negative 側が作動した場合に返される値。デフォルトでは -1 です。 |
maxValue |
positive 側が作動した場合に返される値。デフォルトでは 1 です。 |
positive
側と negative
側の両方の Control が作動した場合、軸合成の結果の値は whichSideWin
パラメーターの設定によって決まります。
WhichSideWins |
説明 |
---|---|
(0) Neither |
どちらの側も優先されません。合成の結果として、minValue と maxValue の中間点が返されます。デフォルト設定では 0 になります。これが、この設定のデフォルト値です。 |
(1) Positive |
正の側が優先され、合成で maxValue が返されます。 |
(2) Negative |
負の側が優先され、合成で minValue が返されます。 |
ノート: 時間経過に従って正と負の間を補間する機能は、まだサポートされていません。
2D Vector
ゲームパッドの D パッドなど、4 方向ボタンの設定を表す合成です。各ボタンは基本方位を表します。Vector2Composite
クラスに実装されています。結果は Vector2
です。
この合成は、WASD キーボード入力などの上下左右のコントロールを表す場合に最も役立ちます。
myAction.AddCompositeBinding("2DVector") //または "Dpad"
.With("Up", "<Keyboard>/w")
.With("Down", "<Keyboard>/s")
.With("Left", "<Keyboard>/a")
.With("Right", "<Keyboard>/d");
//モードを設定する場合 (2=analog、1=digital、0=digitalNormalized)
myAction.AddCompositeBinding("2DVector(mode=2)")
.With("Up", "<Gamepad>/leftStick/up")
.With("Down", "<Gamepad>/leftStick/down")
.With("Left", "<Gamepad>/leftStick/left")
.With("Right", "<Gamepad>/leftStick/right");
2D Vector 合成には、4 つのパーツ Binding があります。
パーツ | タイプ | 説明 |
---|---|---|
up |
Button |
(0,1) (+Y) を表す Control。 |
down |
Button |
(0,-1) (-Y) を表す Control。 |
left |
Button |
(-1,0) (-X) を表す Control。 |
right |
Button |
(1,0) (+X) を表す Control。 |
さらに、2D Vector 合成には、以下のパラメーターを設定できます。
パラメーター | 説明 |
---|---|
mode |
入力をデジタルコントロールとして扱うか、アナログコントロールとして扱うかを指定します。Mode.DigitalNormalized に設定すると、入力はボタン (defaultButtonPressPoint 未満はオフ、それ以上はオン) として扱われます。各入力は、ボタンが押されているかどうかに応じて、0 または 1 になります。上/下/左/右のパーツから生じるベクトルは正規化されます。結果はひし形の 2D 入力範囲です。Mode.Digital に設定すると、基本的には Mode.DigitalNormalized と同じ動作になりますが、結果のベクトルは正規化されません。最後に、 Mode.Analog に設定すると、入力はアナログ (つまり、完全な浮動小数点数値) として扱われ、down と left の符号が反転される点を除いて、値がそのまま渡されます。デフォルトでは Mode.DigitalNormalized です。 |
ノート: 時間の経過に従って上/下/左/右の間を補間する機能は、まだサポートされていません。
3D Vector
6 方向ボタンを表す合成です。2 方向の組み合わせのそれぞれが、3D ベクトルの 1 つの軸を制御します。Vector3Composite
クラスに実装されています。結果は Vector3
です。
myAction.AddCompositeBinding("3DVector")
.With("Up", "<Keyboard>/w")
.With("Down", "<Keyboard>/s")
.With("Left", "<Keyboard>/a")
.With("Right", "<Keyboard>/d");
//モードを設定する場合 (2=analog、1=digital、0=digitalNormalized)
myAction.AddCompositeBinding("3DVector(mode=2)")
.With("Up", "<Gamepad>/leftStick/up")
.With("Down", "<Gamepad>/leftStick/down")
.With("Left", "<Gamepad>/leftStick/left")
.With("Right", "<Gamepad>/leftStick/right");
3D Vector 合成には、6 つのパーツ Binding があります。
パーツ | タイプ | 説明 |
---|---|---|
up |
Button |
(0,1,0) (+Y) を表す Control。 |
down |
Button |
(0,-1,0) (-Y) を表す Control。 |
left |
Button |
(-1,0,0) (-X) を表す Control。 |
right |
Button |
(1,0,0) (+X) を表す Control。 |
forward |
Button |
(0,0,1) (+Z) を表す Control。 |
backward |
Button |
(0,0,-1) (-Z) を表す Control。 |
さらに、3D Vector 合成には、以下のパラメーターを設定できます。
パラメーター | 説明 |
---|---|
mode |
入力をデジタルコントロールとして扱うか、アナログコントロールとして扱うかを指定します。Mode.DigitalNormalized に設定すると、入力はボタン (defaultButtonPressPoint 未満はオフ、それ以上はオン) として扱われます。各入力は、ボタンが押されているかどうかに応じて、0 または 1 になります。上/下/左/右/前/後のパーツから生じるベクトルは正規化されます。Mode.Digital に設定すると、基本的には Mode.DigitalNormalized と同じ動作になりますが、結果のベクトルは正規化されません。最後に、 Mode.Analog に設定すると、入力はアナログ (つまり完全な浮動小数点数値) として扱われ、down 、left 、backward の符号が反転される点を除いて、値がそのまま渡されます。デフォルトでは Analog です。 |
One Modifier
この合成では、1 つの "モディファイア" ボタンを押したままで、もう 1 つのコントロールを押す必要があり、このコントロールで、Binding の実際の値が決まります。例えば、"SHIFT+1" などの Binding に使用できます。OneModifierComposite
クラスに実装されています。このボタンには、あらゆる Device 上にあるボタンのほか、トグルボタンや、ゲームパッドのトリガーのようなフルレンジのボタンも該当します。
結果は、binding
で表されるパーツにバインドされたコントロールと同じ型の値になります。
//"CTRL+1" の Binding を追加します。
myAction.AddCompositeBinding("OneModifier")
.With("Binding", "<Keyboard>/1")
.With("Modifier", "<Keyboard>/ctrl")
//マウス移動への Binding を、Alt キーが押されている
//間にのみ有効になるように追加します。
myAction.AddCompositeBinding("OneModifier")
.With("Binding", "<Mouse>/delta")
.With("Modifier", "<Keyboard>/alt");
One Modifier 合成のボタンには、2 つのパーツ Binding があります。
パーツ | タイプ | 説明 |
---|---|---|
modifier |
Button |
binding を入力するために押している必要のある Modifier。modifier にバインドされているボタンのいずれかと、アクションをトリガーするボタンをユーザーが同時に押した場合、合成は modifier Binding の値になります。modifier にバインドされたボタンをユーザーが押さなかった場合は、合成がデフォルト値のままになります。 |
binding |
Any | その値が、ユーザーが modifier ボタンを押している間の合成の値となるコントロール。 |
この合成にパラメーターはありません。
Two Modifiers
この合成では、ユーザーが、2 つの "モディファイア" ボタンを押したままで、もう 1 つのコントロールを押す必要があり、このコントロールで Binding の実際の値が決まります。例えば、"SHIFT+CTRL+1" のような Binding に使用できます。TwoModifiersComposite
クラスに実装されています。このボタンには、あらゆる Device 上にあるボタンのほか、トグルボタンや、ゲームパッドのトリガーのようなフルレンジのボタンも該当します。
結果は、binding
で表されるパーツにバインドされたコントロールと同じ型の値になります。
myAction.AddCompositeBinding("TwoModifiers")
.With("Button", "<Keyboard>/1")
.With("Modifier1", "<Keyboard>/leftCtrl")
.With("Modifier1", "<Keyboard>/rightCtrl")
.With("Modifier2", "<Keyboard>/leftShift")
.With("Modifier2", "<Keyboard>/rightShift");
Two Modifiers 合成のボタンには、3 つのパーツ Binding があります。
パーツ | タイプ | 説明 |
---|---|---|
modifier1 |
Button |
1 つ目のモディファイア。binding を入力するには、ユーザーが、このモディファイアを modifier2 と同時に押している必要があります。modifier1 にバインドされたボタンをユーザーが押さなかった場合は、合成がデフォルト値のままになります。 |
modifier2 |
Button |
2 つ目のモディファイア。binding を入力するには、ユーザーが、このモディファイアを modifier1 と同時に押している必要があります。modifier2 にバインドされたボタンをユーザーが押さなかった場合は、合成がデフォルト値のままになります。 |
binding |
Any | その値が、ユーザーが modifier1 と modifier2 の両方を同時に押している間の合成の値となるコントロール。 |
この合成にパラメーターはありません。
カスタム合成の作成
新しいタイプの合成を定義し、API に登録することができます。事前定義されているタイプも、Input System の内部で同じように定義され、登録されます。Unity では、どちらも同様に扱われます。
新しいタイプの合成を定義するには、InputBindingComposite<TValue>
を継承してクラスを作成します。
重要: 合成は ステートレス であることが必要です。したがって、処理対象の入力によって変化するローカルの状態を保存することはできません。ステートフル な Binding の処理については、インタラクション を参照してください。
//TValue 型の値を返す合成では、InputBindingComposite<TValue> を
//基本クラスとして使用します。
//ノート: さまざまな種類の値を返す合成を定義できますが、そのためには
//InputBindingComposite から直接派生させる必要があります。
# if UNITY_EDITOR
[InitializeOnLoad] //自動的にエディターに登録します。
# endif
//DisplayStringFormat を適用して、GetBindingDisplayString() で使用される
//合成の書式を指定します。
[DisplayStringFormat("{firstPart}+{secondPart}")]
public class CustomComposite :InputBindingComposite<float>
{
//それぞれのパーツ Binding は int 型のフィールドとして表され、
//InputControlAttribute で注釈が付けられます。"レイアウト" を設定すると、
//UI で選択可能になるコントロールを限定できます。
//
//int 値は、作成時に Binding パーツの整数 ID に
//設定されます。この ID で InputBindingCompositeContext から値を読み取ることができます。
//下にある ReadValue() を参照してください。
[InputControl(layout = "Button")]
public int firstPart;
[InputControl(layout = "Button")]
public int secondPart;
//InputControlAttribute で注釈が付けられていないパブリックフィールドは、すべて
//合成のパラメーターと見なされます。これは、UI で視覚的に設定することも、
//データ内で設定することもできます ("custom(floatParameter=2.0)" など)。
public float floatParameter;
public bool boolParameter;
//このメソッドによって、パーツ Binding からの入力に基づいて、合成の
//結果の入力値が計算されます。
public override float ReadValue(ref InputBindingCompositeContext context)
{
var firstPartValue = context.ReadValue<float>(firstPart);
var secondPartValue = context.ReadValue<float>(secondPart);
//... 処理を実行して値を返します
}
//このメソッドによって、Binding 全体の現在の作動量が計算されます。
public override float EvaluateMagnitude(ref InputBindingCompositeContext context)
{
//現在の作動レベルを表す、正規化された [0..1] の作動量の値を計算します。
}
static CustomComposite()
{
//カスタムの名前を設定するか、デフォルト値 (タイプ名から "Composite" を除いた名前) を使用することができます。
//同じ合成を、異なる名前で複数回登録して別名として使用することも
//できます。
//
//ノート: InitializeOnLoad と RuntimeInitializeOnLoadMethod を使用した
//静的コンストラクターからの登録は、1 つの方法に過ぎません。合成は、
//目的に適した任意の場所から登録できます。ただし、登録は、
//合成が Binding 内で初めて使用される前に実行する必要があることに
//注意してください。さらに、合成がエディターに表示されるようにするには、
//編集モードで動作するコードから登録する必要があります。
InputSystem.RegisterBindingComposite<CustomComposite>();
}
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
static void Init() {} //静的コンストラクターをトリガーします。
}
これで、Binding の追加時にエディター UI に合成が表示され、スクリプトで使用できるようになりました。
myAction.AddCompositeBinding("custom(floatParameter=2.0)")
.With("firstpart", "<Gamepad>/buttonSouth")
.With("secondpart", "<Gamepad>/buttonNorth");
合成に対応するカスタムのパラメーターエディターを定義するには、InputParameterEditor<TObject>
から派生させます。
# if UNITY_EDITOR
public class CustomParameterEditor :InputParameterEditor<CustomComposite>
{
public override void OnGUI()
{
EditorGUILayout.Label("Custom stuff");
target.floatParameter = EditorGUILayout.FloatField("Some Parameter", target.floatParameter);
}
}
# endif
Binding の操作
Binding の検索
InputAction.bindings
プロパティを使用して、Action の Binding を取得することができます。このプロパティは、読み取り専用の InputBinding
構造体の配列を返します。
//"fire" アクションの Binding を取得します。
var fireBindings = playerInput.actions["fire"].bindings;
また、InputActionMap.bindings
プロパティを使用すると、InputActionMap
内のすべての Action に対するすべての Binding が可能になります。各 Binding は、InputBinding.action
プロパティで指定される Action ID または Action 名 によって Action に関連付けられます。
//"gameplay" Action Map のすべての Binding を取得します。
var gameplayBindings = playerInput.actions.FindActionMap("gameplay").bindings;
InputActionRebindingExtensions.GetBindingIndex
メソッドを使用して、InputAction.bindings
内の特定の Binding のインデックスを取得することもできます。
//"Keyboard" Control Scheme 内の Binding を見つけます。
playerInput.actions["fire"].GetBindingIndex(group:"Keyboard");
//"gameplay" Action Map で、Space キーに対する最初の Binding を見つけます。
playerInput.FindActionMap("gameplay").GetBindingIndex(
new InputBinding { path = "<Keyboard>/space" });
最後に、GetBindingIndexForControl
を使用すると、特定の Control に対応する Binding を検索できます。これにより、例えば、InputAction
の controls
配列にある Control を InputBinding
に再びマップできます。
//LMB を "fire" にバインドする Binding を見つけます。該当する Binding がない場合、
//bindingIndex は -1 になります。
var fireAction = playerInput.actions["fire"];
var bindingIndex = fireAction.GetBindingIndexForControl(Mouse.current.leftButton);
if (binding == -1)
Debug.Log("Fire is not bound to LMB of the current mouse.");
Binding の変更
一般に、既存の Binding は、InputActionSetupExtensions.ChangeBinding
メソッドを通じて変更できます。これにより、ターゲットの InputBinding
のプロパティを変更するために使用できるアクセサーが返されます。アクセサーの書き込み操作のほとんどは破壊的であることに注意してください。Binding を非破壊的に変更する方法については、オーバーライドの適用 を参照してください。
//"fire" アクションの 2 番目の Binding に対する書き込みアクセスを取得します。
var accessor = playerInput.actions['fire'].ChangeBinding(1);
//InputActionMap を通じてアクセスを取得することもできます。各マップには、すべての
//Binding を格納した配列が含まれています (InputActionMap.bindings を参照)。
//ここでは、マップ内の 3 番目の Binding へのアクセスを取得します。
accessor = playerInput.actions.FindActionMap("gameplay").ChangeBinding(2);
返されたアクセサーを使用すると、WithPath
や WithProcessors
などのメソッドを介してプロパティを変更できます。
playerInput.actions["fire"].ChangeBinding(1)
//Space キーのパスを変更します。
.WithPath("<Keyboard>/space");
アクセサーを使用して、PreviousBinding
と NextBinding
によって Binding を反復処理することもできます。
//前の Binding にアクセサーを移動します。
accessor = accessor.PreviousBinding();
//次の Binding にアクセサーを移動します。
accessor = accessor.NextBinding();
Binding が 合成 である場合は、インデックスではなく、名前で指定できます。
//"move" アクションの 2DVector 合成を変更します。
playerInput.actions["move"].ChangeCompositeBinding("2DVector")
//
playerInput.actions["move"].ChangeBinding("WASD")
オーバーライドの適用
ランタイムに Binding は、さまざまな面から非破壊的にオーバーライドできます。InputBinding
の特定のプロパティには、override
バリアントがあり、これが設定されている場合は、そのシャドウイング対象のプロパティよりも優先されます。すべての override
プロパティは String
型です。
プロパティ | オーバーライド | 説明 |
---|---|---|
path |
overridePath |
Binding で参照される Control を特定する Control パス を置き換えます。overridePath を空の文字列に設定すると、Binding は実質的に無効になります。例: "<Gamepad>/leftStick" |
processors |
overrideProcessors |
Binding に適用される プロセッサー を置き換えます。 例: "invert,normalize(min=0,max=10)" |
interactions |
overrideInteractions |
Binding に適用される インタラクション を置き換えます。 例: "tap(duration=0.5)" |
ノート:
override
のプロパティ値が Action と一緒に保存されることはありません (InputActionAsset.ToJson()
を呼び出したときなど)。ユーザーによる再 Binding を永続化する方法の詳細については、再 Binding の保存とロード を参照してください。
さまざまな override
プロパティを設定するには、ApplyBindingOverride
API を使用できます。
//ゲームパッドの左トリガーに "fire" アクションを再バインドします。
playerInput.actions["fire"].ApplyBindingOverride("<Gamepad>/leftTrigger");
ほとんどの場合、最善の方法は、GetBindingIndexForControl
などの API を使用して目的の Binding を特定してから、その Binding にオーバーライドを適用することです。
//Space キーの "Jump" Binding を見つけます。
var jumpAction = playerInput.actions["Jump"];
var bindingIndex = jumpAction.GetBindingIndexForControl(Keyboard.current.spaceKey);
//Enter キーに変更します。
jumpAction.ApplyBindingOverride(bindingIndex, "<Keyboard>/enter");
Binding の消去
Binding を消去するには、Binding のアクセサー で Erase
を呼び出します。
//"fire" アクションの最初の Binding を消去します。
playerInput.actions["fire"].ChangeBinding(0).Erase();
//"2DVector" 合成を消去します。これにより、合成のパーツ
//Binding も消去されます。
playerInput.actions["move"].ChangeCompositeBinding("2DVector").Erase();
//合成 Binding に指定した名前を使用して、これを行うこともできます。
playerInput.actions["move"].ChangeCompositeBinding("WASD").Erase();
//"gameplay" アクションマップの 1 番目の Binding を消去します。
playerInput.actions.FindActionMap("gameplay").ChangeBinding(0).Erase();
Binding の追加
AddAction
または AddCompositeBinding
を使用すると、Action に新しい Binding を追加できます。
//左マウスボタンの Binding を "fire" アクションに追加します。
playerInput.actions["fire"].AddBinding("<Mouse>/leftButton");
//"move" アクションに WASD 合成 Binding を追加します。
playerInput.actions["move"]
.AddCompositeBinding("2DVector")
.With("Up", "<Keyboard>/w")
.With("Left", "<Keyboard>/a")
.With("Down", "<Keyboard>/s")
.With("Right", "<Keyboard>/d");
パラメーターの設定
バインドすると、Binding 自体、または Binding に関連付けられた Action を介して、プロセッサー、インタラクション、合成 の各オブジェクトが作成されることがあります。これらのオブジェクトにあるパラメーターは、Action エディターの Binding のプロパティビュー または API 経由で設定できます。ここで設定した値が、パラメーターのデフォルト値となります。
//"Hold" インタラクションが設定されたアクションを作成します。
//"duration" パラメーターを 4 秒に設定します。
var action = new InputAction(interactions:"hold(duration=4)");
このようなパラメーターの現在の値を照会するには、GetParameterValue
API を使用します。
//これで PrimitiveValue? が返されます。パラメーターが見つからない
//場合は null が返されます。それ以外の場合は、数値または
//ブーリアンに変換できる PrimitiveValue が返されます。
var p = action.GetParameterValue("duration");
Debug.Log("'duration' is set to:" + p.Value);
上記のコードで、アクションの任意の Binding に含まれている任意のオブジェクトのパラメーターが検索されます。Binding とオブジェクトの一方または両方を絞り込むこともできます。
//特に "Hold" インタラクションの "duration" パラメーターを取得し、
//"Gamepad" グループの Binding のみに注目します。
action.GetParameterValue("hold:duration", InputBinding.MaskByGroup("Gamepad"));
別の方法として、式パラメーターを使用して、値を取得するパラメーターの型と名前の両方をカプセル化することもできます。これには、文字列パラメーターを必要とせず、パラメーターの型と名前の両方をタイプセーフな形で参照できるという利点があります。
//TapInteraction の "duration" パラメーターの値を取得します。
//この方法では、コンパイル時に "duration" の型が認識されるため、
//PrimitiveValue? ではなく、float? が返されます。
action.GetParameterValue((TapInteraction x) => x.duration);
パラメーターの現在の値を変更するには、"パラメーターのオーバーライド" と呼ばれる手法を使用できます。これらは、個々の InputAction
や InputActionMap
全体のレベルだけでなく、InputActionAsset
全体のレベルでも適用できます。このようなオーバーライドは内部に格納され、後から追加される Binding にも自動的に適用されます。
オーバーライドを追加するには、ApplyParameterOverride
API またはそのオーバーロードの 1 つを使用します。
//アクションのすべての Binding の "duration" パラメーターを 4 に設定します。
action.ApplyParameterOverride("duration", 4f);
//特に "tap" インタラクションのみに "duration" パラメーターを設定します。
action.ApplyParameterOverride("tap:duration", 0.5f);
//"Gamepad" グループ内の Binding のみに、tap インタラクションの "duration"
//パラメーターを設定します。
action.ApplyParameterOverride("tap:duration", 0.5f, InputBinding.MaskByGroup("Gamepad");
//アクションマップ内のすべての Binding に tap duration を設定します。
map.ApplyParameterOverride("tap:duration", 0.5f);
//アセット全体のすべての Binding に tap duration を設定します。
asset.ApplyParameterOverride("tap:duration", 0.5f);
//GetParameterValue と同様に、式を受け取るオーバーロードが
//用意されています。
action.ApplyParameterOverride((TapInteraction x) => x.duration, 0.4f);
map.ApplyParameterOverride((TapInteraction x) => x.duration, 0.4f);
asset.ApplyParameterOverride((TapInteraction x) => x.duration, 0.4f);
新しい値はすぐに適用され、オーバーライドのターゲットとなる、既に使用中のすべての合成、プロセッサー、インタラクションに反映されます。
複数のパラメーターオーバーライドが適用される場合、特に、アクションに直接適用されるものと、マップまたはアセットに適用されるものが混在する場合は、適用されるオーバーライドの間に競合が発生することがあります。この場合は、"最も限定的" なオーバーライドを選択して適用することが試みられます。
//例えば、InputAction `action` があり、InputActionAsset アセットの一部であるとします。
var map = action.actionMap;
var asset = map.asset;
//"tap:duration" のオーバーライドをアクションに適用します。
action.ApplyParameterOverride("tap:duration", 0.6f);
//さらに、"Gamepad" グループ内の Binding に限定した
//"tap:duration" のオーバーライドも適用します。
action.ApplyParameterOverride("tap:duration", 1f, InputBinding.MaskByGroup("Gamepad"));
//最後に、アセット全体にも、"tap:duration" のオーバーライドを適用します。
asset.ApplyParameterOverride("tap:duration", 0.3f);
//これにより、"Gamepad" グループ内での `action` の Binding では、tap duration の値が 1 になり、
//その他の `action` の Binding では 0.6、アセット内の他の Binding では 0.3 になります。
パラメーターのオーバーライドを使用して、例えば、"Look" アクションでのマウス移動量の値を増減することができます。
//サンプルの "Look" アクションを設定します。
var look = new InputAction("look", type:InputActionType.Value);
look.AddBinding("<Mouse>/delta", groups:"KeyboardMouse", processors:"scaleVector2");
look.AddBinding("<Gamepad>/rightStick", groups:"Gamepad", processors:"scaleVector2");
//スティックの感度とマウスの感度を別々に調整できます。
look.ApplyParameterOverride("scaleVector2:x", 0.5f, InputBinding.MaskByGroup("KeyboardMouse"));
look.ApplyParameterOverride("scaleVector2:y", 0.5f, InputBinding.MaskByGroup("KeyboardMouse"));
look.ApplyParameterOverride("scaleVector2:x", 2f, InputBinding.MaskByGroup("Gamepad"));
look.ApplyParameterOverride("scaleVector2:y", 2f, InputBinding.MaskByGroup("Gamepad"));
//グループを使用する代わりに、特定の Binding パスにオーバーライドを直接適用することもできます。
look.ApplyParameterOverride("scaleVector2:x", 0.5f, new InputBinding("<Mouse>/delta"));
look.ApplyParameterOverride("scaleVector2:y", 0.5f, new InputBinding("<Mouse>/delta"));
ノート: パラメーターのオーバーライドがアセットと一緒に永続化されることは ありません。
インタラクティブな再 Binding
ノート: Input System API を使用した再 Binding ユーザーインターフェースの設定方法を示すサンプルプロジェクトを入手するには、Package Manager を開き、Input System パッケージを選択し、"Rebinding UI" サンプルプロジェクトを選択してダウンロードします。
ランタイムの再 Binding により、アプリケーションのユーザーが独自の Binding を設定できるようになります。
ユーザーが独自の Binding をインタラクティブに選択できるようにするには、InputActionRebindingExtensions.RebindingOperation
クラスを使用します。Action で PerformInteractiveRebinding()
メソッドを呼び出すと、再 Binding 操作を作成できます。この操作では、Action で想定された Control タイプに一致する任意の Device 入力が Input System によって登録されるまで待機した後に、InputBinding.overridePath
を使用して、その Control の Control パスが Action の Binding に割り当てられます。ユーザーが複数の Control を作動させた場合は、再 Binding 操作により、作動量 の最も大きい Control が選択されます。
重要:
Dispose()
を介してInputActionRebindingExtensions.RebindingOperation
インスタンスを破棄して、アンマネージメモリヒープでのメモリのリークを防ぐ必要があります。
void RemapButtonClicked(InputAction actionToRebind)
{
var rebindOperation = actionToRebind
.PerformInteractiveRebinding().Start();
}
InputActionRebindingExtensions.RebindingOperation
API では、ニーズに合わせた高度な設定が可能です。例えば、以下の設定が可能です。
想定される Control タイプを選択する (
WithExpectedControlType()
)。特定の Control を除外する (
WithControlsExcluding()
)。操作をキャンセルするための Control を設定する (
WithCancelingThrough()
)。Action に複数の Binding がある場合に、操作の適用先となる Binding を選択する (
WithTargetBinding()
、WithBindingGroup()
、WithBindingMask()
)。
詳細な概要については、スクリプティング API リファレンスの InputActionRebindingExtensions.RebindingOperation
を参照してください。
PerformInteractiveRebinding()
により、指定されたアクションおよびターゲットの Binding に基づいて、一連のデフォルト設定が自動的に適用されます。
再 Binding の保存とロード
Binding のオーバーライドプロパティは、JSON 文字列としてシリアル化したり、それらの文字列から復元したりできます。この文字列を作成するには SaveBindingOverridesAsJson
を使用し、文字列からオーバーライドを復元するには LoadBindingOverridesFromJson
を使用します。
//プレイヤーの再 Binding を PlayerPrefs に保存します。
var rebinds = playerInput.actions.SaveBindingOverridesAsJson();
PlayerPrefs.SetString("rebinds", rebinds);
//プレイヤーの再 Binding を PlayerPrefs から復元します (アクションの
//既存のオーバーライドはすべて削除されます。2 番目の引数に `false` を渡すと、
//これを防ぐことができます)。
var rebinds = PlayerPrefs.GetString("rebinds");
playerInput.actions.LoadBindingOverridesFromJson(rebinds);
元の Binding の復元
RemoveBindingOverride
または RemoveAllBindingOverrides
を使用すると、Binding のオーバーライドを削除してデフォルトの設定を復元できます。
//"fire" アクションの最初の Binding から Binding オーバーライドを削除します。
playerInput.actions["fire"].RemoveBindingOverride(0);
//"fire" アクションから、すべての Binding オーバーライドを削除します。
playerInput.actions["fire"].RemoveAllBindingOverrides();
//プレイヤーのアクションから、すべての Binding オーバーライドを削除します。
playerInput.actions.RemoveAllBindingOverrides();
Binding の表示
UI の再 Binding の設定中は、(アクティブになる可能性のある再 Binding を考慮して) Action の現在の Binding 先がわかるようにしたり、アプリケーションの実行中に画面にヒントを表示したりすると、ユーザーにとって便利である可能性があります。InputBinding.effectivePath
を使用して、Binding の現在アクティブなパスを取得することができます (これにより、overridePath
が (設定されている場合は) 返され、設定されていない場合は path
が返されます)。
アクションの表示文字列を取得する最も簡単な方法は、InputActionRebindingExtensions.GetBindingDisplayString
を呼び出すことです。これは、InputAction
の拡張メソッドです。
//アクション全体の Binding 文字列を取得します。ここでは、現在アクティブな Binding と、
//アクションにバインドされている実際のコントロールが考慮されます。
m_RebindButton.GetComponentInChildren<Text>().text = action.GetBindingDisplayString();
//アクションの特定の Binding の Binding 文字列を、インデックスを指定して取得します。
m_RebindButton.GetComponentInChildren<Text>().text = action.GetBindingDisplayString(1);
//GetBindingIndex を使用して Binding インデックスを取得します。
var bindingIndex = action.GetBindingIndex(InputBinding.MaskByGroup("Gamepad"));
m_RebindButton.GetComponentInChildren<Text>().text =
action.GetBindingDisplayString(bindingIndex);
このメソッドを使用して、テキスト文字列を画像に置き換えることもできます。
//GetBindingDisplayString() を呼び出します。このとき、デバイスレイアウトの名前と
//デバイス上のコントロールのパスに関する情報も返されるようにします。この情報は、
//個々のコントロールに確実に画像を関連付けるために役立ちます。
//ノート: 最初の引数は、InputAction.bindings 内の Binding のインデックスです。
var bindingString = action.GetBindingDisplayString(0, out deviceLayout, out controlPath);
//ゲームパッドの場合は、コントロールのアイコンを探します。
Sprite icon = null;
if (!string.IsNullOrEmpty(deviceLayout)
&& !string.IsNullOrEmpty(controlPath)
&& InputSystem.IsFirstLayoutBasedOnSecond(deviceLayout, "Gamepad"))
{
switch (controlPath)
{
case "buttonSouth":icon = aButtonIcon; break;
case "dpad/up":icon = dpadUpIcon; break;
//...
}
}
//アイコンがある場合は、テキストの代わりに表示します。
var text = m_RebindButton.GetComponentInChildren<Text>();
var image = m_RebindButton.GetComponentInChildren<Image>();
if (icon != null)
{
//アイコンを表示します。
text.gameObject.SetActive(false);
image.gameObject.SetActive(true);
image.sprite = icon;
}
else
{
//テキストを表示します。
text.gameObject.SetActive(true);
image.gameObject.SetActive(false);
text.text = bindingString;
}
さらに、各 Binding には ToDisplayString
メソッドがあり、個々の Binding を表示文字列に変換するために使用できます。Control パスを書式設定する汎用的なメソッドとして、任意の Control パス文字列で使用できる InputControlPath.ToHumanReadableString
も用意されています。
Binding の解決先となる Control は、任意のタイミングで変わる可能性があるため、コントロールの表示文字列は動的に変わることがあります。例えば、現在アクティブなキーボードレイアウトをユーザーが切り替えると、Keyboard
上の各キーの表示文字列が変わる可能性があります。
Control Scheme
1 つの Binding は、任意の数の Binding グループに含めることができます。Unity では、これらのグループが、セミコロン区切りの文字列として InputBinding
クラスの InputBinding.groups
プロパティに格納されます。これらを使用して Binding を自由にグループ化することができます。InputActionMap
や InputActionAsset
で、異なる Binding グループのセットを有効にするには、InputActionMap.bindingMask
/InputActionAsset.bindingMask
プロパティを使用します。Input System では、これを使用して、複数の Binding を異なる InputControlScheme
にグループ化する概念を実装しています。
Control Scheme は、Binding グループを使用して、InputActionMap
または InputActionAsset
内の Binding をさまざまな Device タイプにマップします。PlayerInput
クラスは、これらを使用して、プレイしている Device に基づいて、ゲームに参加する新規 ユーザー に適合する Control Scheme を有効にします。
詳細
Binding の解決
Action にバインドされた Control に Input System が初めてアクセスすると、Action は Binding を解決して、その Control を既存の Device 上の既存の Control に一致させます。このプロセスでは、Action の Binding ごとに、Binding パスに対して InputSystem.FindControls<>()
が呼び出されます (InputActionMap にデバイスが割り当てられている場合は、対象となるデバイスがそれらに絞り込まれます)。これにより、Action にバインドされた解決済みの Control のリストが作成されます。
以下のように、1 つの Binding パス が複数の Control に一致する可能性があることに注意が必要です。
<DualShockGamepad>/buttonEast
などの特定の Device パスは、PlayStation コントローラー の "〇" ボタンに一致します。複数の PlayStation コントローラーが接続されている場合は、それぞれのコントローラーの "〇" ボタンに解決されます。<Gamepad>/buttonEast
のような抽象 Device パスは、接続されている任意のゲームパッドの右アクションボタンに一致します。PlayStation コントローラーと Xbox コントローラー が接続されている場合は、PlayStation コントローラーの "〇" ボタンと、Xbox コントローラーの "B" ボタンに解決されます。Binding パスにワイルドカードを含めることもできます。例えば、
<Gamepad>/button*
と記述できます。これは、任意のゲームパッド上にある、名前が "button" で始まる任意の Control に一致します。つまり、接続されている任意のゲームパッド上にある、4 つのアクションボタンのすべてに一致します。別の例として、*/{Submit}
は、任意の Device 上にある、"Submit" という 使用法 でタグ付けされた任意の Control に一致します。
同じ Action に複数の Binding があり、それらがすべて同じ (1 つまたは複数の) Conrol を参照している場合は、実質的に、Control から Action に複数回入力が送信されます。この動作により、例えば、Control を異なる方法 (合成、プロセッサー、インタラクションなど) でバインドすることで、1 つの Control から同じ Action で異なる入力を生成できます。ただし、Control と特定の Action とのバインド回数にかかわらず、1 つの Control が Action の controls
配列 に出現するのは 1 回だけです。
Action の解決先となる Control を照会するには、InputAction.controls
を使用します。このクエリは、Action が無効になっている場合でも実行できます。
Binding が解決されたときに通知を受け取るには、InputSystem.onActionChange
をリッスンします。Control リストの変更前には InputActionChange.BoundControlsAboutToChange
がトリガーされ、更新後には InputActionChange.BoundControlsChanged
がトリガーされます。
Action が有効になっているときの Binding の解決
状況によっては、Action にバインドされている Control を 2 回以上更新することが必要になります。例えば、新しい Device が Action で使用可能になると、Action が入力を受け取るコントロールが追加される可能性があります。また、Binding の追加、削除、変更を行った場合も、Control リストの更新が必要になります。
このような Control の更新は通常、バックグラウンドで透過的に行われます。ただし、Action が 有効 になっている場合、特に 進行中 である場合は、目に見える影響が Action に生じることがあります。
デバイスの追加または削除は、グローバル な場合でも、Action の デバイスリスト に対する場合でも透過的に行われますが、Action の進行中に、その アクティブな Control のデバイスが削除される場合は 例外 です。この場合、Action は自動的に キャンセル されます。
また、Binding マスク を変更したり、Binding のいずれかを変更したりする (再 Binding を行う、または Binding を追加するか、削除する) と、有効になっているすべての Action が一時的に無効になり、再び有効になって再開されます。
使用する Device の選択
ノート:
InputUser
とPlayerInput
では、このしくみが自動的に使用されます。これらにより、ユーザーに組み合わされた Device に基づいてInputActionMap.devices
が自動的に設定されます。
デフォルトでは、Action の Binding の解決が、Input System に存在するすべての Device (つまり、InputSystem.devices
) に対して行われます。例えば、システム内に 2 つのゲームパッドがある場合、<Gamepad>/buttonSouth
への Binding は両方のゲームパッドに一致し、どちらからでも Action を使用できるようになります。
この動作は、InputActionAsset
または個々の InputActionMap
を特定の Device セットに限定することでオーバーライドできます。これを行うと、指定した Device の Control のみが Binding 解決の対象となります。
var actionMap = new InputActionMap();
//アクションマップを最初のゲームパッドのみに限定します。
actionMap.devices = new[] { Gamepad.all[0] };
入力の競合
以下の 2 つの状況では、送信された入力の解釈が複数存在することになります。
- 複数の Control が同じ Action にバインドされていて、そのうちの 2 つ以上が同時に Action に入力を送信しています。例えば、Action がゲームパッドの左右のトリガーにバインドされていて、両方のトリガーが押されています。
- 入力シーケンスの一部が入力されていて、該当する入力シーケンスが複数存在しています。例えば、ある Action が
B
キーにバインドされていて、別の Action がShift-B
にバインドされています。
同時に使用される複数の Control
ノート: このセクションの内容は、意図的に複数の同時入力を許容するように設計されている
PassThrough
Action には適用されません。
Button
または Value
の Action では、Action を "駆動" している Control は常に 1 つのみです。その Control が activeControl
と見なされます。
Action が複数の Control にバインドされている場合、任意の時点の activeControl
は、"作動" レベルが最大の Control になります。つまり、EvaluateMagnitude
から返される値が最も大きい Control です。現在の activeControl
の作動レベルを超える Control がある場合は、それ自体が、アクティブな Control となります。
以下の例では、Button
Action を使用して、このメカニズムの実例と PassThrough
Action との違いを示します。
//ボタンと Pass-Through アクションを作成し、それぞれを
//ゲームパッドの両方のトリガーにバインドします。
var buttonAction = new InputAction(type:InputActionType.Button,
binding:"<Gamepad>/*Trigger");
var passThroughAction = new InputAction(type:InputActionType.PassThrough,
binding:"<Gamepad>/*Trigger");
buttonAction.performed += c => Debug.Log("${c.control.name} pressed (Button)");
passThroughAction.performed += c => Debug.Log("${c.control.name} changed (Pass-Through)");
buttonAction.Enable();
passThroughAction.Enable();
//左トリガーを奥まで押します。
//buttonAction と passThroughAction の両方がトリガーされます。どちらも
//leftTrigger が activeControl になります。
Set(gamepad.leftTrigger, 1f);
//以下がログに記録されます。
//"leftTrigger pressed (Button)" および
//"leftTrigger changed (Pass-Through)"
//右トリガーを半分まで押します。
//これで buttonAction が実行されることも、変更されることも *ありません*。右トリガーの作動量が、既に
//アクションを動かしている左トリガーよりも *小さい* ためです。
//一方、passThrough アクションは、このような追跡を行わないため、値の変更に直接
//応答します。アクションが実行され、rightTrigger が activeControl になります。
Set(gamepad.rightTrigger, 0.5f);
//以下がログに記録されます。
//"rightTrigger changed (Pass-Through)"
//左トリガーを解放します。
//buttonAction にとっては、これが、アクションに入力しているすべてのコントロールが解放されたことを
//意味するため、ボタンが解放されます。activeControl は null に戻ります。
//passThrough アクションでは、これも値の変更と見なされます。そのため、アクションが
//実行され、アクティブなコントロールが leftTrigger に変わります。
Set(gamepad.leftTrigger, 0f);
//以下がログに記録されます。
//"leftTrigger changed (Pass-Through)"
合成 Binding の場合は、個々の Control の作動量ではなく、合成全体としての作動量が追跡されます。ただし、activeControl
では、合成の個々の Control が追跡されます。
競合解決の無効化
Button および Value タイプの Action では、常に競合解決が行われます。ただし、バインドされた Control から単純にすべての入力を収集する目的で Action を使用する状況では、この動作は望ましくない可能性があります。例えば、以下の Action は、使用可能なすべてのゲームパッドの A ボタンを監視します。
var action = new InputAction(type:InputActionType.PassThrough, binding:"<Gamepad>/buttonSouth");
action.Enable();
Pass-Through Action タイプを使用すると、競合解決がバイパスされるため、1 つのゲームパッドで A ボタンを押しても、他のゲームパッドでの押下が無視されることはなくなります。
複数の入力シーケンス (キーボードショートカットなど)
ノート: ここで説明するメカニズムは、同じ
InputActionMap
orInputActionAsset
の一部である Action にのみ適用されます。
他の入力と組み合わせて入力を使用する場合にも、あいまいさが生じることがあります。例えば、キーボードの b
キーが、単独でも、shift
キーとの組み合わせでもバインドされている場合は、最初に shift
キーを押してから b
キーを押すと、後で押したキーが、どちらの Action に対しても有効な入力と見なされる可能性があります。
このような場合は、"複雑さ" が大きい順に Binding が処理されます。この指標は、以下の方法で Binding から自動的に導かれます。
前の例では、Shift+B
への OneModifier
合成 Binding の方が、B
への Binding よりも "複雑さ" が大きいため、最初に処理されます。
さらに、Action の フェーズ を変更する結果となった最初の Binding は、その入力を "消費" します。この消費の結果、同じ入力に対する他の Binding が処理されなくなります。したがって、前の例では、Shift+B
によって B
の入力が "消費" されると、B
への Binding がスキップされます。
以下の例では、この動作を API レベルで示します。
//同じマップ内に 2 つのアクションを作成します。
var map = new InputActionMap();
var bAction = map.AddAction("B");
var shiftbAction = map.AddAction("ShiftB");
//アクションの 1 つを "B" にバインドし、もう 1 つを "SHIFT+B" にバインドします。
bAction.AddBinding("<Keyboard>/b");
shiftbAction.AddCompositeBinding("OneModifier")
.With("Modifier", "<Keyboard>/shift")
.With("Binding", "<Keyboard>/b");
//アクションがトリガーされたら、コンソールにメッセージを出力します。
bAction.performed += _ => Debug.Log("B action performed");
shiftbAction.performed += _ => Debug.Log("SHIFT+B action performed");
//入力のリッスンを開始します。
map.Enable();
//キーボードの左 Shift キーが押されたとします (ここでは、InputTestFixture API を
//使用して手動でキーを押します)。
Press(Keyboard.current.leftShiftKey);
//次に B が押されたとします。これは、bAction と shiftbAction の
//両方に対して有効な入力です。
//
//この場合、shiftbAction が最初に処理を行うことになります。応答として
//アクションが *実行* され (つまり、`performed` コールバックが呼び出され)、
//入力が "消費" されます。その結果、bAction は何も処理せずにスキップされます。
Press(keyboard.bKey);
初期状態のチェック
Action を 有効 にすると、それ以降の入力の発生時にその Action が応答するようになります。ただし、Action が有効になった時点で、アクションに バインド されている 1 つ以上の Control が既にデフォルト以外の状態になっている場合があります。
"初期状態のチェック" と呼ばれる機能を使用すると、Action が有効になった 後 に状態変更が発生した場合と同様に、そのようなデフォルト以外の状態に を応答させることができます。これは、Action が有効になった後の最初の入力 更新 で、バインドされているすべてのコントロールが順番にチェックされて実現されます。デフォルト以外の状態になっているコントロールがあると、ただちに Action が応答します。
Value アクションでは、このチェックが暗黙で有効になっています。例えば、ゲームパッドの左スティックに Move
Action がバインドされている場合、Move
が有効になった時点で既にスティックが 1 つの方向に押されていると、即座にキャラクターが歩き始めます。
Button および Pass-Through タイプの Action では、このチェックがデフォルトでは実行されません。Action が有効になったときにボタンが押されていても、Action をトリガーするには、最初にボタンを放してから、もう一度押す必要があります。
ただし、これらのタイプの Action でも、エディターで以下のチェックボックスを使用すると、初期状態のチェックを手動で有効にすることができます。