はじめに
今回はPhysics.RaycastAll
とPhysics.RaycastNonAlloc
の違いについて紹介したいと思います。
概要
Physics.RaycastAll
とPhysics.RaycastNonAlloc
はどちらもRay
を飛ばしてぶつかったゲームオブジェクトを返します。
// オーバーロードが複数あるが、その内一個 public static RaycastHit[] RaycastAll (Vector3 origin, Vector3 direction, float maxDistance= Mathf.Infinity, int layerMask= DefaultRaycastLayers, QueryTriggerInteraction queryTriggerInteraction= QueryTriggerInteraction.UseGlobal); public static int RaycastNonAlloc (Vector3 origin, Vector3 direction, RaycastHit[] results, float maxDistance= Mathf.Infinity, int layerMask= DefaultRaycastLayers, QueryTriggerInteraction queryTriggerInteraction= QueryTriggerInteraction.UseGlobal);
それぞれの説明は以下の通り。
Casts a ray through the Scene and returns all hits. Note that order of the results is undefined.
Physics-RaycastAll - Unity スクリプトリファレンス
// DeepL翻訳
Scene を通して光線を投げ、ヒットしたものをすべて返します。結果の順序は不定であることに注意してください。
Cast a ray through the Scene and store the hits into the buffer.
Physics-RaycastNonAlloc - Unity スクリプトリファレンス
// DeepL翻訳
Sceneに光線を照射し、ヒットした光線をバッファに格納します。
使用例は以下の通り。
// Physics.RaycastAllの使用例 // Rayの原点がorigin, Rayの方向がdirectoin, maxDistanceがヒットが発生する最大距離 RaycastHit[] hits = Physics.RaycastAll(origin: Vector3.zero, direction: Vector3.forward, maxDistance: 100);
// Physics.RaycastNonAllocの使用例 // 配列を用意しておき、そこに結果が格納する RaycastHit[] hits = new RaycastHit[100]; // Rayの原点がorigin, Rayの方向がdirectoin, maxDistanceがヒットが発生する最大距離 Physics.RaycastNonAlloc(origin: Vector3.zero, direction: Vector3.forward, results: hits, maxDistance: 100);
違い
上記サンプルだとほぼ違いがない(Physics.RaycastNonAlloc
は配列に入りきらない結果が破棄される,結果のサイズが小さいと余分にメモリを利用する可能性アリ)ですが、Physics.RaycastNonAlloc
はRaycastHit[]
のインスタンスを使いまわすことができるというメリットがあります。
public class Test : MonoBehaviour { private RaycastHit[] _hits = new RaycastHit[100]; private void Update() { // Rayの原点がorigin, Rayの方向がdirectoin, maxDistanceがヒットが発生する最大距離 Physics.RaycastNonAlloc(origin: Vector3.zero, direction: Vector3.forward, results: _hits, maxDistance: 100); } }
こうすることでPhysics.RaycastAll
の度に配列が確保される(GC.Alloc
発生)のを、最初の一回だけしか配列が確保されないようにできます。
ただキャッシュするということはメモリを常に使用するという意味でもあるので、使い分けてください。
実験
適当にUpdate
で10000
回メソッド呼び出ししてみます。
// 適宜特定箇所をコメントアウトして利用 public class Test : MonoBehaviour { private RaycastHit[] _hits = new RaycastHit[100]; private void Update() { for (var i = 0; i < 10000; i++) { // RaycastNonAllocの場合 Physics.RaycastNonAlloc(origin: Vector3.zero, direction: Vector3.forward, results: _hits, maxDistance: 100); // RaycastAllの場合 Physics.RaycastAll(Vector3.zero, Vector3.forward, 100); } } }
Physics.RaycastNonAlloc
は名前の通りGC.Alloc
が0
ですね。処理時間も1/6
くらいになってます。
補足ですが、ヒットするゲームオブジェクトは一個もない状況での実験結果です。
注意点
たまに勘違いされている方がいますが、Physics.RaycastAll
とPhysics.RaycastNonAlloc
のどちらも順序が保証されているわけではないです。
またresults
の配列がnull
だった場合は例外を投げるわけではなく、結果も返されないよう。