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シーンでのオブジェクト管理が効率的になります。また、視錐台カリングと組み合わせることで、無駄な描画を避け、ゲームのパフォーマンスを最適化できます。