MRが楽しい

MRやVRについて学習したことを書き残す

MRTKのGridObjectCollectionを利用してスクリプトからスポーンオブジェクトの整列を行う

本日は MRTK の技術調査枠です。
MRTK の GridObjectCollection を利用してスクリプトからスポーンオブジェクトの整列を行う方法を記事にします。
f:id:bluebirdofoz:20210624231526j:plain

GridObjectCollection

MRTK の GridObjectCollection はオブジェクトを3次元的に整列する機能を提供します。
スクリプトの Update Collection 関数はエディターまたはコードから利用可能です。
docs.microsoft.com

サンプルシーンとスクリプトの作成

サンプルシーンとスクリプトを作成します。
今回は MRTK の GridObjectCollection を利用するため、Unity プロジェクトに MRTK をインポートします。
MRTK のインポート手順は以下の記事などを参考にしてください
bluebirdofoz.hatenablog.com

GridObjectCollection を使って指定プレハブを指定の数スポーンして整列するサンプルスクリプトを作成しました。
・GridObjectCollectionSpawner.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

// GridObjectCollection参照のため
using Microsoft.MixedReality.Toolkit.Utilities;

[RequireComponent(typeof(GridObjectCollection))]
public class GridObjectCollectionSpawner : MonoBehaviour
{
    /// <summary>
    /// Spawnオブジェクト
    /// </summary>
    [SerializeField, Tooltip("Spawnオブジェクト")]
    private GameObject p_SpawnObject;

    /// <summary>
    /// GridObjectCollectionの参照
    /// </summary>
    private GridObjectCollection p_GridObjectCollection;

    public void SpawnObjects(int a_Count)
    {
        if (p_SpawnObject == null) return;

        // GridObjectCollectionの参照を取得する
        p_GridObjectCollection = GetComponent<GridObjectCollection>();

        // 子オブジェクトをいったん全て削除する
        for (int index = gameObject.transform.childCount - 1; index >= 0; --index)
        {
            // DestroyImmediate は呼び出した時点でオブジェクトを削除する
            // Destroy の場合、フレーム完了後に削除されるため、
            // GridObjectCollection で正常に整列できない
            DestroyImmediate(gameObject.transform.GetChild(index).gameObject);
        }

        // 指定数のオブジェクトをスポーンする
        for (int index = 0; index < a_Count; index++)
        {
            // 子オブジェクトとしてスポーンする
            Instantiate(p_SpawnObject, gameObject.transform);
        }

        // GridObjectCollectionを使ってオブジェクトを整列させる
        p_GridObjectCollection.UpdateCollection();
    }
}

再生成を行う場合、GridObjectCollection への影響を避けるため、オブジェクトの削除には DestroyImmediate を利用しています。
Destroy はフレームの完了タイミングでオブジェクトの削除が行われますが、DestroyImmediate は即座に削除が行われます。

DestroyImmediate(gameObject.transform.GetChild(index).gameObject);

また、エディターからも利用可能なように拡張クラスも作成しました。
・GridObjectCollectionSpawnerEditor.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

#if UNITY_EDITOR
using UnityEditor;

// 拡張するクラスを指定する
[CustomEditor(typeof(GridObjectCollectionSpawner))]
public class GridObjectCollectionSpawnerEditor : Editor
{
    private int makeCount;

    // GUIの表示関数をオーバーライドする
    public override void OnInspectorGUI()
    {
        // 元のインスペクター部分を表示
        base.OnInspectorGUI();

        // targetを変換して対象スクリプトの参照を取得する
        GridObjectCollectionSpawner targetScript = target as GridObjectCollectionSpawner;

        // public関数を実行するボタンの作成
        if (GUILayout.Button("SpawnObjectsの実行"))
        {
            targetScript.SpawnObjects(makeCount);
        }

        makeCount = EditorGUILayout.IntField("Count", makeCount);
    }
}
#endif

スクリプトをシーンのオブジェクトに設定します。
今回はスポーンオブジェクトにボタンプレハブを参照しました。
また整列条件は GridObjectCollection に直接設定します。
f:id:bluebirdofoz:20210624231611j:plain

動作確認

最初にランタイムでの動作を確認するため、シーンを再生します。
[Count]変数に生成数を指定して[SpawnObjectsの実行]ボタンをクリックします。
f:id:bluebirdofoz:20210624231638j:plain

指定したオブジェクトが整列して生成されます。
f:id:bluebirdofoz:20210624231648j:plain

シーンを再生せず、エディター上で実行しても利用できます。
f:id:bluebirdofoz:20210624231657j:plain