Unity開発者が犯す10の最も一般的な間違い

公開: 2022-03-11

Unityは、マルチプラットフォーム開発に使用できる優れた簡単なツールです。 その原理は理解しやすく、直感的に製品の作成を開始できます。 ただし、いくつかのことを考慮しないと、最初のプロトタイプフェーズから移行するか、最終リリースに近づくため、作業を次のレベルに進めるときに進行が遅くなります。 この記事では、最も一般的な問題を克服する方法と、新規または既存のプロジェクトの根本的な間違いを回避する方法についてアドバイスします。 この記事の視点は3Dアプリケーション開発に重点を置いていますが、言及されているすべてが2D開発にも当てはまることに注意してください。

Unityは、マルチプラットフォーム開発に使用できる優れた簡単なツールです。
つぶやき

よくある統一の間違い#1:プロジェクト計画フェーズを過小評価する

プロジェクトごとに、プロジェクトのアプリケーション設計とプログラミングの部分を開始する前に、いくつかのことを決定することが重要です。 製品マーケティングがプロセス全体の重要な部分である最近では、実装されたアプリケーションのビジネスモデルがどのようなものになるかを明確に把握することも重要です。 製品をリリースするプラットフォームと、計画に含まれるプラットフォームを確認する必要があります。 また、サポートされる最小限のデバイス仕様を設定して(古いローエンドデバイスをサポートするのか、それとも最近のモデルをサポートするのか)、どのようなパフォーマンスとビジュアルを提供できるかを把握する必要があります。 この記事のすべてのトピックは、この事実に影響されています。

より技術的な観点からは、アセットとモデルを作成するワークフロー全体を事前に設定し、それらをプログラマーに提供する必要があります。特に、モデルにさらに変更や改良が必要な場合の反復プロセスに注意してください。 3Dアーティストがモデルの最大解像度と、LODバリエーションの数を知ることができるように、必要なフレームレートと頂点バジェットについて明確なアイデアを持っている必要があります。 また、一貫したスケールを持つようにすべての測定値を統合し、アプリケーション全体でプロセスをインポートする方法も指定する必要があります。

レベルの分割はパフォーマンスに大きく影響するため、レベルの設計方法は将来の作業にとって非常に重要です。 新しいレベルを設計するときは、パフォーマンスの懸念を常に念頭に置く必要があります。 非現実的なビジョンを持って行かないでください。 「それは合理的に達成できるか」という自問自答は常に重要です。 そうでない場合は、ほとんど達成できないことに貴重なリソースを浪費するべきではありません(もちろん、それを主要な競争上の優位性として持つことがビジネス戦略の一部ではない場合)。

よくあるUnityの間違い#2:最適化されていないモデルでの作業

さらに変更を加えることなくシーンで使用できるように、すべてのモデルを十分に準備しておくことが重要です。 良いモデルが満たすべきことがいくつかあります。

スケールを正しく設定することが重要です。 これらのアプリケーションが使用している単位が異なるため、3Dモデリングソフトウェアからこれを正しく設定できない場合があります。 すべてを正しくするには、モデルのインポート設定でスケール係数を設定し(3dsMaxとModoの場合は0.01のまま、Mayaの場合は1.0に設定)、スケール設定を変更した後にオブジェクトを再インポートする必要がある場合があることに注意してください。 これらの設定により、シーンで基本スケール1,1,1のみを使用して、一貫した動作を実現し、物理的な問題を発生させないようにする必要があります。 動的バッチ処理も正しく機能する可能性が高くなります。 このルールは、メインオブジェクトだけでなく、モデル内のすべてのサブオブジェクトにも適用する必要があります。 オブジェクトの寸法を微調整する必要がある場合は、Unityではなく3Dモデリングアプリケーションで他のオブジェクトに関して調整してください。 ただし、Unityでスケールを試して適切な値を見つけることはできますが、最終的なアプリケーションと一貫したワークフローのために、Unityにインポートする前にすべてを十分に準備しておくことをお勧めします。

オブジェクトの機能とその動的な部分に関しては、モデルを適切に分割してください。 サブオブジェクトが少ないほど良いです。 アニメーションの目的やその他の相互作用のために、動的に移動または回転する場合など、必要な場合に備えてオブジェクトの部分を分離します。 すべてのオブジェクトとそのサブオブジェクトは、その主な機能に関して、ピボットを適切に位置合わせして回転させる必要があります。 メインオブジェクトのZ軸は前方を向き、ピボットはオブジェクトの下部に配置して、シーンにより適切に配置する必要があります。 オブジェクトにはできるだけ少ないマテリアルを使用します(これについては以下で詳しく説明します)。

すべてのアセットには、そのタイプと機能を簡単に説明できる適切な名前を付ける必要があります。 すべてのプロジェクトを通じてこの一貫性を維持してください。

よくあるUnityの間違い#3:相互依存するコードアーキテクチャの構築

Unityの機能のプロトタイピングと実装は非常に簡単です。 他のオブジェクトへの参照を簡単にドラッグアンドドロップし、シーン内のすべてのオブジェクトをアドレス指定し、そのオブジェクトが持つすべてのコンポーネントにアクセスできます。 ただし、これは潜在的に危険な場合もあります。 顕著なパフォーマンスの問題(階層内のオブジェクトの検索とコンポーネントへのアクセスにはオーバーヘッドがあります)に加えて、コードの一部を相互に完全に依存させることにも大きな危険があります。 または、アプリケーションに固有の他のシステムやスクリプトに依存している、あるいは現在のシーンや現在のシナリオに依存している。 よりモジュール化されたアプローチを取り、アプリケーションの他の部分で使用したり、アプリケーションポートフォリオ全体で共有したりできる再利用可能なパーツを作成してみてください。 ナレッジベースを構築するのと同じ方法で、UnityAPIの上にフレームワークとライブラリを構築します。

これを確実にするためのさまざまなアプローチがあります。 良い出発点は、Unityコンポーネントシステム自体です。 特定のコンポーネントがアプリケーションの他のシステムと通信する必要がある場合、問題が発生する可能性があります。 このために、インターフェースを使用して、システムの一部をより抽象的で再利用可能にすることができます。 または、メッセージングシステムを作成するか、他のシステムの一部にリスナーとして直接登録することにより、範囲外の特定のイベントに対応するためのイベント駆動型アプローチを使用できます。 正しいアプローチは、gameObjectプロパティをプログラムロジック(少なくともモデルコントローラーの原則のようなもの)から分離しようとすることです。これは、位置や回転などの変換プロパティを変更しているオブジェクトを特定するのが難しいためです。 それは専らその管理者の責任であるべきです。

すべてを十分に文書化するようにしてください。 長い時間をかけてコードに戻る必要があるように常に扱い、コードのこの部分が何をしているのかをすばやく理解する必要があります。 実際には、しばらくするとアプリケーションの一部にたどり着くことがよくあり、問題にすばやく飛び込むための不必要な障害になります。 しかし、これをやり過ぎないでください。 場合によっては、適切なクラス、メソッド、またはプロパティ名で十分な場合があります。

よくあるUnityの間違い#4:パフォーマンスの浪費

携帯電話、コンソール、またはデスクトップコンピュータの最新の製品ラインは、パフォーマンスを気にする必要がないほど高度になることはありません。 パフォーマンスの最適化は常に必要であり、市場に出回っている他のゲームやアプリケーションと比較して、ゲームやアプリケーションの外観を変えるための基盤を提供します。 ある部分でパフォーマンスをいくらか節約すると、それを使用してアプリケーションの他の部分を磨くことができるからです。

最適化の領域はたくさんあります。 このトピックについての表面をかじるだけで、記事全体が必要になります。 少なくとも、私はこのドメインをいくつかのコア領域に分割しようとします。

ループの更新

更新ループでパフォーマンスを重視するものを使用せず、代わりにキャッシュを使用してください。 典型的な例は、シーン内のコンポーネントやその他のオブジェクトへのアクセス、またはスクリプト内の集中的な計算です。 可能であれば、すべてをAwake()メソッドにキャッシュするか、アーキテクチャをよりイベント駆動型のアプローチに変更して、必要なときにトリガーします。

インスタンス化

非常に頻繁にインスタンス化されるオブジェクト(たとえば、FPSゲームの弾丸)の場合、事前に初期化されたオブジェクトのプールを作成し、必要なときにすでに初期化されているものを選択してアクティブ化します。 次に、不要になったときに破棄するのではなく、非アクティブにしてプールに戻します。

レンダリング

オクルージョンカリングまたはLODテクニックを使用して、シーンのレンダリングされた部分を制限します。 最適化されたモデルを使用して、シーンの頂点数を制御できるようにしてください。 頂点の数は、モデル自体の頂点の数だけでなく、法線(ハードエッジ)、UV座標(UVシーム)、頂点の色などの他の要素の影響を受けることに注意してください。 また、シーン内の動的なライトの数は全体的なパフォーマンスに劇的な影響を与えるため、可能な限り事前にすべてをベイク処理するようにしてください。

コールを引く

ドローコールの数を減らすようにしてください。 Unityでは、静止オブジェクトには静的バッチを使用し、移動オブジェクトには動的バッチを使用することで、描画呼び出しの削減を実現できます。 ただし、最初にシーンとモデルを準備する必要があり(バッチ処理されたオブジェクトは同じマテリアルを共有する必要があります)、動的オブジェクトのバッチ処理は低解像度モデルでのみ機能します。 または、バッチ処理を使用する代わりに、スクリプトによってメッシュを1つに結合することもできます( Mesh.CombineMeshes )が、一部のプラットフォームでビュー錐台カリングを利用できない大きすぎるオブジェクトを作成しないように注意する必要があります。 一般的に、重要なのは、できるだけ少ない素材を使用し、シーン全体でそれらを共有することです。 異なるオブジェクト間で1つのマテリアルを共有できるようにするには、テクスチャからアトラスを作成する必要がある場合があります。 また、シーンライトマップテクスチャの高解像度(生成された解像度ではなく、テクスチャ出力解像度)を使用して、大規模な環境でライトをベイク処理するときにその数を減らすこともお勧めします。

オーバードローの問題

塗りつぶし率の問題が発生するため、不要な場合は透明なテクスチャを使用しないでください。 木や茂みなど、複雑で遠くのジオメトリに使用しても問題ありません。 使用する必要がある場合は、アルファテストを使用するシェーダーではなく、モバイルプラットフォーム用のカットアウトシェーダーではなく、アルファブレンドシェーダーを使用してください。 これらの問題を一般的に特定するには、アプリケーションの解像度を下げてみてください。 それが役立つ場合は、これらのフィルレートの問題があるか、シェーダーをさらに最適化する必要がある可能性があります。 そうしないと、メモリの問題が増える可能性があります。

シェーダー

パフォーマンスを向上させるためにシェーダーを最適化します。 パスの数を減らし、精度の低い変数を使用し、複雑な数学計算を事前に生成されたルックアップテクスチャに置き換えます。

ボトルネックを特定するには、常にプロファイラーを使用してください。 それは素晴らしいツールです。 レンダリングには、すばらしいフレームデバッガーを使用することもできます。これは、レンダリングプロセスを分解するときに、一般的にどのように機能するかについて多くのことを学ぶのに役立ちます。

よくあるUnityの間違い#5:ガベージコレクションの問題を無視する

ガベージコレクター(GC)自体が非常に効率的で、プログラミングの重要なことに集中するのに役立つという事実にもかかわらず、明示的に知っておくべきことがいくつかあることを認識する必要があります。 GCの使用は無料ではありません。 一般に、GCが頻繁に起動して、フレームレートの急上昇によってパフォーマンスが低下するのを防ぐために、不要なメモリ割り当てを回避する必要があります。 理想的には、フレームごとに定期的に新しいメモリ割り当てが発生することはありません。 しかし、どうすればこの目標を達成できますか? これは実際にはアプリケーションアーキテクチャによって決定されますが、従うことができるいくつかのルールがあります。

  • 更新ループでの不要な割り当ては避けてください。
  • 単純なプロパティコンテナには構造体が割り当てられていないため、構造体を使用します。
  • 更新ループ内に作成するのではなく、配列、リスト、またはその他のオブジェクトのコレクションを事前に割り当てるようにしてください。
  • Unityは、理想的には最適化されていない古いバージョンのMonoを使用しているため(LINQ式やforeachループなど)、モノ問題のあるものの使用は避けてください(執筆時点では、バージョン2.6に変更され、ロードマップがアップグレードされています)。
  • Awake()メソッドまたはイベントで文字列をキャッシュします。
  • 更新ループで文字列プロパティを更新する必要がある場合は、文字列の代わりにStringBuilderオブジェクトを使用してください。
  • プロファイラーを使用して、潜在的な問題を特定します。

よくあるUnityの間違い#6:最後にメモリとスペースの使用を最適化する

プロジェクトの最初からアプリケーションのメモリとスペースの使用量が最も少ないことに注意を払う必要があります。これは、プレリリースフェーズの最適化を終了するときに行うのがより複雑になるためです。 モバイルデバイスでは、リソースが非常に不足しているため、これはさらに重要です。 また、インストールのサイズが100MBを超えると、かなりの量の顧客を失う可能性があります。 これは、セルラーネットワークのダウンロードが100 MBに制限されていることと、心理的な理由によるものです。 アプリケーションが顧客の貴重な電話リソースを浪費しない場合は常に優れており、サイズが小さい場合はアプリをダウンロードまたは購入する可能性が高くなります。

リソースドレイナーを見つけるために、エディターログを使用して、オーディオ、テクスチャ、DLLなどの個別のカテゴリに分割されたリソースのサイズを(新しいビルドごとに)確認できます。 より良い方向性のために、Unity Asset Storeにはエディター拡張機能があり、ファイルシステム内の参照されたリソースとファイルを含む詳細な要約を提供します。 実際のメモリ消費量はプロファイラーでも確認できますが、エディターまたはターゲットプラットフォーム以外でテストする場合は多くの不整合があるため、ターゲットプラットフォームでビルドするために接続するときにテストすることをお勧めします。

最大のメモリ消費者は、多くの場合テクスチャです。 スペースとメモリの使用量がはるかに少ないため、圧縮テクスチャを使用することをお勧めします。 すべてのテクスチャを2乗する、理想的には、両側の長さを2の累乗(POT)にしますが、UnityはNPOTテクスチャをPOTに自動的にスケーリングすることもできることに注意してください。 POT形式の場合、テクスチャを圧縮できます。 アトラステクスチャを一緒に使用して、テクスチャ全体を塗りつぶします。 場合によっては、追加のスペースとパフォーマンスを節約するために、シェーダーの追加情報にテクスチャアルファチャネルを使用することもできます。 そしてもちろん、シーンのテクスチャを可能な限り再利用し、良好な視覚的外観を維持できる場合は繰り返しテクスチャを使用するようにしてください。 ローエンドデバイスの場合、品質設定でテクスチャの解像度を下げることができます。 バックグラウンドミュージックなどの長いオーディオクリップには、圧縮オーディオ形式を使用します。

さまざまなプラットフォーム、解像度、またはローカリゼーションを扱っている場合、アセットバンドルを使用して、さまざまなデバイスまたはユーザーにさまざまなテクスチャのセットを使用できます。 これらのアセットバンドルは、アプリケーションのインストール後にインターネットから動的にロードできます。 このように、ゲーム中にリソースをダウンロードすることで、100MBの制限を超えることができます。

一般的な統一の間違い#7:一般的な物理学の間違い

シーン内でオブジェクトを移動するときに、オブジェクトにコライダーがあり、その位置を変更すると、エンジンが物理世界全体を再計算する必要があることに気付かない場合があります。 その場合は、 Rigidbodyコンポーネントを追加する必要があります(外力を使用したくない場合は、非キネマティックに設定できます)。

Rigidbodyを使用してオブジェクトの位置を変更するには、新しい位置が前の位置に従わない場合は常にRigidbody.positionを設定し、連続移動の場合はRigidbody.MovePositionを設定します。これは、補間も考慮に入れます。 変更する場合は、 Update関数ではなく、常にFixedUpdateで操作を適用してください。 それは一貫した物理的振る舞いを保証します。

可能であれば、メッシュコライダーではなく、球、ボックス、円柱などのゲームオブジェクトでプリミティブコライダーを使用します。 これらの複数のコライダーから最終的なコライダーを作成できます。 物理演算は、CPUオーバーヘッドがあり、プリミティブコライダー間の衝突の計算がはるかに高速であるため、アプリケーションのパフォーマンスのボトルネックになる可能性があります。 物理演算の精度がそれほど必要でない場合は、タイムマネージャで固定タイムステップ設定を調整して、物理演算の固定更新の頻度を減らすこともできます。

よくあるUnityの間違い#8:すべての機能を手動でテストする

プレイモードで実験して機能を手動でテストする傾向がある場合があります。これは、非常に楽しく、すべてを直接制御できるためです。 しかし、このクールな要素は非常に急速に減少する可能性があります。 アプリケーションが複雑になるほど、プログラマーは、アプリケーションが本来の意図どおりに動作することを保証するために、繰り返して考える必要のある面倒なタスクが増えます。 反復的で受動的な性質があるため、開発プロセス全体の最悪の部分になる可能性があります。 また、テストシナリオを手動で繰り返すのはそれほど楽しいことではないため、一部のバグがテストプロセス全体を通過する可能性が高くなります。

Unityには、これを自動化するための優れたテストツールがあります。 適切なアーキテクチャとコードの設計により、単体テストを使用して分離された機能をテストしたり、統合テストを使用してより複雑なシナリオをテストしたりすることができます。 実際のデータをログに記録し、それを目的の状態と比較するという、試行錯誤のアプローチ大幅に減らすことができます。

手動テストは間違いなく開発の重要な部分です。 しかし、その量を減らすことができ、プロセス全体をより堅牢で高速にすることができます。 自動化する方法がない場合は、テストシーンを準備して、解決しようとしている問題にできるだけ早く取り掛かることができるようにします。 理想的には、再生ボタンを押してから数フレーム後。 ショートカットまたはチートを実装して、テストに必要な状態を設定します。 また、テスト状況を分離して、問題の原因を確認します。 テストが蓄積されるときの再生モードでの不要な秒ごと、および問題をテストする初期バイアスが大きいほど、問題をまったくテストしない可能性が高くなり、すべてが正常に機能することを期待できます。 しかし、おそらくそうではありません。

Unityのよくある間違い#9:UnityAssetStoreプラグインを考えるとすべての問題が解決します

私を信じて; 彼らはしません。 一部のクライアントと仕事をしているとき、私は時々、すべての小さなことにアセットストアプラグインを使用するという過去の傾向や遺物に直面しました。 UnityAssetStoreに便利なUnity拡張機能がないという意味ではありません。 それらはたくさんあり、どれを選ぶか決めるのが難しいことさえあります。 しかし、すべてのプロジェクトで、一貫性を維持することが重要です。一貫性は、うまく合わないさまざまな部分を不当に使用することによって破壊される可能性があります。

一方、実装に時間がかかる機能の場合は、Unity Asset Storeの十分にテストされた製品を使用すると常に便利であり、開発時間を大幅に節約できます。 ただし、慎重に選び、最終製品に制御不能で奇妙なバグを多くもたらさない実証済みのものを使用してください。 5つ星のレビューは、最初の良い尺度です。

必要な機能を実装するのが難しくない場合は、それを絶えず成長している個人(または会社)のライブラリに追加するだけで、後ですべてのプロジェクトで使用できます。 そうすることで、知識とツールセットを同時に向上させることができます。

よくあるUnityの間違い#10:Unityの基本機能を拡張する必要がない

Unity Editor環境は、基本的なゲームのテストとレベルデザインには十分であるように見える場合があり、それを拡張するのは時間の無駄です。 しかし、私を信じてください、そうではありません。 Unityの大きな拡張の可能性は、さまざまなプロジェクトで解決する必要のある特定の問題にUnityを適応させることができることから生まれます。 これにより、Unityで作業するときのユーザーエクスペリエンスが向上するか、開発およびレベルデザインワークフロー全体が劇的にスピードアップします。 組み込みまたはカスタムのプロパティドロワー、デコレータドロワー、カスタムコンポーネントインスペクタ設定などの組み込み機能を使用しないこと、または独自のエディタウィンドウでプラグイン全体を構築しないことは残念です。

結論

Unityプロジェクトをさらに進める際に、これらのトピックがお役に立てば幸いです。 プロジェクト固有のものがたくさんあるので適用できませんが、より困難で具体的な問題を解決しようとするときは、いくつかの基本的なルールを念頭に置いておくと常に役立ちます。 プロジェクトでこれらの問題を解決する方法について、さまざまな意見や手順がある場合があります。 最も重要なのは、プロジェクト全体でイディオムの一貫性を保ち、チーム内の誰もが特定のドメインを正しく解決する方法を明確に理解できるようにすることです。


Toptal Engineeringブログでさらに読む:

  • Unity AI開発:有限状態マシンのチュートリアル