Overview: Performance Optimization Manual     Reference     Scripting  
Scripting
Overview: パフォーマンス最適化(Performance Optimization)

1. 静的型の使用(Use Static Typing)

Javascriptを使う場合の最も重要な最適化は、動的型付けではなく、静的型付けを使うことです。Unityは型継承の技術を用いて、自動的にJavascriptのコンストラクタを静的に片付けられたコードに変換します。

var foo = 5;

上 の例では、fooは自動的にInteger値として設定されます。そのためUnityは多くのコンパイル時最適化を行ないます。動的な名前変数の探索にコ ストを掛けません。これはUnityのJavascriptが通常のJavascriptより平均20倍程度速い理由の一つです。

唯一の問題は、型付けが常に自動的に行えるとは限らないことです。そのためUnityは動的型付けも行ないます。動的型付けを行うJavascriptコードは簡単に記述できます。しかしながら、コードの動作速度は遅くなってしまうでしょう。

例を見てみましょう。

function Start ()
{
var foo = GetComponent(MyScript);
foo.DoSomething();
}

こ こではfoo変数が動的に型づけされます。そのためDoSomething関数の呼出は必要以上に時間がかかってしまいます。fooの型が解らないためで す。DoSomething関数がサポートされているかどうかを調べ、サポートされていれば実行するという手順が必要になります。

function Start ()
{
var foo : MyScript = GetComponent(MyScript);
foo.DoSomething();
}

ここでは、fooの型が特定されています。より良いパフォーマンスを得ることが出来るでしょう。

2. #pragma strict を使いましょう(Use #pragma strict)

ここでの問題はもちろん、普段は#pragma strict がどのように静的型付けを助けるのか気づかないことです。単純には、スクリプトの最初に記述した#pragma strictは、Unityが動的に型づけをすることを停止し、静的な型附を使うことを強制します。型が判明していな場合、Unityはコンパイルエラー を報告します。この場合、fooはコンパイルエラーを発生するでしょう。:

#pragma strict
function Start ()
{
var foo = GetComponent(MyScript);
foo.DoSomething();
}

3. キャッシュコンポーネントの探索(Cache component lookups)

もうひとつの最適化は、コンポーネントをキャッシュすることです。この最適化は、残念ながら、少しのプログラミングによる努力を必要としますし、常に効果 的ではありません。しかし、そのスクリプトを非常に多く利用し、最後のチューニングをしなければならないとき、これはとても良い最適化となります。

コンポーネントにGetComonentを用いてアクセスする場合、Unityはゲームオブジェクトから対象のコンポーネントを探さなければなりません。この時間は、参照をプライベート変数にキャッシュしておくことで簡単に節約できます。

単純に、次のような場合:

function Update () {
transform.Translate(0, 0, 5);
}

以下のように変更します。:

private var myTransform : Transform;
function Awake () {
myTransform = transform;
}

function Update () {
myTransform.Translate(0, 0, 5);
}

2 つ目のコードは、かなり速く動作します。Unityがtransformコンポーネントをゲームオブジェクトからフレームごとに探し出す必要がなくなるか らです。同様の手法が、スクリプトでGetComponentを使い他の属性やtransformを取得する場合にも適用できます。

4. 組み込み配列の使用(Use Builtin arrays)

組み込み配列は高速です、とても高速です。使いましょう。

ArrayListやArrayク ラスは要素の追加が簡単なため使いやすいですが、速度面ではかなり劣ります。組み込み配列は定まったサイズですが、多くの場合は事前に最大数が分かってい ます。組み込み配列の最も良いところは、構造データ型とバッファが強く結びついていることです。その他の追加情報やオーバヘッドがありません。そのため、 配列を捜査することはキャッシュ上でメモリを線形に探索するようにとても簡単に行なえます。or3[];

function Awake () {
positions = new Vector3[100];
for (var i=0;i<100;i++)
positions[i] = Vector3.zero;
}

5. 必要がなければ関数を呼び出さないでください(Don't call a function if you don't have to)

最も単純で有効な最適化は、少ない作業をすることです。たとえば、敵が遠い場合、敵を眠らせておいても問題有りません。プレイヤーが近づくまで、全く必要ありません。このような状況を遅く扱うには、次のようになります:

function Update ()
{
// Early out if the player is too far away.
if (Vector3.Distance(transform.position, target.position) > 100)
return;
perform real work work...
}

こ れは、良い方法ではありません。Unityはupdate関数を呼び出し、マイフレームごとに作業を行っています。より良い解決策は、プレイヤが近くに来 るまで、動作を行わないことです。3つの方法があります。1. OnBecameVisibleかOnBecameInvisbleを使う。これらは、レンダリングシステムに結びついています。カメラがオブジェクトを 見ることが出来るようになると、OnBecameVisibleが呼ばれ、カメラがオブジェクトを見られなくなるとOnBecameInvisibleが 呼ばれます。これはいくつかの場合に有効ですが、AIにとっては有効ではありません。敵はあなたが向きを変えると無効化されてしまうからです。

function OnBecameVisible () {
enabled = true;
}

function OnBecameInvisible ()
{
enabled = false;
}

2. トリガを使う。シンプルな球形のトリガが素晴らしく動作します。球体に入ったり出たりしたときに、OnTriggerEnter/Exit呼出を受けとります。

function OnTriggerEnter (c : Collider)
{
if (c.CompareTag("Player"))
enabled = true;
}

function OnTriggerExit (c : Collider)
{
if (c.CompareTag("Player"))
enabled = false;
}

3. コルーチンを使う。Update呼出の問題はマイフレームごとに実行されることです。5秒ごとにプレイヤとの距離を測るのは十分に使用可能です。多くの計算パワーが削減できるでしょう。