![こじゃら](/wp-content/uploads/avatars/avatar-0001-01.png)
![こじゃら](/wp-content/uploads/avatars/avatar-0001-01-dark.png)
Input Systemでローカルマルチを実装する方法はないの?
複数のコントローラーを繋いで画面分割して遊べるゲームを目指してるの…
![このは](/wp-content/uploads/avatars/avatar-0002-01.png)
![このは](/wp-content/uploads/avatars/avatar-0002-01-dark.png)
Player Input Managerを使えばこの辺が楽に出来るわ。
Input Systemでは、PCなどに複数のゲームパッドを繋いでゲームをプレイするローカルマルチプレイヤーに対応しています。
これは、Player InputおよびPlayer Input Managerコンポーネントを用いることで簡単に実現可能です。
次のような挙動が実現できます。
- 個別のコントローラーにプレイヤーを割り当てて操作可能にする
- 何かキーが押されたら参加する
- プレイヤーの入室・退室を検知する
- プレイヤー毎のカメラを画面分割表示する
本記事では、このようなローカルマルチをInput Systemで実現する方法を解説していきます。また、後半では画面分割する方法を解説し、Cinemachine使用下での分割方法も紹介します。
- Unity 2022.1.21f1
- Input System 1.5.1
- Cinemachine 2.9.7
この作品はユニティちゃんライセンス条項の元に提供されています
目次 非表示
前提条件
事前にInput Systemパッケージがインストールされ、有効化されているものとします。
ここまでの手順が分からない方は、以下記事を参考にセットアップを済ませてください。
また、Input Systemからの入力はPlayer Inputコンポーネントを通じて取得するものとします。
Player Inputコンポーネントの基本的な使い方は以下記事で解説しています。
プレイヤー側の準備
次のようにプレイヤーオブジェクトにPlayer Inputコンポーネントがアタッチされており、Player Inputから入力を取得して操作できるようなPrefabをセットアップしておきます。
![](/wp-content/uploads/2023/05/unity-input-system-local-multiplayer-1-1024x604.png)
本記事ではPlayer Inputおよび操作スクリプトのセットアップ手順は割愛します。
操作可能なプレイヤーPrefabが欲しい場合、以下記事の内容に従ってプレイヤーを実装し、プレイヤーオブジェクトをPrefab化してください。
Unity公式が提供しているStarter Assetsのプレイヤーでも可能です。
Starter Assetsの使い方は以下記事で解説しています。
また、以下記事の内容を実践すると、ユニティちゃんをPlayer Input経由で動かすことが可能になります。
複数プレイヤーを管理する仕組み
Player Input Managerコンポーネントによって複数プレイヤーのオブジェクトやコントローラーを管理できます。
Player Input Managerは個々のプレイヤーをPlayer Inputコンポーネントとして管理します。
![](/wp-content/uploads/2023/06/unity-input-system-local-multiplayer-2.png)
![](/wp-content/uploads/2023/06/unity-input-system-local-multiplayer-2-dark.png)
Player Inputがアタッチされたゲームオブジェクトがシーン上に配置されると、自動的にプレイヤーインデックスが割り振られ、入室扱いとなります。
オブジェクトが無効化されたり破棄されると、そのプレイヤーは退室扱いとなります。この時、インデックスは全体的に詰められます。
![](/wp-content/uploads/2023/06/unity-input-system-local-multiplayer-3.png)
![](/wp-content/uploads/2023/06/unity-input-system-local-multiplayer-3-dark.png)
各プレイヤーに割り当てるコントローラーのスキームは同一でも異なっていても構いません。
例えば、キーボード&マウス、ゲームパッド1、ゲームパッド2にそれぞれプレイヤーを割り当てることも可能です。
![](/wp-content/uploads/2023/06/unity-input-system-local-multiplayer-4.png)
![](/wp-content/uploads/2023/06/unity-input-system-local-multiplayer-4-dark.png)
Player Input Managerのセットアップ
まず、Player Input Managerコンポーネントを適当なゲームオブジェクトに追加します。
すると、次のような項目がインスペクターより設定可能になります。
![](/wp-content/uploads/2023/06/unity-input-system-local-multiplayer-5.png)
各設定項目については後述します。
実行結果
Player Input Managerコンポーネントがシーンに存在している状態で、Player Inputコンポーネントがアタッチされているオブジェクト(プレイヤーなど)がシーンに配置されると、Player Inputにはユーザーインデックスが割り当てられます。
インデックスは、Player InputコンポーネントのインスペクターのDebug > Userから確認できます。
![](/wp-content/uploads/2023/06/unity-input-system-local-multiplayer-6-1024x433.png)
Player Input Managerの各種設定
ここからは、Player Input Managerコンポーネントの各設定項目について解説していきます。
入退室通知の設定
プレイヤーオブジェクトが(追加されるなどで)有効化されると「入室(参加)」イベントが通知されます。
プレイヤーオブジェクトが無効化されると「退室」イベントが通知されます。
参考:Class PlayerInputManager| Input System | 1.5.1
通知方法はNotification Behaviour項目から以下4種類を選択できます。
Send Messages | SendMessage経由で通知する。「入室」はOnPlayerJoined、「退室」はすOnPlayerLeftイベントとして通知される。Player Inputコンポーネントがアタッチされているオブジェクトに受信用のスクリプトがアタッチされている必要がある。 |
Broadcast Messages | BoardcastMessage経由で通知する。Player Inputコンポーネントがアタッチされているオブジェクトまたは子オブジェクトに受信用のスクリプトがアタッチされている必要がある。 |
Invoke Unity Event | UnityEvent経由で通知する。「入室」はPlayerInputManager.playerJoinedEventプロパティ、「退室」はPlayerInputManager.playerLeftEventプロパティ。 |
Invoke C Shard Events | C#標準のデリゲート経由で通知する。「入室」はPlayerInputManager.onPlayerJoinedプロパティ、「退室」はPlayerInputManager.onPlayerLeftプロパティ。 |
![](/wp-content/uploads/2023/06/unity-input-system-local-multiplayer-7.png)
参考:Class PlayerInputManager| Input System | 1.5.1
サンプルスクリプト
以下、UnityEvent経由で通知を受け取るスクリプトの例です。
上記をReceiveNotificationExample.csという名前で保存し、適当なゲームオブジェクトにアタッチします。
そして、Player Input ManagerのNotification BehaviourをUnity Eventに設定し、Events配下のそれぞれにメソッドを指定してください。
実行結果
プレイヤーが追加されると、「入室」通知のログが出力されます。プレイヤーを削除すると、「退室」通知のログが出力されます。
このとき、該当するプレイヤーのインデックスも一緒に表示しています。
スクリプトの説明
各プレイヤーオブジェクトのユーザー情報は、PlayerInput.userプロパティから取得できます。
これは、InputUser型の構造体で、インデックス以外にもユニークIDやデバイス情報なども格納しています。
参考:Struct InputUser| Input System | 1.5.1
プレイヤー入室方法の設定
Player Input Managerには、特定の条件を満たしたタイミングでプレイヤーを追加する機能を有しています。これは、プレイヤーのPrefabをInstantiateすることで実現します。
入室に関する設定は、Joining以下の項目から行います。
![](/wp-content/uploads/2023/06/unity-input-system-local-multiplayer-8.png)
参考:Class PlayerInputManager| Input System | 1.5.1
Join Behaviour項目には、プレイヤー(Prefab)を追加させる条件を指定します。
次の3種類の設定が可能です。
Join Players When Button Is Pressed | プレイヤーが割り当てられていないコントローラーのボタンが押された時に入室する。 |
Join Players When Join Action Is Triggered | 指定されたActionの入力があった時に入室する。 |
Join Players Manually | スクリプトから手動で入室させる設定。 |
参考:Enum PlayerJoinBehavior| Input System | 1.5.1
Player Prefab項目には、追加するプレイヤーのPrefabを指定します。Player Input Manager側から新しいプレイヤーを入室させる際には、このPrefabがシーン上にInstantiateされます。
Joining Enabled By Default項目には、初期状態でプレイヤーの入室を有効化するかを設定します。チェックが入っていると有効になります。
この項目が無効になっていると、Join Behaviourで指定された入室条件を満たしていてもPrefabが追加されないのでご注意ください。
Limit Number of Players項目にチェックを入れると、入室可能なプレイヤー数の上限を設定できるようになります。
![](/wp-content/uploads/2023/06/unity-input-system-local-multiplayer-9.png)
上限は、チェックを入れると現れるMax Player Count項目から設定できます。
以下、Prefabを設定するまでの例です。
例では、Join BehaviourにJoin Players When Button Is Pressedを指定し、未割り当ての任意ボタンが押されたら入室する設定としています。
実行結果
入室の条件を満たすと、プレイヤーがシーンに配置されるようになりました。
プレイヤー毎に異なるコントローラーから排他的に操作できるようになっていれば成功です。
各プレイヤーのPlayer Inputをインスペクターから見ると、別々のコントローラーが割り当てられていることが確認できます。
![](/wp-content/uploads/2023/06/unity-input-system-local-multiplayer-10-1024x460.png)
もし複数のコントローラーから1プレイヤーが制御可能になっている場合、Input Action AssetのControl Schemeの設定に問題がある可能性があります。
例えば、該当するActionのControl Schemeに複数のLayoutが設定されている場合などに起きます。
![](/wp-content/uploads/2023/06/unity-input-system-local-multiplayer-11.png)
この場合、SchemeをGamepadのみなど単一のLayoutにすると良いです。
画面分割設定
Player Input Managerでは、プレイヤー毎のカメラ分割機能もサポートしています。
カメラ分割を有効にするには、Split-Screen > Enable Split-Screen項目にチェックを入れます。
すると、詳細設定項目が出現します。
![](/wp-content/uploads/2023/06/unity-input-system-local-multiplayer-12.png)
設定内容の詳細については、後述する画面分割のセットアップ手順にて説明します。
画面分割する
Player Input Managerの画面分割機能を有効化すると、各プレイヤー毎の視点のカメラで画面分割することができます。
この手順について解説していきます。
カメラの配置
まず、プレイヤーを写すカメラオブジェクトを配置します。
プレイヤーをPrefabとしている場合、Prefabの子オブジェクトとして配置すれば良いでしょう。
この場合、例えばルートのプレイヤーオブジェクト直下にカメラオブジェクトとCharacter Controllerオブジェクトが存在する形になります。
![](/wp-content/uploads/2023/06/unity-input-system-local-multiplayer-13.png)
Player Input側の設定
Player InputコンポーネントのCamera項目に、前述のプレイヤー用カメラを指定します。
最終的に以下のようになっていれば良いです。
![](/wp-content/uploads/2023/06/unity-input-system-local-multiplayer-14.png)
画面分割方法の設定
画面分割の仕方は、Player Input ManagerコンポーネントのSplie-Screen以下の項目から設定します。
![](/wp-content/uploads/2023/06/unity-input-system-local-multiplayer-12.png)
Maintain Aspect Ratio項目は、画面分割する際にカメラのアスペクト比を維持する設定です。
Set Fixed Number項目は、分割表示する画面数を固定化する設定です。チェックを入れると、その下に出現するNumber of Screens項目から画面数を指定できます。
![](/wp-content/uploads/2023/06/unity-input-system-local-multiplayer-15.png)
例えば4を指定すると、最初から画面を4分割する前提でカメラ画面の表示領域が計算されます。
Screen Rectangle項目には、画面全体の表示領域をビューポート座標として指定します。
![](/wp-content/uploads/2023/06/unity-input-system-local-multiplayer-16-1024x540.png)
初期値は画面全体を表す(0, 0) 〜 (1, 1)です。
実行結果
プレイヤーが入室すると、数に応じて適切に画面分割されているのを確認できます。
Cinemachine使用下で画面分割する
ここまで解説した方法では、Cinemachineを使わない純粋なUnityカメラを用いて画面分割を行っていました。
Cinemachineを使用した環境下でも画面分割は可能です。
ただし、次のような制約があります。
- プレイヤー数分のレイヤーが必要
- 例:P0、P1、P2、P3
- UnityカメラのCulling Maskにプレイヤー用のレイヤーが排他的に設定されている
- 例:P0が設定され、P1、P2、P3は未設定など
- Cinemachineカメラ(バーチャルカメラ)のレイヤー設定
- 例:P0
これは、Cinemachineで複数のUnityカメラを扱う方法に準拠した制約です。Cinemachineで複数カメラを機能させる仕組み、および基本手順を知りたい方は、以下記事をご覧ください。
以上を踏まえ、Cinemachine環境下でも機能させる手順を解説していきます。
Cinemachineカメラの設定
まず、プレイヤーPrefab配下のカメラにCinemachine Brainコンポーネントを追加します。
次に、プレイヤー用のCinemachineカメラ(バーチャルカメラ)を配置します。
ヒエラルキー左上の+アイコン > Cinemachine > Virtual Cameraより配置できます。
必要に応じて、カメラの追従設定を行います。
例では、FollowとLook At項目にプレイヤーオブジェクトを指定し、BodyにTransposerを指定し、Binding ModeにSimple Follow With World Upを指定して追従させることとします。
![](/wp-content/uploads/2023/06/unity-input-system-local-multiplayer-17.png)
プレイヤー専用レイヤーの定義
各プレイヤー毎のレイヤーを追加します。
例では4人対戦ゲームを想定し、P0、P1、P2、P3の4レイヤーを追加することとします。
![](/wp-content/uploads/2023/06/unity-input-system-local-multiplayer-18.png)
プレイヤーインデックスに応じたレイヤーを設定するスクリプトの実装
プレイヤー入室時に割り当てられるプレイヤーインデックスに応じたレイヤーを設定するスクリプトを実装します。
スクリプトの実装例は以下のようになります。
上記をPlayerCameraLayerUpdater.csとしてUnityプロジェクトに保存します。
スクリプトの適用
前述のスクリプトをプレイヤーPrefabにアタッチし、インスペクターよりPlayer InputとCinemachineカメラを設定します。
そして、各プレイヤーインデックスとレイヤーの対応テーブルを定義します。
![](/wp-content/uploads/2023/06/unity-input-system-local-multiplayer-19.png)
例では、レイヤーP0、P1、P2、P3のインデックスがそれぞれ7、8、9、10であるため、上記の設定にしています。
レイヤー値は数値を直接入力する必要がありますが、構造体やProperty Drawerを自作すると選択式でレイヤー値を指定することも可能です。
詳細は以下記事で解説しています。
Player Input Manager側のイベント通知設定
上記サンプルスクリプトでは、C#イベント経由で退室通知を受け取るため、Player Input ManagerコンポーネントのNotification Behaviour項目にはInvoke C Shard Eventsを指定してください。
実行結果
ここまでの手順を成功させると、動画のようにCinemachineが適用された状態でも独立してカメラが制御されることが確認できます。
この時、プレイヤーのUnityカメラのCulling Maskには、自身のレイヤーが設定され、他プレイヤーのレイヤーが未設定になります。
![](/wp-content/uploads/2023/06/unity-input-system-local-multiplayer-20.png)
また、Cinemachineカメラのレイヤーには自身に対応するレイヤーが設定されます。
![](/wp-content/uploads/2023/06/unity-input-system-local-multiplayer-21.png)
スクリプトの解説
自身のプレイヤーインデックスが更新される時、次の処理でレイヤー情報とカメラの取得を行います。
次に、自身のカメラのCulling Maskを更新します。この時、他プレイヤーのレイヤーは除外する必要があります。
そして、Cinemachineカメラのレイヤーを自身のものに設定します。
ここまでの処理は、自身が入室した時に行うほか、他プレイヤーが退室した時もインデックスがずれる可能性があるため行います。
さいごに
Input Systemでローカルマルチを実装したい場合、Player Input Managerコンポーネントを用いると比較的楽に実装できます。
ただし、プレイヤーはPlayer Inputコンポーネント経由で操作する必要があるという制約もあります。
また、画面分割もサポートしていることも特徴です。Cinemachine環境下でも実現可能です。