プレイヤーの動き

Tutorial

intermediate

+10XP

45 mins

Unity Technologies

プレイヤーの動き

このチュートリアルでは、プレイヤーキャラクターの動きに関連する 3 つのシステム(入力、アニメーション、ナビゲーション)について学びます。また、自作のゲームにおいて、プレイヤーの動きに対するアクセシビリティの考慮についても取り上げます。

注:このチュートリアルでは、Out of Circulation の入力リマッピングの概要について説明します。

Languages available:

1. 概要

すべてのゲームがプレイヤーの移動を必要とするわけではありませんが、『Out of Circulation』のようなストーリー性のあるアドベンチャーゲームでは重要な特徴となっています。また、プレイヤーの動きはプレイヤーの入力に大きく影響されるため、様々なタイプの入力を使って探索できるような体験を実現するためには、アクセシビリティに関する配慮が必要でした。

このチュートリアルでは、次のことを行います:

  • 『Out of Circulation』でのプレイヤーの動きに関連する 3 つのシステムを評価する
  • 『Out of Circulation』でプレイヤーの移動パラメーターを調整し、その影響を評価する
  • 自作のゲームでプレイヤーの移動に関するアクセシビリティへの配慮を検討する


注:『
Out of Circulation』のハイレベルな詳細について、事前に記憶を呼び起こしたい場合は、Explore Out of Circulation をご覧ください。

2. アクセシビリティ要件

初期要件

『Out of Circulation』の制作を始めた当初は、プレイヤーの移動に関する具体的なアクセシビリティ要件は設定していませんでした。

ただし、入力のリマッピングに関する以下の要件は、プレイヤーの移動システムに大きな影響を与えました:

  • プレイヤー入力は、キーボード、マウス、ゲームパッドまたは代替入力のリマップが可能
  • プレイヤーは、ゲーム設定の入力セクションで、可能な各アクションに関連する好みのコントロールを実行時に設定できる必要がある
  • アナログハードウェアだけでなく、On-Screen Keyboard (OSK) 等のデジタルキーボードも使用できる


ゲームに入力のリマッピングを実装すると、プレイヤーは自分に最適なコントローラーを使用できるようになります。これは、プレイヤーに与える重要な選択肢です。

3. プレイヤーの動き:入力システム

『Out of Circulation』は、Unity の Input System のパッケージを使用しています。Input System は、プレイヤーキャラクターを動かしたり、NPC と対話するなど、ゲーム内のアクションを定義するために使用します。各アクションは、ボタンなら Boolean、方向なら Vector2 というように、特定のデータ型を持っています。Input System を使用すると、各アクションにコントロールのバインディングを追加することができます。このバインディングは、そのアクションのためのコントロールを指定します。

Input System は、アクションをコードから抽象化することで、アクションがバインドされているコントロールを知らなくても、アクションの値に対してクエリを実行できるという利点を提供します。この利点は、コードを変更することなくアクションを再バインドできることを意味します。アクションのコントロールを再バインドすることを入力のリマッピングと呼びます。

Input System は、各バインディングのオーバーライドをサポートしているので、オリジナルのバインディングは変更されずに残ります。つまり、コントロールはデフォルトのバインディングにリセットすることができます。Input System には、これらのオーバーライドを JSON ファイルに保存したり、読み込んでディスクに保存したりする機能も組み込まれています。

カスタムコード:SaveSystem.cs

『Out of Circulation』のために特別に書かれた入力関連のコードは、実行時にバインディングを作成する処理だけです。

実行時に(アクセシビリティ設定で)デフォルトのバインディングをオーバーライドしたい場合、プレイヤーは選択した入力制御を保存することができます。この場合、プロジェクトで作成したデフォルトは上書きされないので、必要であれば、プレイヤーはデフォルト値に戻せます。

オーバーライドデータは、SaveSystem スクリプトの SaveInput 関数で、カスタムの JSON ファイルに保存されます。これは、同じクラスの InternalInit 関数を使用して、初期化時にロードさ れます。

4. プレイヤーの動き:アニメーションシステム

NavMesh Agents を使用したアニメーションのキャラクターには、一般的に 3 種類の動き方があります:

  1. NavMeshAgent は、キャラクターを直接動かすことができる
  2. スクリプトは、NavMeshAgent からのパス情報を使ってキャラクターを移動させることができる
  3. アニメーションのルートモーションは、キャラクターを動かすことができる

この 3 つのアプローチは、バーティカルスライスで行ったように、分けて組み合わせることも可能です。

Sureswim の動き

『Out of Circulation』では、NavMeshAgent がプレイヤーキャラクターを動かしますが(アプローチ 1)、キャラクターの回転はスクリプトで制御し(アプローチ 2)、アニメーションはその動きに合わせようとします。

できるだけシンプルにするために、プレイヤーキャラクターのアニメーションは、静止しているときの idle アニメーションと、移動しているときの walk アニメーションの 2 つだけにしています。

この 2 つのアニメーションは、キャラクターの移動速度をブレンドパラメーターとする Blend Tree で一緒にブレンドされます。walk (歩行) アニメーションにはルートモーションがないため、アニメーションの中でキャラクターが歩いている速度を簡単に判断することはできません。つまり、キャラクターの実際の動きとアニメーションのやり方が一致しない可能性があることを意味します。

これに対処するため、AnimatorController の再生速度は、フレームごとに、以下に基づく計算で設定されます:

  • キャラクターが実際に動いている速度
  • アニメーションが動いているように見える速度を試行錯誤して決めたもの

5. プレイヤーの動き:ナビゲーションシステム

プレイヤーキャラクターのナビゲーションは、NavMesh システムに基づきます。プレイヤーキャラクターがシーン内を移動できるように、NavMesh Agent コンポーネントの目的地を設定する、専用の NavMeshAgentController スクリプトを作成しました。NavMeshAgentController がこの目的地を設定する方法は、使用されている入力デバイスによって異なります。NavMeshAgentController スクリプトは、どの入力タイプが使用されているかを検出し、そのタイプの入力に対してリッスンします。

ポインター入力

ポインター入力は、『Out of Circulation』が使用する最もシンプルな入力です。この処理は、InputActionAssetGameplay/Pointer Interact で定義した、CharacterControl という入力メソッドであれば、どの入力メソッドでも動作します。

プレイヤーがポインター入力でキャラクターを動かすと、以下のような現象が起きます:

  1. NavMeshAgentController スクリプトは、ポインターの入力(マウスのクリックなど)をリッスンし、ポインターの位置でシーンにレイキャストします。
  2. NavMeshAgentController スクリプトは、ヒットしたもの、特に地面や InteractiveObjects をチェックします。
  3. 地面がヒットした場合、NavMeshAgentController はヒットしたポイントを目的地として設定します。InteractiveObject がヒットした場合、 NavMeshAgentController はその InteractiveObject のインタラクションする位置を目的地として設定します。
  4. InteractiveObject が目的地として選択された場合、インタラクションが終了するまで他のナビゲーション入力がブロックされます。このブロックにより、インタラクションが終了する前に、プレイヤーが誤ってその場を離れることを防ぐことができます。

軸入力

キャラクターを動かすためのもう一つの処理は、矢印キーや WASD 操作、ジョイスティックなど、InputActionAsset で定義されている軸を使った入力です。

プレイヤーが軸を使った入力でキャラクターを動かすと次のような現象が起こります:

  1. NavMeshAgentController スクリプトは、入力用の軸値をベクトルとして読み取ります。
  2. スクリプトは、このベクトルをカメラに対するシーンのワールド空間のベクトルに変換します。
  3. 次に、そのベクトルの方向にプレイヤーキャラクターの位置から特定の近距離に移動先を設定します。
  4. 前の 3 つのステップは毎フレーム起こるので、移動先は常に更新されることになります。その結果、スムーズな動きを実現することができます。

6. 軸入力による相互作用の意図

プレイヤーがさまざまな入力コントローラーを使えるようにするだけでは十分ではありません。それらのプレイヤーにとってのユーザー体験は、デフォルトでサポートされているコントローラーでのユーザー体験と同じくらい良いものでなければならないのです。プレイヤーがゲームにアクセスするさまざまな方法を平等にサポートしなければ、デフォルトの入力コントローラーを使用しないプレイヤーに、実際にゲームを平等に体験させることはできません。

プレイヤーが操作しようとするインタラクティブオブジェクトを正しく認識することは、『Out of Circulation』の軸入力をうまくサポートするための重要なポイントです。この識別システムがうまく働かないと、軸入力を使うプレイヤーのユーザー体験は、ポインター入力を使うプレイヤー体験よりもかなり劣ることになります。

InteractiveObject のチャレンジ

NPC や収集可能アイテム等の InteractiveObject とインタラクションするために軸入力を使用する場合、対処しなければならない特定の複雑さがあります。

『Out of Circulation』では、ゲームプレイの各シーンでプレイヤーがインタラクトできるさまざまな InteractiveObject が存在します。そこで、プレイヤーがどの InteractiveObject とインタラクトしようとしているのか、その時々に最も可能性の高いものを判断するシステムが必要でした。このシステムによって、プレイヤーにそのオブジェクトをハイライト表示することができるようになります。プレイヤーがオブジェクトとインタラクトする意図を示す要因はさまざまであり、このシステムの実装と改良は困難です。

基本的な計算方法

プレイヤーの意図を計算する最初の段階として、いくつかのベクトルが作成されます。作成されるベクトルは以下のとおり:

  • ワールド空間におけるプレイヤーの入力を表すベクトル
  • プレイヤーキャラクターから、シーン内の各 InteractiveObject へのベクトル

次に、システムはプレイヤーがどの InteractiveObject とインタラクトしたいかを判断し、そのオブジェクトをハイライト表示する必要があります。

このオブジェクトを決定するために、システムは以下の分析を行います:

  1. シーン内の各 InteractiveObject の 2 つのベクトル間の角度
  2. プレイヤーキャラクターとシーン内の各 InteractiveObject との間の距離

注:この仕組みを正確に復習しなくても、システムの機能を理解することは可能です。ただし、計算内容を確認したい場合は、NavMeshController のスクリプトで確認することができます。関数名は FindDesiredInteractableIndexForAxisInput です。

プレイヤーの意図を汲み取る

Unity のシステムでは、ベクトル間の角度が小さく、プレイヤーキャラクターとインタラクティブオブジェクトとの距離が短い場合、プレイヤーはオブジェクトとのインタラクションを強く意図していると判断することができます。

逆に、あるオブジェクトに対するプレイヤーの意志が弱い場合は、ベクトル間の角度が大きく、プレイヤーキャラクターとインタラクティブオブジェクトとの距離が大きくなることで意志があることがわかります。

下図では、InteractiveObject を番号付きのボックスで表示しています。システムは、プレイヤーがこのダイアグラムの InteractiveObject 3 とインタラクションする可能性が最も高いと判断することになります。これは、最小の角度と最短の距離を伴うからです。

距離と角度の値から計算を行い、システムはプレイヤーがインタラクトする最も強い意思を示した InteractiveObject をハイライト表示します。

注:プレイヤーが InteractiveObject とインタラクションを行うかどうかに関わらず、最も可能性の高い InteractiveObject をハイライトするために、同じ計算を行わなければなりません。

7. 軸入力によるインタラクションに関するユーザーフィードバック

ユーザーからのフィードバックは、設計・開発プロセスにおいて非常に重要です。最終的なバーティカルスライスのプレイヤーの動きを実現するために、何度もユーザーからのフィードバックが必要でした。

プレイヤーの移動システムについては、最初の作業を終えた後、マイルストーンとなるビルドに含めて確認しました。その結果、プレイヤーによって、このシステムがどのように機能するかについて、異なる期待を持っていることが明らかになりました。そこで、このシステムを改善するために、2 つの重要なアクションを確認しました:

  1. テスターの中には、シーン内の InteractiveObject ごとに 2 つのベクトル間の角度を計算することが最も重要な要素であると考える人もいました。他のテスターは、ベクトル間の距離をより重要視すべきだと考えました。私たちは、重み付けシステムを改良できるようにする必要がありました。
  2. プレイヤー入力だけでは、InteractiveObject に対する強い意志を判断することができない場合がありました。また、プレイヤーキャラクターが向いている方向も考慮する必要がありました。

角度と距離に対する洗練された重み付け

2 つの異なるユーザー視点の妥協点を見つけられるように、angleAddionalInteractionWeighting という public 変数を使って、比較可能なもの(システムが計算した結果)に重み付けをしました。

プレイヤーキャラクターの前方方向

プレイヤーキャラクターの Transform の前方ベクトルを考慮するように調整しました。この調整により、プレイヤーキャラクターの向いている方向がシステムの計算に組み込まれるようになりました。

システムは、プレイヤー入力で適切な InteractiveObject が見つからなかった場合、プレイヤーキャラクターの前方方向を使用するように戻します。このとき、システムは入力ベクトル(m_InputInWorldSpace 変数)ではなく、プレイヤーキャラクターの前方ベクトルを使って計算を行います。

8. 探索:NavMeshController の public 変数を設定する

『Out of Circulation』のプレイヤーの動きに関する public 変数について、少し時間をかけて試してみましょう。テストの一環として行ったように、変数を設定するには:

1. 『Out of Circulation』の Unity プロジェクトを開きます。

2. 好きなゲームプレイのシーンをロードして、Play モードに入ります。

3. Hierarchy で DontDestroyOnLoad ゲームオブジェクトを展開し、子ゲームオブジェクトである Character(Clone) を選択します。

4. Inspector で、Nav Mesh Agent Controller (Script) コンポーネントを見つけます。


5.
変数で実験し、調整がプレイヤー体験に与える影響を考慮してください。

プレイヤーの移動変数

以下の public 変数が設定可能です:

  • Max Angle Of Interaction:(1) プレイヤーキャラクターが向いている方向、または (2) プレイヤー入力とキャラクターからインタラクション可能なオブジェクトまでの方向との間に許容される最大角度のこと。この値より大きな角度を持つオブジェクトは、インタラクションやハイライトの対象にはなりません。
  • Max Distance Of Interaction:軸入力で登録され、インタラクションやハイライトの対象となるオブジェクトの、プレイヤーキャラクターと InteractiveObject の間の最大許容距離です。
  • Angle Additional Interaction Weighting:(1) プレイヤーキャラクターが向いている方向、または (2) プレイヤー入力とキャラクターからインタラクティブなオブジェクトへの方向との間の小さな角度の重要性を判断します。この値を大きくすると、小さな角度の重要度が増します。
  • Axis Input Destination Distance:軸入力使用時に、スクリプトが NavMeshAgent の移動先を設定する際の、プレイヤーキャラクターからの距離を指定します。
  • Max Path Angle Error:プレイヤーキャラクターの意図する進行方向と、計算されたパスの方向との間に許容される最大角度を決定します。例えば、プレイヤーの入力が壁に突き当たる場合、その壁の反対側を目的地として設定することができます。この状況では、計算されたパスが入力の方向と全く異なるため、プレイヤーが望まないパスとなる可能性があります。この値以上に入力と異なる角度を持つパスをシステムが設定することはできません。
  • Orientation Interpolation Speed:プレイヤーキャラクターが進行方向を向く速度(度/秒)
  • Teleport To Interactable Object Distance:プレイヤーキャラクターと InteractiveObject との距離で、(1) キャラクターのパスがキャンセルされ、(2) キャラクターがオブジェクトにスナップされる距離です。プレイヤーキャラクターの正確な位置関係が重要なゲームでは有効です。

9. 拡張:リマップ操作のリマインダーの実装

このプロジェクトでコントロールのリマッピングについて詳しく知りたい場合は、画面上でコントロールリマインダーを実装することができます(Unity のスコープを減らす要件)。

このリマインダーを実行するには:

  • ControlManager クラスから利用できる Input Action Asset のバインディングを照会します。
  • バインドされた入力コントロールを含む文字列を小さなリマインダー UI ウィンドウに表示し、プレイヤーは Action の GetBindingDisplayString 関数を使用してオン/オフを切り替えることができます。


ヒント:
より詳しいガイダンスが必要な場合は、ゲーム設定の入力リマップタブ用に作成したコードを再利用してこれを行うことができます。このコードは UIControlSettingBase クラスにあります。

10. ゲーム内における入力のリマッピング

ご自身のゲームに取り組む際に、以下のような質問を考えてみてください:

  • Scoping for remappable input『Out of Circulation』のプレイヤーのアクションはシンプルなので、入力をリマップできるようにすることはプロジェクトのスコープ内でした。プレイヤーはどのようなアクションを行うのでしょうか。リマップ可能なコントロールを実装するために、プロジェクトのスコープを十分に確保しましたか?
  • Control remindersプレイヤーは、ゲームプレイ中にリマップされたものを含むゲームコントロールのリマインダーを表示することができるのでしょうか?『Out of Circulation』には搭載していませんが、私たちの仕事の延長線上で追加されたのかもしれません。
  • Data persistence:『Out of Circulation』のリマップされたコントロールは、ゲームビルドをプレイしていても、Unity エディターでテストしていても、複数のセッション間で保存されます。プレイヤーがセッション間でリマップしたコントロールを保存できるように、ゲームにデータ永続性を実装する予定ですか?データ永続性について詳しく知りたい方は、Junior Programmer ミッションの Manage scene flow and data から始めてみてはいかがでしょうか。
  • Control sensitivity『Out of Circulation』にはコントロールの感度がありません。もしこれをゲームに実装するのであれば、さまざまなニーズを持つプレイヤーが異なる入力コントロールを使ってカスタマイズできるようにするために、どのように工夫しているのでしょうか?


重要:
これらの質問に対する回答は、定期的なテストや障害を持つプレイヤーからのフィードバックの代わりとなるものではありません。

その他のリソース

プレイヤーの動きと入力マッピングのアクセシビリティについて詳しく知りたい方は、アクションマッピングと入力のインタラクションに関する詳細なガイダンスが含まれる SpecialEffect DevKit から始めてください。

また、このコースの前半で調べたアクセシビリティのリソースも参照できることを覚えておいてくださいね。

11. 次のステップ

Design and development」の他のチュートリアルでは、『Out of Circulation』の開発の他の側面について知ることができます。準備ができたら「Continue your journey」に進んでください。

Complete this Tutorial