Bounds
はUnityの構造体で、3D空間内のオブジェクトの軸に平行な境界ボックス(AABB: Axis-Aligned Bounding Box)を表します。この境界ボックスはオブジェクトの位置とサイズを基に決定され、主に以下の用途で使用されます。
Bounds
が重なっているかどうかを確認します。次のプログラム例では、2つのオブジェクトのBounds
を取得し、それらが交差しているかどうかを判定します。
using UnityEngine;
public class BoundsExample : MonoBehaviour
{
// シーン内の他のオブジェクトを参照します。
public GameObject otherObject;
void Start()
{
// このスクリプトがアタッチされたオブジェクトのColliderを取得し、Boundsを取得します。
Bounds objectBounds = GetComponent<Collider>().bounds;
// 他のオブジェクトのColliderを取得し、Boundsを取得します。
Bounds otherBounds = otherObject.GetComponent<Collider>().bounds;
// 2つのBoundsが交差しているかどうかをチェックします。
if (objectBounds.Intersects(otherBounds))
{
Debug.Log("Objects are intersecting.");
}
else
{
Debug.Log("Objects are not intersecting.");
}
}
}
Bounds
の取得:
Bounds
は、オブジェクトのCollider
から取得します。Collider
は、オブジェクトの形状や物理的な性質を持つコンポーネントであり、bounds
プロパティを使ってそのオブジェクトの境界ボックスを取得できます。Bounds objectBounds = GetComponent<Collider>().bounds;
ここでGetComponent<Collider>()
は、オブジェクトにアタッチされたCollider
を取得し、そのbounds
プロパティがBounds
を返します。
Intersects
メソッド:
Intersects
メソッドは、2つのBounds
が重なっているかどうかを判定するためのメソッドです。これにより、2つのオブジェクトが空間内で交差しているかどうかを簡単に確認できます。if (objectBounds.Intersects(otherBounds))
{
Debug.Log("Objects are intersecting.");
}
この例では、objectBounds
とotherBounds
が交差している場合に「Objects are intersecting.」というメッセージをコンソールに出力します。
Bounds
を使ってオブジェクトがカメラの視野内にあるかどうかを判定することもできます。視錐台(Frustum)内にBounds
が存在するかを確認するために、GeometryUtility.CalculateFrustumPlanes
とGeometryUtility.TestPlanesAABB
メソッドを使用します。Plane[] frustumPlanes = GeometryUtility.CalculateFrustumPlanes(Camera.main);
if (GeometryUtility.TestPlanesAABB(frustumPlanes, objectBounds))
{
Debug.Log("Object is within the camera's frustum.");
}
else
{
Debug.Log("Object is outside the camera's frustum.");
}
この例では、カメラの視錐台(frustum)を表す平面(Plane
)の配列を計算し、それに対してBounds
が含まれているかをチェックしています。
Bounds
の取得: オブジェクトのCollider
からBounds
を取得します。Intersects
メソッドを使って、2つのBounds
が交差しているかを判定できます。Bounds
が含まれているかを確認し、カメラの視野内にオブジェクトがあるかを判定します。これらの機能は、3Dゲームやシミュレーションにおいて、効率的な衝突判定や可視性チェックを行うために非常に重要です。
カメラの視錐台内にオブジェクトが含まれているかをチェックし、その結果に基づいて描画するかどうかを決定するプログラムの例を見てみます。
using UnityEngine;
public class FrustumCullingExample : MonoBehaviour
{
public GameObject[] objectsToRender; // チェック対象のオブジェクトリスト
void Update()
{
// カメラの視錐台を計算
Plane[] frustumPlanes = GeometryUtility.CalculateFrustumPlanes(Camera.main);
// 全てのオブジェクトに対して処理を行う
foreach (GameObject obj in objectsToRender)
{
// 各オブジェクトのBoundsを取得
Bounds bounds = obj.GetComponent<Renderer>().bounds;
// オブジェクトのBoundsが視錐台内にあるかチェック
if (GeometryUtility.TestPlanesAABB(frustumPlanes, bounds))
{
// 視錐台内にある場合、オブジェクトを表示
obj.SetActive(true);
}
else
{
// 視錐台外にある場合、オブジェクトを非表示にする
obj.SetActive(false);
}
}
}
}
objectsToRender
配列:
objectsToRender
には、チェック対象のオブジェクトを登録しておきます。public GameObject[] objectsToRender;
GeometryUtility.CalculateFrustumPlanes
メソッド:
CalculateFrustumPlanes
は、カメラの視錐台を構成する6つの平面を計算し、それをPlane
型の配列として返します。この視錐台を使ってオブジェクトのBounds
が含まれているかをチェックします。Plane[] frustumPlanes = GeometryUtility.CalculateFrustumPlanes(Camera.main);
Renderer
コンポーネントからBounds
を取得:
Renderer
コンポーネントのbounds
プロパティを使って、オブジェクトのBounds
を取得します。Renderer
は、メッシュなどの描画を担当するコンポーネントです。Bounds bounds = obj.GetComponent<Renderer>().bounds;
GeometryUtility.TestPlanesAABB
メソッド:
Bounds
があるかどうかを判定します。もし視錐台内にBounds
があれば、true
を返します。if (GeometryUtility.TestPlanesAABB(frustumPlanes, bounds))
SetActive(true)
で表示し、視錐台外であればSetActive(false)
で非表示にします。これにより、視錐台内にあるオブジェクトだけが描画されます。obj.SetActive(true);
このプログラムは、視錐台カリングと呼ばれる手法の一例です。視錐台カリングは、カメラの視野内にないオブジェクトを描画しないことで、ゲームのパフォーマンスを向上させるために使用されます。特に大規模なシーンや多くのオブジェクトがあるシーンでは、視錐台カリングを活用することで、描画負荷を大幅に減らすことができます。
この例では、オブジェクトの表示・非表示をSetActive
メソッドで制御していますが、実際のゲームでは、他のカリング手法(例えばオクルージョンカリングやLOD(レベルオブディテール))と組み合わせて使用されることもあります。
GeometryUtility.TestPlanesAABB
メソッドを使用して、視錐台の平面とBounds
を比較します。Bounds
(空間)が視錐台内にあるか、または交差しているかを判定します。Bounds
を使ったカリングは、オブジェクトが描画されるかどうかを効率的に判断するのに役立ちます。GameObject
自体を直接視錐台の中にあるかどうかを調べることはできません。まずGameObject
のRenderer
やCollider
を取得して、それに基づいてBounds
を取得し、そのBounds
を用いて視錐台との比較を行う必要があります。GameObject
は物理的な存在なので、単体でのカリングは不可能ですが、Bounds
を通じて間接的にカリングを行います。Bounds
は、オブジェクトが占める空間を示す構造体で、主にカリングや衝突判定に使用されます。GameObject
は、Unity内での実際のエンティティであり、位置や回転、コンポーネントを持っています。Bounds
を使って行うため、GameObject
そのものを直接調べることはできません。GameObject
からBounds
を取得し、その情報を用いて判定します。このように、Bounds
とGameObject
は異なる役割を持っており、視錐台カリングではBounds
を利用することで、効率的に描画対象を決定することができます。
(Octreeの構造を使って効率的に空間分割を行い、視錐台内に存在するオブジェクトのみを描画することを目指します。)
以下にその例と解説。
この仕組みを使うことで、特に大規模なシーンでの描画パフォーマンスを向上させることが可能です。
using UnityEngine;
using System.Collections.Generic;
public class OctreeNode
{
public Bounds bounds;
public List<GameObject> objects;
public OctreeNode[] children;
public OctreeNode(Bounds bounds)
{
this.bounds = bounds;
this.objects = new List<GameObject>();
this.children = null;
}
// Octreeの子ノードを作成
public void Subdivide()
{
if (children != null)
return;
children = new OctreeNode[8];
Vector3 size = bounds.size / 2f;
Vector3 center = bounds.center;
for (int i = 0; i < 8; i++)
{
Vector3 newCenter = center + new Vector3(
(i & 1) == 0 ? -size.x / 2f : size.x / 2f,
(i & 2) == 0 ? -size.y / 2f : size.y / 2f,
(i & 4) == 0 ? -size.z / 2f : size.z / 2f
);
children[i] = new OctreeNode(new Bounds(newCenter, size));
}
}
// オブジェクトをOctreeに追加
public void Insert(GameObject obj)
{
if (!bounds.Contains(obj.GetComponent<Renderer>().bounds.min) || !bounds.Contains(obj.GetComponent<Renderer>().bounds.max))
return;
if (children == null)
{
objects.Add(obj);
// ノードが満杯になった場合、分割する
if (objects.Count > 8)
{
Subdivide();
foreach (var existingObj in objects)
{
Insert(existingObj);
}
objects.Clear();
}
}
else
{
foreach (var child in children)
{
child.Insert(obj);
}
}
}
// 視錐台内のオブジェクトを取得
public void RetrieveObjectsInFrustum(Plane[] frustumPlanes, List<GameObject> result)
{
if (!GeometryUtility.TestPlanesAABB(frustumPlanes, bounds))
return;
if (children != null)
{
foreach (var child in children)
{
child.RetrieveObjectsInFrustum(frustumPlanes, result);
}
}
else
{
foreach (var obj in objects)
{
if (GeometryUtility.TestPlanesAABB(frustumPlanes, obj.GetComponent<Renderer>().bounds))
{
result.Add(obj);
}
}
}
}
}
public class OctreeCulling : MonoBehaviour
{
public GameObject[] objectsToInsert;
private OctreeNode rootNode;
void Start()
{
// Octreeのルートノードを設定(ここでは空間の全体サイズを指定)
Bounds sceneBounds = new Bounds(Vector3.zero, new Vector3(100, 100, 100));
rootNode = new OctreeNode(sceneBounds);
// オブジェクトをOctreeに挿入
foreach (var obj in objectsToInsert)
{
rootNode.Insert(obj);
}
}
void Update()
{
// カメラの視錐台を取得
Plane[] frustumPlanes = GeometryUtility.CalculateFrustumPlanes(Camera.main);
// 視錐台内に存在するオブジェクトを取得
List<GameObject> objectsInFrustum = new List<GameObject>();
rootNode.RetrieveObjectsInFrustum(frustumPlanes, objectsInFrustum);
// 全てのオブジェクトを非表示にし、視錐台内に存在するオブジェクトのみ表示
foreach (var obj in objectsToInsert)
{
obj.SetActive(false);
}
foreach (var obj in objectsInFrustum)
{
obj.SetActive(true);
}
}
}
Bounds
と、その領域内に含まれるGameObject
のリストを持っています。また、必要に応じて8つの子ノードを持つことができます。Bounds
に完全に収まる場合、そのノードに追加されます。ノードが満杯になると分割され、オブジェクトが適切な子ノードに再挿入されます。Bounds
をチェックし、その範囲内にあるオブジェクトを取得します。再帰的に子ノードをチェックし、最終的に視錐台内のオブジェクトをリストに追加します。OctreeNode
のルートノードを作成し、シーン内のすべてのオブジェクトを挿入します。Update
メソッドで、視錐台内にあるオブジェクトだけを表示する処理を行います。objectsToInsert
に指定されたオブジェクトがOctreeに挿入されます。このように、Octreeを使用することで、大規模な3Dシーンでのオブジェクト管理が効率的になります。また、視錐台カリングと組み合わせることで、無駄な描画を避け、ゲームのパフォーマンスを最適化できます。