Unityゲームエンジンの入力システム「 Input System」でゲーム内のキーコンフィグ、つまりゲーム内でキーを変更する処理を実装する方法を紹介します。
従来のInput Managerは、そもそもキーのバインドができないため実装不可能でした。そのため、ゲーム内キーコンフィグを実装するには一からInput Managerもどきを実装する必要がありました。
Input Systemはゲーム内でキーの再バインディングを行うことで実装が可能です。そのため、使う側は何をバインディングしたかを全く意識することなく利用が可能です。
リバインディングを実装
では、サンプル例を用意します。
空のGameObjectを作成しPlayerInputコンポーネントを追加、Creater Actionsで予め簡単な設定が完了しているInputActionアセットを作成します。
uGuiのTextとButtonを作成、TextをRebindingMessageと命名しActive状態をFalseにします。ButtonをRebindingButtonとします。
新規c#ファイル「RebindingInput.cs」を作成し、先ほどのGameObjectにコンポーネントとして追加します。コードは以下の通り。
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.UI;
public class RebindingInput : MonoBehaviour
{
//PlayerInputコンポーネントがあるゲームオブジェクト
[SerializeField]
private PlayerInput _pInput;
//リバインディング中のメッセージ表示テキスト。アクティブ状態の可否に使用。
[SerializeField]
private GameObject _rebindingMessage;
//リバインディングを開始するボタン。アクティブ状態の可否に使用。
[SerializeField]
private GameObject _rebindingButton;
//リバインディング開始ボタンのテキスト。キー名を表示。
[SerializeField]
private Text _bindingName;
//リバインディングしたいInputAction項目。今回はMap:PlayerのAction:Fireを使用しています。
[SerializeField]
private InputActionReference _action;
//リバインディングしたいコントロールのインデックス
private string rebindingIndex;
private InputActionRebindingExtensions.RebindingOperation _rebindingOperation;
public void Start()
{
//アクション(Fire)ボタンを押下するとデバッグにFireと表示
_pInput.actions["Fire"].performed += _ => Debug.Log("Fire");
//すでにリバインディングしたことがある場合はシーン読み込み時に変更。
string rebinds = PlayerPrefs.GetString("rebindSample");
if (!string.IsNullOrEmpty(rebinds))
{
//リバインディング状態をロード
_pInput.actions.LoadBindingOverridesFromJson(rebinds);
//バインディング名を取得
int bindingIndex = _action.action.GetBindingIndexForControl(_action.action.controls[0]);
_bindingName.text = InputControlPath.ToHumanReadableString(
_action.action.bindings[bindingIndex].effectivePath,
InputControlPath.HumanReadableStringOptions.OmitDevice);
}
}
public void StartRebinding()
{
//ボタンを消し、代わりにリバインディング中のメッセージを表示
_rebindingButton.SetActive(false);
_rebindingMessage.SetActive(true);
//ボタン制御中の表示
//ボタンの誤作動を防ぐため、何も無いアクションマップに切り替え
_pInput.SwitchCurrentActionMap("Select");
//Fireボタンのリバインディング開始
_rebindingOperation = _action.action.PerformInteractiveRebinding()
.WithTargetBinding(_action.action.GetBindingIndexForControl(_action.action.controls[0]))
.WithControlsExcluding("Mouse")
.OnMatchWaitForAnother(0.1f)
.OnComplete(operation => RebindComplete())
.Start();
}
public void RebindComplete()
{
//fireアクションの1番目のコントロール(バインディングしたコントロール)のインデックスを取得
int bindingIndex = _action.action.GetBindingIndexForControl(_action.action.controls[0]);
//バインディングしたキーの名称を取得する
_bindingName.text = InputControlPath.ToHumanReadableString(
_action.action.bindings[bindingIndex].effectivePath,
InputControlPath.HumanReadableStringOptions.OmitDevice);
_rebindingOperation.Dispose();
//画面を通常に戻す
_rebindingButton.SetActive(true);
_rebindingMessage.SetActive(false);
//リバインディング時は空のアクションマップだったので通常のアクションマップに切り替え
_pInput.SwitchCurrentActionMap("Player");
//リバインディングしたキーを保存(シーン開始時に読み込むため)
PlayerPrefs.SetString("rebindSample", _pInput.actions.SaveBindingOverridesAsJson());
}
}
次にButtonを押した時、StartRebinding()を実行するようにします。
InputActionアセットのAction Mapsに新しいマップ「Select」を追加します。何もアクションが存在しないマップです。
実行するとボタンが一つあり、選択するとキー入力画面になり、何かキーを押すと、そのキーがボタンのテキストに表示されます。また、初期はマウスの右クリックでログが出力しますが、以後は設定したキーを押すとログを出力するように変化します。
では、コードを解説します。
セーブ&ロード
//リバインディングしたキーを保存(シーン開始時に読み込むため)
PlayerPrefs.SetString("rebindSample", _pInput.actions.SaveBindingOverridesAsJson());
//リバインディング状態をロード
string rebinds = PlayerPrefs.GetString("rebindSample");
_pInput.actions.LoadBindingOverridesFromJson(rebinds);
InputSystem Ver1.10以降に利用できる機能です。その時のバインディングの状態をJSON形式で出力、読み込みができます。
RebindingOperation
リバインディングの核となるクラスです。
_rebindingOperation = _fireAction.action.PerformInteractiveRebinding()
.OnComplete(operation => RebindComplete())
.Start();
実際のリバインディングを実行している箇所。「RebindingOperation = 指定のアクション.PerformInteractiveRebinding」でバインディングしたい項目をセットし、.Start()で実行します。リバインディング終了後にOnCompleteの処理を施して終了します。
OnComplete後、RebindingOperation.actionでリバインディング後のアクションの取得が可能です。
この他にも、RebindingOperationには様々なメソッド項目があります。
リバインディング項目の設定
指定したコントロールに対してバインディングを実行します。上記コードだと以下の箇所です。
.WithTargetBinding(_action.action.GetBindingIndexForControl(_action.action.controls[0]))
Fireアクションの有効になっているコントロールの一番最初をリバインディングします。action.controlsは、例えば、キーボードの値を変更しようと思った場合、Control Schemesを「Keyboard & Mouse」に設定しているMが0番、Right Triggerが1番になります。それ以外の項目は取得できないようになっています。
今回はコントロールを指定する方法で実現しましたが、以下のようなメソッドも用意されています。これらは複合指定可能です。
WithAction | 指定したアクションをリバインディングします。 |
WithBindingGroup | 指定したグループ(Control Schemes)をリバインディングします。 |
WithBindingMask | 指定したマスクをリバインディングします。 |
WithCancelingThrough | 指定したコントロールをリバインディングします。 |
//以下のコードは同じ意味です。
_rebindingOperation = _action.action.PerformInteractiveRebinding();
_rebindingOperation = new InputActionRebindingExtensions.RebindingOperation()
.WithAction(_action);
指定例
var rebind = new RebindingOperation()
.WithAction(_action)
.WithBindingGroup("Gamepad")
.WithCancelingThrough("<Keyboard>/escape");
rebind.Start();
選択できないキーやデバイスを設定:WithControlsExcluding
WithControlsExcludingは、リバインディングできないデバイスやキーを設定できます。
.WithControlsExcluding("<Pointer>/position")
.WithControlsExcluding("Mouse")
特定のデバイスに設定:WithControlsHavingToMatchPath
WithControlsHavingToMatchPathは、リバインディングできるデバイスやキーを固定することができます。以下はゲームパッド以外のリバインディングを受け付けなくなります。
.WithControlsHavingToMatchPath("<Gamepad>");
Rebind Action UIコンポーネントを利用する
Input Systemのサンプル例にRebindingの実装を行っているサンプルがあります。各UI一つ一つにリバインドを設定できるコンポーネントになっており、手軽に利用できます。セーブできないので拡張する必要がありますが。
Package ManagerのInput Systemからsampleをクリックし、Rebinding UIをInstallします。
ボタンなど、一つ一つのリバインドしたいUIにコンポーネントを追加します。
Binding
Actionは、リバインドしたいInput Actionアセット内のアクションをセットします。
Bindingは、Actionでセットしたアクションに設定したコントロールが表示されるので、リバインドで上書きしても良い項目を選択します。
Display Optionはバインディング中にUIに表示したい項目です。
UI
Action Labelは、アクション名を表示します。
Binding Textは、Display Optionsで指定した項目を表示します。
Rebind Overlayは、リバインド中にアクティブにするゲームオブジェクトです。
Rebind Textは、リバインドしたキーを表示します。