サーチ…
備考
- 可能であれば、必要でないオブジェクトのスクリプトを無効にします。例えば、敵オブジェクトにスクリプトを持っていて、プレイヤーを捜索したり発砲したりする場合、敵がプレーヤーから遠すぎる場合にこのスクリプトを無効にすることを検討してください。
迅速かつ効率的なチェック
可能な限り、特にUpdate
などの何度も何回も呼び出されるメソッドでは、不要な操作やメソッド呼び出しを避けてください。
距離/範囲チェック
距離を比較するときは、 magnitude
代わりにsqrMagnitude
を使用します。これにより、不要なsqrt
操作が回避されます。 sqrMagnitude
を使用するsqrMagnitude
は、右辺も二乗する必要があります。
if ((target.position - transform.position).sqrMagnitude < minDistance * minDistance))
境界チェック
オブジェクトの交差は、そのCollider
/ Renderer
境界が交差しているかどうかをチェックすることによって、きちんとチェックすることができます。 Bounds
構造体には、2つの境界が交差するかどうかを判断するのに役立つ、便利なIntersects
メソッドもあります。
Bounds
また、(参照オブジェクト間の実際の (表面に面)までの距離の近い概算を算出するために私たちを助けるBounds.SqrDistance
)。
警告
境界チェックは、凸オブジェクトの場合は本当にうまく機能しますが、凹オブジェクトの境界チェックは、オブジェクトの形状によってはるかに高い精度をもたらす場合があります。
Mesh.bounds
を使用することは、ローカル空間の境界を返すので推奨されません。代わりにMeshRenderer.bounds
を使用してください。
コルーチンの力
使用法
スレッドセーフではないUnity APIに依存する長期間の操作をしている場合は、 Coroutinesを使用して複数のフレームに分割し、アプリケーションを応答可能にします。
コルーチンはまた、各フレームでそのアクションを実行する代わりに、高価なアクションをn番目のフレームごとに実行するのに役立ちます。
複数のフレームにわたる長時間実行ルーチンの分割
Coroutinesは、長時間実行される操作を複数のフレームに分散して、アプリケーションのフレームレートを維持するのに役立ちます。
手続き的に地形をペイントまたは生成するルーチンやノイズを生成するルーチンは、コルーチンの処理が必要な例です。
for (int y = 0; y < heightmap.Height; y++)
{
for (int x = 0; x < heightmap.Width; x++)
{
// Generate pixel at (x, y)
// Assign pixel at (x, y)
// Process only 32768 pixels each frame
if ((y * heightmap.Height + x) % 32 * 1024) == 0)
yield return null; // Wait for next frame
}
}
上のコードは分かりやすい例です。プロダクションコードでは、
yield return
いつyield return
(おそらく2〜3行ごとに行う)、事前for
ループ長を事前に計算することをチェックするピクセル単位のチェックを避けるほうがよいです。
高価なアクションをあまり頻繁に実行しない
コルーチンは、高価なアクションの実行頻度を減らすことができます。そのため、フレームごとに実行される場合と同じようにパフォーマンスが低下することはありません。
マニュアルから直接次の例を考えてみましょう:
private void ProximityCheck()
{
for (int i = 0; i < enemies.Length; i++)
{
if (Vector3.Distance(transform.position, enemies[i].transform.position) < dangerDistance)
return true;
}
return false;
}
private IEnumerator ProximityCheckCoroutine()
{
while(true)
{
ProximityCheck();
yield return new WaitForSeconds(.1f);
}
}
近接性テストは、 CullingGroup APIを使用してさらに最適化することができます 。
共通落とし穴
開発者がよく行う間違いは、コルーチン外のコルーチンの結果や副作用にアクセスすることです。コルーチンは、 yield return
文に遭遇し、その結果または副作用がまだ実行されないとすぐに、呼び出し元に制御を返します。コルーチン外で結果/副作用を使用する必要がある問題を回避するには、 この回答を確認してください。
文字列
謙虚な文字列よりもUnityに大きなリソースがあると主張するかもしれませんが、早い段階で修正する方が簡単です。
文字列操作でゴミが生成される
ほとんどの文字列演算はごくわずかな量のゴミを生成しますが、それらの演算が1回の更新で何回か呼び出されると、スタックされます。時間が経つと自動的にガベージコレクションがトリガーされ、CPUスパイクが目立つことがあります。
文字列操作をキャッシュする
次の例を考えてみましょう。
string[] StringKeys = new string[] {
"Key0",
"Key1",
"Key2"
};
void Update()
{
for (var i = 0; i < 3; i++)
{
// Cached, no garbage generated
Debug.Log(StringKeys[i]);
}
for (var i = 0; i < 3; i++)
{
// Not cached, garbage every cycle
Debug.Log("Key" + i);
}
// The most memory-efficient way is to not create a cache at all and use literals or constants.
// However, it is not necessarily the most readable or beautiful way.
Debug.Log("Key0");
Debug.Log("Key1");
Debug.Log("Key2");
}
それは愚かで冗長に見えるかもしれませんが、Shadersで作業している場合は、これらの状況に遭遇するかもしれません。キーをキャッシュすることで違いが生まれます。
文字列リテラルと定数は、プログラムスタック空間に静的に注入されるため、ガベージを生成しないことに注意してください。あなたは実行時に文字列を生成していると、毎回上記の例のように同じ文字列を生成することが保証されている場合は、キャッシュは間違いなく役立ちます。
生成される文字列が毎回同じでない他の場合には、それらの文字列を生成する他の方法はありません。そのため、毎回手動で文字列を生成するメモリスパイクは、一度に何万もの文字列が生成されない限り、通常は無視できます。
ほとんどの文字列操作はデバッグメッセージです
デバッグメッセージの文字列操作、つまりDebug.Log("Object Name: " + obj.name)
は問題なく、開発中に避けることはできません。ただし、リリースされた製品に無関係のデバッグメッセージが残らないようにすることが重要です。
1つの方法は、デバッグ呼び出しでConditional属性を使用することです。これはメソッド呼び出しだけでなく、すべての文字列操作も取り除きます。
using UnityEngine;
using System.Collections;
public class ConditionalDebugExample: MonoBehaviour
{
IEnumerator Start()
{
while(true)
{
// This message will pop up in Editor but not in builds
Log("Elapsed: " + Time.timeSinceLevelLoad);
yield return new WaitForSeconds(1f);
}
}
[System.Diagnostics.Conditional("UNITY_EDITOR")]
void Log(string Message)
{
Debug.Log(Message);
}
}
これは簡単な例です。完全に本格的なロギング・ルーチンを設計するには、しばらく時間を費やすことをお勧めします。
文字列の比較
これはマイナーな最適化ですが、言及する価値があります。文字列の比較は、考えられるよりも多少複雑です。システムは、デフォルトで文化的な違いを考慮に入れようとします。代わりに単純なバイナリ比較を使用することができます。これは高速です。
// Faster string comparison
if (strA.Equals(strB, System.StringComparison.Ordinal)) {...}
// Compared to
if (strA == strB) {...}
// Less overhead
if (!string.IsNullOrEmpty(strA)) {...}
// Compared to
if (strA == "") {...}
// Faster lookups
Dictionary<string, int> myDic = new Dictionary<string, int>(System.StringComparer.Ordinal);
// Compared to
Dictionary<string, int> myDictionary = new Dictionary<string, int>();
キャッシュ参照
特に更新機能で高価な呼び出しを避けるために参照をキャッシュします。これは、利用可能な場合は開始時にこれらの参照をキャッシュするか、または利用可能な場合にはnull / boolフラットをチェックして参照を再度取得しないようにすることによって実行できます。
例:
コンポーネント参照をキャッシュする
変化する
void Update()
{
var renderer = GetComponent<Renderer>();
renderer.material.SetColor("_Color", Color.green);
}
に
private Renderer myRenderer;
void Start()
{
myRenderer = GetComponent<Renderer>();
}
void Update()
{
myRenderer.material.SetColor("_Color", Color.green);
}
オブジェクト参照をキャッシュする
変化する
void Update()
{
var enemy = GameObject.Find("enemy");
enemy.transform.LookAt(new Vector3(0,0,0));
}
に
private Transform enemy;
void Start()
{
this.enemy = GameObject.Find("enemy").transform;
}
void Update()
{
enemy.LookAt(new Vector3(0, 0, 0));
}
さらに、可能な限りMathfへの呼び出しのような高価な呼び出しをキャッシュします。
文字列を使用したメソッドの呼び出しを避ける
メソッドを受け入れることができる文字列を使ってメソッドを呼び出さないようにしてください。このアプローチでは、リフレクションを使用して、特に更新機能で使用するときにゲームを減速させることができます。
例:
//Avoid StartCoroutine with method name
this.StartCoroutine("SampleCoroutine");
//Instead use the method directly
this.StartCoroutine(this.SampleCoroutine());
//Avoid send message
var enemy = GameObject.Find("enemy");
enemy.SendMessage("Die");
//Instead make direct call
var enemy = GameObject.Find("enemy") as Enemy;
enemy.Die();
空の統一方法を避ける
空の統一方法を避けてください。プログラミングスタイルが悪いだけでなく、ランタイムスクリプトに伴うオーバーヘッドも非常に少なくなります。多くの場合、これはビルドアップしてパフォーマンスに影響を与えます。
void Update
{
}
void FixedUpdate
{
}