レンダリングの最適化
旧トピック

スクリプトの最適化

このセクションでは、ゲームが使用する実際のスクリプトと最適化を行う方法を示しています。また、最適化が動作する理由と、なぜそれらを適用すると特定の状況で有効になるのか詳しく説明します。

プロファイラー は最も重要です

プロジェクトが円滑に実行されるように確認するボックスのリストのようなものはありません。遅いプロジェクトを最適化するには、プロファイリングして無駄に処理時間がかかる部分を特定する必要があります。プロファイリングせずに、つまり、処理結果を完全に理解せずに最適化しようとするのは、目隠ししながら最適化するようなものです。

内部のモバイルプロファイラー

どんなプロセス(物理的であったり、スクリプト、またはレンダリング)がゲームを遅くしているかを把握するために、ビルトインプロファイラーでのパフォーマンス測定を使用することができますが、実際の犯人を見つけるために、特定のスクリプトや方法を掘り下げるすることはできません。しかし、のゲームに特定の機能を無効にするスイッチを取り付けることにより、あなたはかなり最悪の犯人を絞り込むことができます。例えば、あなたが敵キャラクターの AI スクリプトを削除したら、フレームレートが 2 倍になったならば、あなたはそのスクリプト、または、それがゲームにもたらしたことを知り、最適化されなければならない。唯一の問題は、あなたが問題を見つける前に、異なる多くのものを試してみなければならないかもしれないことです。

モバイルデバイス上でのプロファイリングの詳細については、モバイルのための最適化実用ガイドを参照してください。

設計による最適化

最初から何かを高速に開発しようとすることは危険です。開発に時間をかけて最適化するか、最適化を考慮せず開発時間を短縮するかのトレードオフがあります。もし、後者を選んであまりにも動作が遅い場合、後で削除または更新しなければならなくなります。ゲームのための重要な最適化はゲームによって異なり、別の最適化に失敗する場合があるため、直感と、ハードウェアの知識で良好な意思決定をしましょう。

オブジェクトプーリング

スクリプトとゲームプレイ方法でよいゲームプレイとよいコード設計の間で交差する例としてのオブジェクトプーリングを提供しています。一時的なオブジェクトのためにオブジェクトプーリングを使うと、それらを作成、破棄するよりも高速です。メモリアロケーションをより簡易にし、動的メモリ割り当てとガベージコレクション作業を除けるからです。

メモリ割り当て

自動メモリ管理とは

Unity で書いたスクリプトは自動メモリ管理を使用しています。すべてのスクリプト言語はこれを行います。対照的に C や C++のような低水準言語は手動でメモリ割り当てをし、プログラマが直接メモリアドレスへの読み書きを許可します。そのため、作成するすべてのオブジェクトを削除する責任があります。C++でオブジェクトを作成する場合は、手動でメモリの割当てを解除する必要があります。スクリプト言語では、ObjectReference= NULL; と書けば十分です。

メモ GameObject myGameObject; または var myGameObject : GameObject; ようなゲームオブジェクト変数を使用している場合、myGameObject = null; としたとき、なぜそれは破壊されないのか?

  • ゲームオブジェクトはまだ Unity によって参照されます。なぜなら Unity は描画する、更新する、などのために参照を維持しなければなりません。 Destroy(myGameObject); の呼び出しでその参照とそのオブジェクトを削除します

しかし、あなたは Unity で何も考えずにオブジェクトを作成した場合、例えば、なんらかから継承していないクラスのインスタンス(対照的には、ほとんどのクラスまたは、MonoBehaviour から継承した“スクリプト·コンポーネント”)、その後、それに参照変数を null に設定して、実際に起こることは、あなたのスクリプトと Unity に関する限り、オブジェクトが失われるということです;彼らはそれにアクセスすることはできず、再びそれを見ることはなく、それはメモリに常駐してます。その後、いくつかの時間が経った後に、ガベージコレクタが実行され、どこからも参照されていないメモリ内容は削除されます。これは、舞台裏でメモリの各ブロックへの参照数とそのトラックを維持しているため行うことが可能でです。これがスクリプト言語が C++よりも遅い理由の一つです。

割り当てられたメモリを避ける方法

オブジェクトが作成されるたびにメモリが割り当てられます。非常に多くのコードでは、あなたはそれを知らなくてもオブジェクトを作成しています。

  • Debug.Log("boo" + "hoo"); オブジェクトを作成します。
    • 文字列を多く扱うとき、"" の代わりに System.String.Empty を使う。
  • イミディエイト(即時要求)モード GUI ( UnityGUI )は遅く、パフォーマンスが問題である状況では使用すべきではありません。
  • クラスと構造体の違い:

クラスはオブジェクトであり、レファレンスとして振る舞う。Foo がクラスの場合、

  Foo foo = new Foo();
  MyFunction(foo); 

その後、MyFunction は、ヒープに割り当てられた元の Foo オブジェクトへのレファレンスを受け取ります。MyFunction の内側の foo への変更は、目に見えるどこでも foo がレファレンスされるようになります。

クラスはデータであり、そのように振る舞います。Foo は構造体である場合、

  Foo foo = new Foo();
  MyFunction(foo); 

その後 MyFunctionfoo のコピーを受けます。foo はヒープに割り当てられることはなく、決してガベージが収集されることはありません。MyFunctionfoo のコピーに変更する場合、他の foo は影響を受けません。

  • 長い間近くにいるオブジェクトはクラスでなければなりません。そして、短命であるオブジェクトは構造体でなければなりません。Vector3 はおそらくもっとも有名な構造体です。もし、それがクラスなら、すべてがもっと遅くなるでしょう。

なぜオブジェクトプーリングは高速か

これの結論は、インスタンス化とデストロイの使用は、ガベージコレクタに多くのするべきことをやらせる ということです。これはゲームにおいて“支障”を引き起こすことがありえます。自動メモリ管理を理解するページが説明するように、インスタンス化とデストロイを囲んで、一般的なパフォーマンスでヒッチを回避する方法は他にもあります。何かが起こっていないとき手動でガベージコレクタをトリガーとし、または非常に多くの場合、未使用のメモリの大きなバックログを蓄積させないようにトリガーにします。

もう一つの理由はそれであり、具体的にはプレハブが最初にインスタンス化されたとき、時々の追加の物事が RAM にロードされなければならず、またはテクスチャやメッシュは、GPU にアップロードする必要があります。れは同様に支障を引き起こす可能性があり、オブジェクトプーリングとゲームを続ける代わりのレベルの負荷を発生します。

パペット(人形)の無限の箱を持っている「パペットマスター(人形使い)」を想像してみてください。キャラクターが表示されるスクリプトを呼び出すたびに、その人形の新しいコピーを箱から取り出し、ステージが終わるたびに、その現在のコピーを捨てます。オブジェクトプーリングは、ショーが始まる前にすべての人形を箱から出して、見えるべきでないときには舞台裏のテーブルの上ににすべての人形を置いておくのと同じようなものです。

なぜオブジェクトプーリングが遅くなってしまうのか

1つの問題は、プールの作成は他の目的で使うヒープメモリの量を減らしてしまうことです。あなたが作成したばかりのプールの上にメモリを割り当てる続けると、より頻繁にガベージコレクションをトリガーする可能性があります。それだけでなく、すべてのコレクションは遅くなります。収集に要する時間は生きているオブジェクトの数が増加するにしたがって増大するためです。これらの問題を念頭に置き、あなたが大きすぎるプールを割り当てた場合、またはそれらをアクティブに維持している場合、明らかにパフォーマンスが悪くなることがあります。さらにまた、多くの種類のオブジェクトは、オブジェクトをプールするのに役に立ちません。例えば、ゲームは、かなりの時間を持続し、または大量に出現する敵のためにスペルエフェクトを含んでいるが、ゲームが進むにつれて徐々にキルされていきます。このような場合では、パフォーマンスの利点をオーバーヘッドが大幅に上回るのでオブジェクトプールは使用すべきではありません。

実装

ここでは、単純な発射のための単純にスクリプトを並べて比較してます。ひとつはインスタンスを使用し、ひとつはオブジェクトプーリングを使用。

 // GunWithInstantiate.js                                                  // GunWithObjectPooling.js

 #pragma strict                                                            #pragma strict

 var prefab : ProjectileWithInstantiate;                                   var prefab : ProjectileWithObjectPooling;
                                                                           var maximumInstanceCount = 10;
 var power = 10.0;                                                         var power = 10.0;

                                                                           private var instances : ProjectileWithObjectPooling[];

                                                                           static var stackPosition = Vector3(-9999, -9999, -9999);

                                                                           function Start () {
                                                                               instances = new ProjectileWithObjectPooling[maximumInstanceCount];
                                                                               for(var i = 0; i < maximumInstanceCount; i++) {
                                                                                   // place the pile of unused objects somewhere far off the map
                                                                                   instances[i] = Instantiate(prefab, stackPosition, Quaternion.identity);
                                                                                   // disable by default, these objects are not active yet.
                                                                                   instances[i].enabled = false;
                                                                               }
                                                                           }

 function Update () {                                                      function Update () {
     if(Input.GetButtonDown("Fire1")) {                                        if(Input.GetButtonDown("Fire1")) {
         var instance : ProjectileWithInstantiate =                                var instance : ProjectileWithObjectPooling = GetNextAvailiableInstance();
             Instantiate(prefab, transform.position, transform.rotation);          if(instance != null) {
         instance.velocity = transform.forward * power;                                instance.Initialize(transform, power);
     }                                                                             }
 }                                                                             }
                                                                           }

                                                                           function GetNextAvailiableInstance () : ProjectileWithObjectPooling {
                                                                               for(var i = 0; i < maximumInstanceCount; i++) {
                                                                                   if(!instances[i].enabled) return instances[i];
                                                                               }
                                                                               return null;
                                                                           }




 // ProjectileWithInstantiate.js                                           // ProjectileWithObjectPooling.js

 #pragma strict                                                            #pragma strict

 var gravity = 10.0;                                                       var gravity = 10.0;
 var drag = 0.01;                                                          var drag = 0.01;
 var lifetime = 10.0;                                                      var lifetime = 10.0;

 var velocity : Vector3;                                                   var velocity : Vector3;

 private var timer = 0.0;                                                  private var timer = 0.0;

                                                                           function Initialize(parent : Transform, speed : float) {
                                                                               transform.position = parent.position;
                                                                               transform.rotation = parent.rotation;
                                                                               velocity = parent.forward * speed;
                                                                               timer = 0;
                                                                               enabled = true;
                                                                           }

 function Update () {                                                      function Update () {
     velocity -= velocity * drag * Time.deltaTime;                             velocity -= velocity * drag * Time.deltaTime;
     velocity -= Vector3.up * gravity * Time.deltaTime;                        velocity -= Vector3.up * gravity * Time.deltaTime;
     transform.position += velocity * Time.deltaTime;                          transform.position += velocity * Time.deltaTime;

     timer += Time.deltaTime;                                                  timer += Time.deltaTime;
     if(timer > lifetime) {                                                    if(timer > lifetime) {
                                                                                   transform.position = GunWithObjectPooling.stackPosition;
         Destroy(gameObject);                                                      enabled = false;
     }                                                                         }
 }                                                                         }


もちろん、大型で複雑なゲームのためにすべてのあなたのプレハブのために働く一般的な解決策を確認したくなるでしょう。

別の例: Coin Party!

スクリプトとゲームプレイ方法で与えられる“数百人の回転、ダイナミックな点灯、一度の画面上で集められたコイン”の例はどのようにスクリプトコードを書くか、セクションを実証するために使用されます。パーティクルシステムのような Unity コンポーネントやカスタムシェーダーは貧弱なモバイルハードウェアに負担をかけずに素晴らしい効果を作成するために使用できます。

この効果は大量のコインが落ち、バウンドして、回転する 2D の横スクロールのゲームの文脈で生かすことを想像してみてください。コインは動的にポイントライトに照らされています。私たちはゲームをより印象的にするために光きらめくコインをキャプチャしたい。

もし強力なハードウェアを持っていた場合、この問題への標準的なアプローチを使用することができます。すべてのコインオブジェクトを確認し、それぞれの頂点を照らしオブジェクトに陰影をつけ、前方または後方を照らし、その後、画像効果としてトップに輝きを追加し、キラキラと反射したコインが周辺に光を出力させます。

しかし、モバイルのハードウェアは多くのオブジェクトに窒息されるでしょう。グローエフェクトを適用するにはまったく問題外です。では、我々は何をしますか?

スプライトパーティクルシステムのアニメーション

あなたは、そのすべてが同じように移動し、プレーヤーによって慎重に検査できない多数のオブジェクトを表示する場合、あなたは時間がなくてもパーティクルシステムを使用してそれらを大量に描画することができるかもしれません。ここにこの技術のいくつかのステレオタイプのアプリケーションがあります:

  • 収集品やコイン
  • フライングデブリ
  • シンプルな敵の大群や群れ
  • 応援群集
  • 何百もの発射体や爆発

スプライトパッカーと呼ばれる無料のエディター拡張機能があり、アニメーションスプライトパーティクルシステムの作成が容易になります。テクスチャにあなたのオブジェクトのフレームをレンダリングし、次にパーティクルシステムにアニメーションスプライトシートとして使用することができます。私たちは回転するコインでそれを使用例として使用しています。

実装のレファレンス

スプライトパッカーに含まれているプロジェクトは、まさにこの問題に対する解決策を示す例です。

それは低いコンピューティング性能で見事な効果を達成するために、すべての異なる種類のアセットのファミリーを使用しています:

  • 制御スクリプト
  • スクリプトパッカーの出力から専用テクスチャを作成
  • 密接に制御スクリプトとテクスチャの両方に接続されている特殊なシェーダー。

readme ファイルはどのような機能が必要でどのように実装され、使用されたプロセスがどのようにシステムに動作するか、これを説明する例を含んでいます。これはそのファイルです:

問題は次のように定義さます。"一度に画面に表示する数百の動的に点灯して回転し、集められたコイン」

単純なアプローチは、コインをレンダリングするために粒子を使用しようとする代わりに、コインプレハブのコピーをまとめてインスタンス化することです。しかし、これは私たちが克服しなければならない多くの課題を引き合わせてくれます。

  • 粒子はそれらを持っていないため視野角が問題です。
    • 私たちはカメラが右側の上に留め、コインは、Y 軸を中心に回転することを想定しています。
    • 私たちはスプライトパッカーを使ってパックされたアニメーションのテクスチャのコインが回転するイリュージョンを作成。
      • これは新しい問題を引き合わせる:すべて同じ早さで同じ方向に回転するコインの単調さ
      • 私たちは自分自身の回転とライフタイムを追跡し、これを修正するために、スクリプト内で粒子の寿命に回転を“与える”。
  • 粒子がそれらを持っていないため法線が問題であり、リアルタイムのライティングを必要とします。
    • スプライトパッカーによって生成された各アニメーションフレームにおいてコインの面のための単一の法線ベクトルを生成します。
    • 法線ベクトルは上記のリストに基づいて、スクリプト内の各粒子のため Blinn-Phong ライティングを行います。
    • 色として粒子に結果を適用します。
    • コインの面とシェーダーで別々にコインの縁を処理します。 新しい問題と引き合わせる: シェーダーは縁がどこにあり、縁の一部がそれの上にあることをどうすれば知ることができるか?
      • UV のを使用することはできず、彼らはすでにアニメーションに使用されてます。
      • テクスチャマップを使用してください。
        • コインに相対な Y-位置が必要
        • “面の上” vs “縁の上” バイナリが必要です。
      • 私たちは別のテクスチャを導入しようとせず、より多くのテクスチャを読み込み、より多くのテクスチャメモリを使う。
      • 1 つのチャネルに必要な情報を組み合わせ、それとテクスチャのカラーチャンネルの 1 つをそれと交換してください。
        • 現在、私たちのコインは間違った色です!私たちは何をすればよいか?
        • 残りの 2 つのチャンネルを組み合わせ、欠落しているチャンネルを再構築するためにシェーダーを使用します。
  • 私たちは光きらめくコインを輝かせたいとします。モバイルデバイス用としてはポストプロセスはあまりにも高価です。
    • 別のパーティクルシステムを作成し、柔らかさを与え、コインアニメーションの glowy バージョンを与えます。
    • 対応するコインの色が光り輝く時のみグローカラー(夜行色)にします。
  • すべてのフレームすべてのコイン上に輝きを与えることはできません - レートキラーを埋める。 * グローをフレームごとにリセット。明るさ > 0 の位置のみ。

  • 物理の問題は、コインの収集が問題です - 粒子がよく衝突することはない。
    • 組み込まれた粒子衝突を使用してもらえますか?
    • 代わりに、単にスクリプトに衝突を書きました。
  • 最後に我々は 1 つ以上の問題を抱えている。- このスクリプトは、多くのことを行い、そして遅くなる!
    • パフォーマンスは、アクティブなコインの数に比例します。
      • 限度一杯のコイン。これが私たちの目標は十分に動作すること: 100 のコイン、2 の光を、モバイルデバイス上で本当に速く動作させることです。
  • さらなる最適化させるもの:
    • 個別にすべてのコインのためのライティングを計算する代わりに、世界を塊に分割し、すべての塊内のすべての回転フレームのための照明条件を算出します。
    • 指標としてコイン位置とコインの回転をルックアップテーブルとして使用します。
      • 位置の双線形補間を使用して忠実度を増やします。
      • まれに更新するルックアップテーブル、または、完全に静的なルックアップテーブル
      • このために光プローブを使用しますか?
    • スクリプト内の照明を計算する代わりに、通常にマップされた粒子を使う?
      • 法線のフレームアニメーションをベイクするために“ディスプレイ法線”シェーダーを使用してください。
      • ライトの数を制限します。
      • 遅いスクリプト問題を修正しました。

この例の最終目標または“話の教訓”は、あなたのゲームが本当に必要とするものがある場合、あなたは従来の手段でそれを達成しようとするとそれが遅れの原因となりますが、それは不可能であることを意味するものではありません。それはあなたがはるかに高速に実行される独自のシステム上に、いくつかの仕事を配置する必要があることを意味します。

数千のオブジェクトを管理するための技術

これらは動的オブジェクトの数百または数千に関与している状況で適用される特定のスクリプトの最適化です。あなたのゲーム内のすべてのスクリプトにこれらの技術を適用することは恐ろしい考えです:これらは、実行時に大量のオブジェクトまたは大量のデータを扱う大規模なスクリプト用のツールや設計ガイドラインとして予約する必要があります。

大規模なデータセットを避ける、または O(N2)の操作を最小限に抑える

コンピューターサイエンスでは、O(n)で示される操作の順序は、動作はそれが適用されるオブジェクトの数が増加して評価されなければならない回数(n)が増加する方法を指す。

例えば、基本的なソートアルゴリズムを検討してみる。n ある数を、最小から最大に(昇順)それらをソートしたい。

 void sort(int[] arr) {
    int i, j, newValue;
    for (i = 1; i < arr.Length; i++) {
        // record
        newValue = arr[i];
        //shift everything that is larger to the right
        j = i;
        while (j > 0 && arr[j - 1] > newValue) {
            arr[j] = arr[j - 1];
            j--;
        }
        // place recorded value to the left of large values
        arr[j] = newValue;
    }
 }

重要な部分は、ここで二つのループがあり、一つは他方の内側(のループ)があるということです。

 for (i = 1; i < arr.Length; i++) {
    ...
    j = i;
    while (j > 0 && arr[j - 1] > newValue) {
        ...
        j--;
    }
 }

それでは、アルゴリズムの最悪のケースを与えると仮定します:入力された数字をソートしますが、逆の順でされています。その場合、もっとも内側のル���プは j 回実行されます。平均して、i1 から arr.Length–1 へ行くように、jarr.Length/2 になります。O(n) の条件で、arr.Length は、私たちの n があり、したがって、合計で、もっとも内側のループは、nn / 2* 回、または n2/2** 回実行します。しかし、我々は操作の実際の数ではなく、操作の数が増加する方法について話をしたいので、O(n) 項で、1/2 にすべての定数を固定します。だから、このアルゴリズムは O(n2 です。データセットが大きい場合には、操作の数が指数関数的に爆発するので、操作の順序は重要です。

O(n2) 操作のゲーム内の例が 100 の敵です。ここで、各敵の AI は、他のすべての敵の動きを考慮に入れます。マップをセルに分割させて、速くなるかもしれない。最寄りのセル内に各敵の動きを記録し、その後、各敵は最寄りのいくつかのセルにサンプリングされる。つまり、O(n) の操作になります。

不要な検索を実行する代わりにキャッシュを参照

あなたがゲームで 100 の敵を持っていると言うと彼らすべてがプレイヤーに向かって移動する。

 // EnemyAI.js
 var speed = 5.0;
 
 function Update () {
    transform.LookAt(GameObject.FindWithTag("Player").transform);
    // this would be even worse:
    //transform.LookAt(FindObjectOfType(Player).transform);
 
    transform.position += transform.forward * speed * Time.deltaTime;
 }

それは、それらが十分に存在し、同時に実行した場合遅くなる可能性があります。あまり知られていない事実: MonoBehaviour のコンポーネント·アクセサリーのすべて、transformrendereraudio のようなことは GetComponent(Transform) の対応と同等で、彼らは実際に少し遅いです。GameObject.FindWithTag は、いくつかの場合において、例えば、内部ループで、またはインスタンスの多くで実行するスクリプトで、最適化されています。このスクリプトは少し遅いかもしれません。

これはスクリプトの改良版です。

 // EnemyAI.js
 var speed = 5.0;
 
 private var myTransform : Transform;
 private var playerTransform : Transform;
 
 function Start () {
    myTransform = transform;
    playerTransform = GameObject.FindWithTag("Player").transform;
 }
 
 function Update () {
    myTransform.LookAt(playerTransform);
 
    myTransform.position += myTransform.forward * speed * Time.deltaTime;
 }

高等な数学関数を最小化する

超越関数( Mathf.SinMathf.Pow 等)、除算及び平方根は、すべて乗算の時間の 100 倍ほどかかります。物事の壮大なスキームでは、まったく時間がないが、あなたがそれらをフレームごとに何千回も呼び出している場合、それは追加することができます。

これのもっとも一般的なケースはベクトル正規化です。何度も同じベクトル正規化している場合、一度代わりにそれを正規化し、後で使用するために結果をキャッシュすることを検討してください。

両方のベクトルの長さを使用して、それを正規化している場合、normalized プロパティーを使うより、長さの逆数でベクトルを乗算して正規化されたベクトルを得るために速いでしょう。

あなたは距離を比較している場合は、実際の距離を比較する必要はありません。代わりに、sqrMagnitude プロパティーを使用し、距離の二乗を比較し、平方根またはその 2 つを保存することができます。

もう一つは、あなたは定数 c によって何度も除算している場合には、代わりに逆数を掛けることができます。最初に 1.0/c を掛けて計算します。

たまに高等な操作を実行する、例: Physics.Raycast()

あなたは高等な何かをしなければならない場合、あなたが、多くの場合、それを少なくやって、その結果をキャッシュすることで、それを最適化することができるかもしれません。例えば、レイキャストを使用した発射スクリプトを考えてみます。

 // Bullet.js
 var speed = 5.0;
 
 function FixedUpdate () {
    var distanceThisFrame = speed * Time.fixedDeltaTime;
    var hit : RaycastHit;
 
    // every frame, we cast a ray forward from where we are to where we will be next frame
    if(Physics.Raycast(transform.position, transform.forward, hit, distanceThisFrame)) {
        // Do hit
    } else {
        transform.position += transform.forward * distanceThisFrame;
    }
 }

直ちに、我々は deltaTime と fixedDeltaTime とアップデートで FixedUpdate に置き換えることで、スクリプトを向上させることができます。FixedUpdate は、フィジクスのアップデートを指し、これはフレームの更新よりも頻繁に起こります。しかし、n 秒ごとにレイキャスティングすることでさらに行われます。小さい n は大きな時間分解能を提供しますし、n が大きいとよりよいパフォーマンスを提供します。より大きく、より遅いあなたのターゲットがあり、時間的なエイリアシングが発生する前に大きな n があってもよいです(プレーヤーがターゲットを攻撃した所、ターゲットが n 秒前にいたところでの爆発のように見えます、または、プレーヤーはターゲットを攻撃しました、しかし、発射体は右を通り過ぎていきます)。

 // BulletOptimized.js
 var speed = 5.0;
 var interval = 0.4; // this is 'n', in seconds.
 
 private var begin : Vector3;
 private var timer = 0.0;
 private var hasHit = false;
 private var timeTillImpact = 0.0;
 private var hit : RaycastHit;
 
 // set up initial interval
 function Start () {
    begin = transform.position;
    timer = interval+1;
 }
 
 function Update () {
    // don't allow an interval smaller than the frame.
    var usedInterval = interval;
    if(Time.deltaTime > usedInterval) usedInterval = Time.deltaTime;
 
    // every interval, we cast a ray forward from where we were at the start of this interval
    // to where we will be at the start of the next interval
    if(!hasHit && timer >= usedInterval) {
        timer = 0;
        var distanceThisInterval = speed * usedInterval;
 
        if(Physics.Raycast(begin, transform.forward, hit, distanceThisInterval)) {
            hasHit = true;
            if(speed != 0) timeTillImpact = hit.distance / speed;
        }
 
        begin += transform.forward * distanceThisInterval;
    }
 
    timer += Time.deltaTime;
 
    // after the Raycast hit something, wait until the bullet has traveled
    // about as far as the ray traveled to do the actual hit
    if(hasHit && timer > timeTillImpact) {
        // Do hit
    } else {
        transform.position += transform.forward * speed * Time.deltaTime;
    }
 }

内側のループでコールスタックオーバーヘッドを最小限に抑える

ただ関数を呼び出すこと自体にオーバーヘッドが少しあります。あなたはフレームあたり数千回と X= Mathf.Abs(x) "のようなものを呼び出している場合、その代わりに X=(X>0 X :-x ?); それだけでやる方がよいかもしれません。

Physics パーフォーマンスの最適化

Unity で使われる NVIDIA の PhysX 物理エンジンはモバイルで利用できますが、デスクトップよりもモバイルプラットフォーム上ではハードウェアの性能限界は、より簡単に達します。

ここではモバイル上で、よりよいパフォーマンスを得るためにフィジクスをチューニングするためのヒントを紹介します: -

  • あなたはフィジクスの更新に費やされる時間を減らすために(Time Managerの中の) Fixed Timestep の設定を調整することができます。タイムステップを大きくするとフィジクスの精度を犠牲にして CPU のオーバーヘッドを減少でき、多くの場合、低い精度は高速度化のために許容できるトレードオフです。
  • フィジクスの最悪のシナリオでは、8–10fps の範囲で時間を費やしてキャプするように Time Managerの中の Maximum Allowed Timestep で設定します。
  • メッシュコライダーはプリミティブコライダーよりもはるかに高性能でオーバーヘッドを持っており、控えめにそれらを使用しています。これはプリミティブコライダーと子オブジェクトを使用して、メッシュの形状を近似することがしばしば可能です。子コライダーは、親にリッジボディによる単一の複合コライダーとして一括して制御されます。
  • ホイールコライダーは、厳密にはソリッドオブジェクトの意味でのコライダーではありませんが、それにもかかわらず、高い CPU のオーバーヘッドを持っています。
レンダリングの最適化
旧トピック