Unityゲームエンジンの入力システム「 Input System」のインストール方法と代表的な機能の使い方を説明するチュートリアルです。
インストール方法
Window > Package ManagerのUnity RegisryよりInput Systemをプロジェクトにインストールします。
Input Systemをインストールすると、Unityは旧Inputを無効にし、InputSystemを有効にするかを尋ねます。Yesを選択するとエディターが再起動し、InputSystemが反映されます。
デフォルトでは、Unityの従来の入力マネージャー(UnityEngine.Input)がアクティブであり、新しい入力システムのサポートは非アクティブです。これにより、既存のUnityプロジェクトをそのまま機能させることができます。
ちなみに、Edit > Project Setting > PlayerのConfigurationにある項目、Active Input Handlingで同様の設定が可能です。
- Input Manager(Old):従来のInput Managerを使用。Input Systemは無効化。
- Input System Package(New):Input Systemを使用。Input Managerは無効化。
- Both:Input SystemもInput Managerも有効化。
Bothにすると新旧どちらのInputも動作しますが意図せずに使用していない方のInputを使用してバグが起きる可能性があるため、Input Systemのみの使用を推奨します。
単純なボタン押下確認
まずは、そのフレームで単一のキーが押下状態にあるかを確認するコードです。
キーボード入力 | Keyboard.current. ボタン.wasPressedThisFrame |
マウス入力 | Mouse.current.ボタン.wasPressedThisFrame |
ゲームパッド入力 | Gamepad.current.ボタン.wasPressedThisFrame |
using UnityEngine;
using UnityEngine.InputSystem;
public class Character : MonoBehaviour
{
public void Update()
{
if (Keyboard.current.spaceKey.wasPressedThisFrame)
{
Debug.Log("スペースキーが押されたよ");
}
if (Mouse.current.leftButton.wasPressedThisFrame)
{
Debug.Log("マウスの左ボタンがクリックされたよ");
}
if (Gamepad.current.aButton.wasPressedThisFrame)
{
Debug.Log("ゲームパッドのaボタンがクリックされたよ");
}
}
}
Player Inputを使った入力取得
Input Sysmteを扱うには1からコードを書くことも可能ですが、通常はPlayer Inputコンポーネントを使用します。Player InputにInput Actionアセットをセットすることで、煩雑なコードを書くことなく機能を利用できます。
HierarchyでCreate Emptyを選択し、空のGameObjectを作成しPlayerInputコンポーネントを追加します。1つのPlayerInputは1人のプレイヤーを表します。マルチプレイの場合は複数のPlayerInputが存在することもあります。
PlayerInputのInspectorでCreate Actionボタンを押してInputActionsアセットを作成します。できたInputActionsアセットをPlayerInputのActionsにドラッグドロップします。
仮想キーに割り当てることができる機能があり、InputActionsアセットで各種ボタンの結び付けを行います。PlayerInputからCreateした場合、予めデフォルトの設定がいくつか組み込まれています。Projectを右クリックしてCreate > InputActionsでも作成できますが、こちらは空で一から自分で設定します。
▲PlayerInputからCreateしたInputActionsの設定画面。方向キーやボタン2つに様々なデバイスが設定されていることが分かります。
デフォルトの状態だと、以下のようになっています。
Move | ゲームパッドの左スティック、WASD、スマホコントローラーの十字キー、ジョイスティックに反応しVector2を返します。 |
Look | ゲームパッドの右スティック、カーソル位置、ジョイスティックのボタンに反応しVector2を返します。 |
Fire | ゲームパッドのRTボタン、マウスの右クリック、画面タップ、ジョイスティックのトリガー、スマホコントローラーのボタンに反応しFloatを返します。 |
Input Actionアセットの詳しい編集方法は以下のページで解説しています。
Projectで右クリックし、Create > c#スクリプトを選択し、名前をInputTestとします。GameObjectにInputTestコンポーネントを追加します。InputTestに以下のコードを記述します。
using UnityEngine;
using UnityEngine.InputSystem;
public class InputTest : MonoBehaviour
{
private InputAction _moveAction, _lookAction, _fireAction;
public void Start()
{
var pInput = GetComponent<PlayerInput>();
//現在のアクションマップを取得。
//初期状態はPlayerInputコンポーネントのinspectorのDefaultMap
var actionMap = pInput.currentActionMap;
//アクションマップからアクションを取得
_moveAction = actionMap["Move"];
_lookAction = actionMap["Look"];
_fireAction = actionMap["Fire"];
}
public void Update()
{
//アクションからコントローラの入力値を取得
Vector2 move = _moveAction.ReadValue<Vector2>();
Vector2 look = _lookAction.ReadValue<Vector2>();
bool fire = _fireAction.triggered;
Debug.Log(string.Format("move:{0} + Look:{1} + Fire:{2}", move, look, fire));
}
}
Startで、PlayerInputのInspector上で設定しているDefault Mapのアクションを取得し、更新部で必要になったらそれぞれの入力値を取得しています。_fireは押した瞬間を取得したいためTriggeredを使用していますが、毎回押しているか判断する場合は_fireAction.ReadValue<float>で0~1のfloat値を取得することも可能です。
次のようにコールバックを受け取って記述することもできます。
using UnityEngine;
using UnityEngine.InputSystem;
public class InputTest : MonoBehaviour
{
private InputAction _fireAction;
public void Start()
{
var pInput = GetComponent<PlayerInput>();
var actionMap = pInput.currentActionMap;
actionMap["Fire"].performed += ClickFire;
}
private void ClickFire(InputAction.CallbackContext context)
{
Debug.Log("Fire!");
}
Behaviour = InvokeUnityEventの時
Behaviour = InvokeUnityEventにするとInspector上からメソッドを指定して呼び出すこともできます。
using UnityEngine;
using UnityEngine.InputSystem;
public class InputTest : MonoBehaviour
{
public void Fire(InputAction.CallbackContext context)
{
Debug.Log("Fireが実行した");
}
}
GameObjectにInputTestコンポーネントを追加します。その後、PlayerInputのInspecterでBehaviourをInvoke Unity EventsにするとInputActionsに設定したEvent一覧が表示されるので、Events > Player > Fire(CallbackCotext)に、Capsuleをドロップし、GameObject > InputTest > Fireをセット。
これで準備は完了で、プレイモードにてマウス右クリック(InputActionsのFireに設定している入力の一つ)するとログが出力されています。
PlayerInputを使用しない方法
コンポーネントを使用せず、直接InputActionsにアクセス可能です。ActionInputを作成した後、InputSystem.inputactionsのInspecterにてGenerate c# classにチェックを入れます。ClassFileの場所、Class名、Namespace名を指定の上、Applyボタンを押すとInpuaActionsを最適化したc#ファイルが作成されます。
using UnityEngine;
using UnityEngine.InputSystem;
public class InputAll : MonoBehaviour, InputActionData.IPlayerActions
{
private InputActionData _input;
private void Awake()
{
_input = new InputActionData();
//マップ「Player」のコールバックを全て登録
_input.Player.SetCallbacks(this);
}
//マップに対応する各アクションのコールバック
public void OnFire(InputAction.CallbackContext context)
{
if (context.performed)
{
Debug.Log("Fire :" + context.ReadValue<float>());
}
}
public void OnMove(InputAction.CallbackContext context)
{
if (context.performed)
{
Debug.Log("Move :" + context.ReadValue<Vector2>());
}
}
public void OnLook(InputAction.CallbackContext context)
{
if (context.performed)
{
Debug.Log("Look :" + context.ReadValue<Vector2>());
}
}
private void OnEnable()
{
_input.Enable();
}
private void OnDisable()
{
_input.Disable();
}
private void OnDestroy()
{
_input.Dispose();
}
}
InputActionDataとなっている箇所は出力したc#ファイル名と置き換えてください。
uGUiとの連携
uGuiを利用するとHierarchyにEventSystemが自動的に作られますが、EventSystemでは、旧来のInputとuGuiを結び付けるStandalone Input Moduleコンポーネントが付いています。Input Systemには反応しないため削除し、代わりにInput System UI Input Moduleコンポーネントを追加します。
InputActionsアセットを生成していれば自動的にそのアセットがセットされます。各操作とInputActionsアセットで作成した入力配置の結び付けを行えば完了です。PlayerInputコンポーネントのCreateで作成できるInputActionsアセットは良い感じでUIの各種操作と結びつけるActionsを予め作成しているのでお勧めです。
ゲームパッドの取得
接続されているゲームパッドを見つけるにはGamePadクラスを利用します。
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.DualShock;
public class InputTest : MonoBehaviour
{
public void Start()
{
//接続されている全てのゲームパッドを取得
var allGamepads = Gamepad.all;
//接続されている全てのPS4コントローラーを取得
var allPS4Gamepads = DualShock4GamepadHID.all;
}
}
入力の追跡
UnityのUPDATEは指定コンマ秒毎に、FixedUpdateはフレーム毎に呼び出されます。ですが、ゲームパッドは大抵それより速いタイミング毎に入力情報を渡します。
InputSystemは、それらの入力情報を保持する機能があります。スタックにどんどん入力情報を保持し、スタックが一定容量になったら古い情報からどんどん破棄します。次のように利用します。
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Utilities;
public class InputTest : MonoBehaviour
{
private InputAction _fireAction;
private InputActionTrace _trace;
public void Start()
{
var pInput = GetComponent<PlayerInput>();
var actionMap = pInput.currentActionMap;
//トレース生成
_trace = new InputActionTrace();
//トレース開始
_trace.SubscribeTo(actionMap["Fire"]);
}
public void OnDisable()
{
//トレース破棄
//溜まり続けるので必ず破棄が必要
_trace.Dispose();
}
public void Update()
{
foreach (var record in _trace)
{
float fire = record.ReadValue<float>();
Debug.Log(fire);
}
_trace.Clear();
}
}
トレースを開始したいタイミングで_trace.SubscribeTo(アクションマップ > アクション名)を入れることによりトレース開始。_traceはアクションに反応があった時にスタックに保持されていきます。例えばButton型の場合はボタンを押した時、Vector2型の場合は4つのボタンをうち、どれかが押された時など。
_trace.clear()を行うと、それまで溜めていたスタックを全て削除。新たにスタックに溜め始めます。また、オブジェクト破棄時は_trace.Disposeで破棄宣言をしておく必要があります。