本日は Unity の小ネタ枠です。
Meshのローカル座標とワールド座標のBoundsをそれぞれ取得する手順を記事にします。
MeshのBounds取得
Mesh の Bounds 情報をスクリプトから取得するには MeshFilter と MeshRenderer の2つのアクセス方法があります。
MeshFilter からはローカル座標の Bounds、MeshRenderer からはワールド座標の Bounds 情報が取得可能です。
MeshFilterからのローカル座標のBounds取得
ローカル座標の Bounds は以下のように取得します。
Mesh mesh = GetComponent<MeshFilter>().mesh; Bounds bounds = mesh.bounds;
MeshRendererからのワールド座標のBounds取得方法
ワールド座標の Bounds は以下のように取得します。
Renderer renderer = GetComponent<Renderer>(); Bounds bounds = renderer.bounds;
サンプルスクリプト
以下の記事で作成した Mesh の bounds をもとにワールド座標のスケールを調整するスクリプトを改修してみます。
bluebirdofoz.hatenablog.com
スクリプトは以下の処理を行います。
1.アタッチしたゲームオブジェクトの子オブジェクトを全てチェックする。
2.MeshRenderer が設定されているオブジェクトからメッシュ形状の Bounds を取得する
3.取得したメッシュ形状の Bounds 全てを含むワールド座標系の Bounds を取得する
4.ワールド座標の Bounds の最大長の辺が指定のスケールに収まるようにローカルスケールを調整する
5.アタッチしたゲームオブジェクトのローカル座標系の Bounds を設定する
6.シーンで結果が確認できるように Bounds サイズの BoxCollider を設定する
・RendererControlChildObjBounds.cs
using System.Collections; using System.Collections.Generic; using UnityEngine; public class RendererControlChildObjBounds : MonoBehaviour { /// <summary> /// 子オブジェクトの統合Bounds /// </summary> public Bounds childObjBounds; void Start() { // オブジェクトのローカルスケールをオブジェクトが 1m 長に収まるよう調整する ChangeWorldBoundsSize(1.0f); // Boundsの大きさと形状が見た目に分かるようコライダーを追加する BoxCollider collider = this.gameObject.AddComponent<BoxCollider>(); // 計算されたバウンドボックスに合わせてコライダーの大きさと位置を変更する collider.center = childObjBounds.center; collider.size = childObjBounds.size; } /// <summary> /// ワールド座標での全体のバウンドサイズを元にローカルスケールを調整する /// </summary> public void ChangeWorldBoundsSize(float size) { // ワールド座標のバウンドサイズを計算する Bounds objBounds = CalcChildObjWorldBounds(this.gameObject, new Bounds()); // バウンドの最大長の辺の長さを取得する float maxlength = Mathf.Max(objBounds.size.x, objBounds.size.y, objBounds.size.z); // スケール調整の係数を取得する float coefficient = size / maxlength; // ローカルスケールを変更する this.transform.localScale = this.transform.localScale * coefficient; // ローカル座標でのバウンドサイズを計算する childObjBounds = CalcLocalObjBounds(this.gameObject); } /// <summary> /// 現在オブジェクトのローカル座標でのバウンド計算 /// </summary> private Bounds CalcLocalObjBounds(GameObject obj) { // 指定オブジェクトのワールドバウンドを計算する Bounds totalBounds = CalcChildObjWorldBounds(obj, new Bounds()); // ローカルオブジェクトの相対座標に合わせてバウンドを再計算する // オブジェクトのワールド座標とサイズを取得する Vector3 ObjWorldPosition = this.transform.position; Vector3 ObjWorldScale = this.transform.lossyScale; // バウンドのローカル座標とサイズを取得する Vector3 totalBoundsLocalCenter = new Vector3( (totalBounds.center.x - ObjWorldPosition.x) / ObjWorldScale.x, (totalBounds.center.y - ObjWorldPosition.y) / ObjWorldScale.y, (totalBounds.center.z - ObjWorldPosition.z) / ObjWorldScale.z); Vector3 meshBoundsLocalSize = new Vector3( totalBounds.size.x / ObjWorldScale.x, totalBounds.size.y / ObjWorldScale.y, totalBounds.size.z / ObjWorldScale.z); Bounds localBounds = new Bounds(totalBoundsLocalCenter, meshBoundsLocalSize); return localBounds; } /// <summary> /// 子オブジェクトのワールド座標でのバウンド計算(再帰処理) /// </summary> private Bounds CalcChildObjWorldBounds(GameObject obj, Bounds bounds) { // 指定オブジェクトの全ての子オブジェクトをチェックする foreach (Transform child in obj.transform) { if(!child.gameObject.activeSelf) { // 無効なゲームオブジェクトは無視する continue; } // メッシュレンダラーの存在確認 MeshRenderer renderer = child.gameObject.GetComponent<MeshRenderer>(); if (renderer != null) { // フィルターのメッシュ情報からバウンドボックスを取得する Bounds meshBounds = renderer.bounds; // バウンドのワールド座標とサイズを取得する Vector3 meshBoundsWorldCenter = meshBounds.center; Vector3 meshBoundsWorldSize = meshBounds.size; // バウンドの最小座標と最大座標を取得する Vector3 meshBoundsWorldMin = meshBoundsWorldCenter - (meshBoundsWorldSize / 2); Vector3 meshBoundsWorldMax = meshBoundsWorldCenter + (meshBoundsWorldSize / 2); // 取得した最小座標と最大座標を含むように拡大/縮小を行う if (bounds.size == Vector3.zero) { // 元バウンドのサイズがゼロの場合はバウンドを作り直す bounds = new Bounds(meshBoundsWorldCenter, Vector3.zero); } bounds.Encapsulate(meshBoundsWorldMin); bounds.Encapsulate(meshBoundsWorldMax); } // 再帰処理 bounds = CalcChildObjWorldBounds(child.gameObject, bounds); } return bounds; } }
MeshFilter で Bounds 情報を取得していた個所を MeshRenderer で取得することでよりシンプルに記述できました。
指定のスケールサイズに収めたい子オブジェクトを持つオブジェクトに、スクリプトを設定します。
実行確認
シーンを再生して動作を確認します。
シーンの再生と同時に、子オブジェクトが 1m 立方のサイズに収まるようにオブジェクトが縮小されます。