RTSゲームの経路探索システム

今回の主な話題：RTSゲームの経路探索システム、AssetBundleがパッケージ化された後のmd5は異なる、UGUIの3D  HPバー最適化の問題、シーン内の一部のオブジェクトの外観を早く変更する方法。

---
###経路探索システム
Q1: RTSゲームを作っています。UnityのNavMesh経路探索から始めましたが、ブロックとフラッシュの問題が常に発生するため、これはRTSマルチプレーヤー経路探索には適していません。Asset StoreのA *経路探索のコメントに、「衝突に問題があります」というコメントは書いてあります。比較的に通じるソリューションはありませんか？
>前のプロジェクトはRTSプロジェクトでした。最初に経路探索の問題を解決する過程で同じような疑問に遭いました。長い時間の変更、調査、調整の後、参照用にいくつかのポイントを共有します。

>**まずは結論：A Start Pathfinding Project Pro + Unity NavMeshの2層構造を採用しました。**

>１）主な経路探索スキームは、A Start Pathfinding Project Proプラグインの従来のメッシュ形式を使用し、ディープな変更と拡張しました。主に経路探索過程中の通過性は単位半径を考慮する必要があります。そうすると経路探索の精度を高くなされます。
>2）Unity NavMeshシステムを経路探索過程中の衝突システムとして使用します。あなたが言う通りに、この経路探索プラグインの衝突に問題があります。これについては後で説明します。

>上記の2つの部分は、「経路探索アルゴリズム、正確な衝突」を構成します。次に、上記のスキームの理由と詳細について説明します。

>**1．NavMeshの経路探索速度は速いですが、十分に正確ではありません。**RTSは従来の正方形のNavGrid経路探索に適しています。既存の経路探索アルゴリズムは、デフォルトで経路探索プロセスにおける経路探索ユニットの半径の影響を考慮することをサポートしていません。弊社のプランナーは「1メートル幅のギャップに2メートル幅のデブが通じる」のようなことに絶対許しませんから、Navmeshを使用して動的にブロッキングを生成すると、経路探索結果はありますが、衝突の問題で単位は大きいギャップに「シェイクする」可能性が高いです。これは自分でしか変更できないため、プラグインを購入して裏から外へ変更し、具体的にアルゴリズムの規則と変更された概略図を変更しました。こちらを参考できます：
http://www.cnblogs.com/yaukey/p/rts_unit_traverse_size_based_path_finding.html。
変更後、毎回経路探索しますとユニットの直径はパラメータの1つとして導入されます。

>このプラグインを使用してスレッドを2つくらい開いて経路探索を行うことは私が調整した理想的な結果であります。次のは、マップ内のさまざまなユニットの出産と死亡です。ユニットが静止している場合は、ブロックを生成する必要があります。移動しますとブロックを削除して通過性を保証すべきです。プラグインのDynamicObsculeコンポーネントを変更することにより（元の機能シンプルすぎ）達成します。具体的には、各ユニットに対応するサイズのCapsuleCollider空オブジェクトにこのプラグインを添付して、ユニットが立つ時このオブジェクトをユニットの位置に置いてアクティブさせます。ユニットが移動しますとこの空オブジェクトを隠します。注意すべきのは、このプラグインはマップ通過性のアップデートを導き、このアップデートはマルチスレッドであります。即時性は保証されていないため、同じフレームまたは次のフレームでは、コールバックイベントを使用して応答することを実行しないでください。こうすれば、マップ全体の通過性はユニットによりアップデートします。

>**2．経路探索の通過性は保証できになり、次は衝突の問題を解決します。**このプラグインはrvoアルゴリズムである衝突アルゴリズムが付属していますが、調整実験によると、その衝突は私たちの要求に満たしていません。ユニットの数が多く、混雑している場合、いくつかのユニットは立った後に常にオーバーラップします。最も許容できないのは、2つのユニットが完全にオーバーラップしていることです。私はここで多くの時間を費やしましたが、満足できる結果は修正してでられませんでした。後でrecastnavigationの作者が言ったことを思い出した。UnityのNavMeshはこれに基づいていますが、Detourシステムが大幅に書き換えられました、 Detourに巡航と衝突を含みます。実際の使用時に、NavMeshAgent間の衝突は非常に正確であることがわかりました。考えましたと、巧妙な方法は出てきました。地面にAStarプラグインの経路探索面積と同じサイズのNavMeshを配置します。ブロックはありません（純粋な青として理解できます）。同時に経路探索単位に一つのNavMeshAgentを添付します。しかし、このものは衝突のみを使用し、経路探索を使用しない（ブロックなくて、経路探索は2点直線であり、ほとんどコストがないと見なすことができる）ため、最後に実際の衝突効果は私たちの要求に大体満足できます。

>**以上は私が言った「2層構造」でした、そして問題を述べます。**

>1)開発時に使ったUnityバージョンはUnity 5.3.7-5.3.8で、プラグインバージョンはxでした。経路探索プラグインの基層にPhysics.CheckCapsuleコールはあるはずですが、マルチスレッドの多くのコール操作は、基層のPhysX物理エンジンの崩壊、ゲームの突然終了を導きます。必然ではありませんが、回数が増加しますと崩れやすくなります。プラグインの公式BBSにもこのバグに関する議論があります。後でこれを2つのPhysics.CheckSphereコールに置き換えました。2017バージョンはまだテストしていません。
2)1で使用した方法は、UWAパフォーマンスレポートで、Dynamic Colliderは毎回標準を超えました。これは自分で選択する必要がありますが、全体的に私たちのゲームのボトルネックは経路探索に集まることではなく、これらのテストでコストは比較的に理想であります。
3)プラグインのマルチスレッド更新により、たくさんの完了イベントコールバックがあり、基本的にこれを利用してゲームシステムとリンクします。足りない場合は自分で追加します、そうしないと更新が完了しない時にまだ間違い結果は出やすいです。
4)できれば内部のMonoメモリの割り当てを最適化し、ゲームシステムと組み合わせた後に採用の解放と切断することに注意してください。そうしないと、メモリリークが発生しやすくなり、経路探索プラグインが採用した後、多くのオブジェクトが解放できない状況を導きやすいです。
5)経路探索精度と衝突精度がゲーム内の表現、パラメーターを調整するためにより多くの時間を必要とします。たまたまに表現が悪いが、方法が悪いと言う意味ではありません。そして、リアルタイム同期に対する高い要求がある場合、このソリューションをさらに改善する必要があります。

---
###レンダリング
Q2: シーン内の一部のオブジェクトの外観をすばやく変更したいのですが、DrawCallへの影響、材質変更の時間コストなどを考慮すると、最も効率的な方法は何ですか。

>材質のみを変更する場合は、Colorの変更など、MaterialPropertyBlockを使用できます。

>```ruby:
puts 'The best way to log and share programmers knowledge.'![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/632473fc-6308-8556-be39-f9127d133bcd.png)
MaterialPropertyBlock mpb = new MaterialPropertyBlock();
    mpb.SetColor("_FillColor", Color.red); 
    GetComponent<MeshRenderer>().SetPropertyBlock(mpb);
```
MaterialPropertyBlockの変更効率は高いため、GPU Instacingと連携してDrawCallをマージすることもできます。 この記事を参照できます：[Material Property BlockでMaterial属性操作を差し替え](https://qiita.com/UWATechnology/items/e89c2372565705eeab76)

---
###アセット管理
Q3: Unity 5.6.5でAssetBundleパッケージツールを作成しました。Manifestを付属する最新のPipeline APIを使用しました。たとえば、Perfabをパッケージ化します。このPrefabにはTexture、Shader、スクリプトが含まれます。これで問題が発生します。Androidプラットフォームで、異なるコンピューターができたAssetBundleは異なります。UnityStudioで分析して、Shaderのmd5が異なることが原因です。何かいい方法はありますか？
①
>Unityはいつもこの問題があります。同じコンピューターであっても、Libraryを削除してから再生すると異なります。パッチ適用ツールの比較プロセスを共有しましょう。
最初に、SVNに基づいて2つのバージョンの違いがあるAssetBundlesを見つけ、範囲を絞り込みます。
次に、ABに対応するmanifestファイルを比較して、AssetHashが実際に変更されたかどうかを確認し、変更されていないファイルを除外します。同時にTypeTreeHashもチェックし、変更がある場合は警告を出します。

>ところで、このAPIにちょっと文句を言います。
https://docs.unity3d.com/ScriptReference/BuildPipeline.GetHashForAssetBundle.html
このAPIの原理は同じ名前のmanifest内のAssetHash値を直接読み取ることであり、このAPIがHashを計算するためのものであると誤解しやすいです。

②
>MD5の問題は常に存在しており、AssetBundleのHasidで直接判断することをお勧めします。

>こちらを参照してください：https://docs.unity3d.com/ScriptReference/BuildAssetBundleOptions.AppendHashToAssetBundleName.html

---
###UI
Q4: UGUI WorldSpace Canvasを使用して、クリープ（クリープは頻繁に移動し、数は数十個くらい）3D UI HPバーを作成しました。HPバーUIは、カメラからの距離に応じて適切なレンダリング順序を自動的に形成します。しかし、このようにすれば、各HPバーにはCanvasが必要であり、一つのCanvasは一つのDrawCallであります。だからすべてのHPバーを同じWorldSpace Canvasに統合し、すべてのHPバーUIに対してDrawCallが1つだけになるようにしますが、これは合理なオクルージョン関係を形成しません。

だから数フレームごとにすべてのHPバーUIとカメラ間の距離を計算して並び、transform.SetSiblingIndexを変更してオクルージョン順序の問題を模擬しますが、これによりCPU計算が増加します。では、数量の少し多い3D UI HPバーの最適化はどうせればいいですか？
>まず、UGUIでオクルージョンを変更したい場合、変更できるのはSiblingIndexのみです。並べ替えや変更が必要なHPバーの量が比較的多い場合は、かなりのオーバーヘッドが発生しやすくなります。 ただし、「数十」が大きすぎるかどうかを判断するのは困難です。このアプローチのパフォーマンスをローエンドマシンでテストすることをお勧めします。主な焦点は、距離、並べ替え、変更の総コストを計算することです。数フレームごとに行いますから、結果は1〜3msなら納得できます。

---
UWA Technologyは、モバイル/VRなど様々なゲーム開発者向け、**パフォーマンス分析**と**最適化ソリューション**及び**コンサルティングサービス**を提供している会社でございます。

UWA公式サイト：https://jp.uwa4d.com
UWA公式ブログ：https://blog.jp.uwa4d.com