はなちるのマイノート

Unityをメインとした技術ブログ。自分らしくまったりやっていきたいと思いますー!

【Unity】「Selection Groups」を活用して複数のGameObjectをグループ化する(GoQLによる検索も便利)

はじめに

今回はSelection Groupsについて取り上げたいと思います。

Selection GroupはUnity公式パッケージで、複数のGameObjectをグループ化・操作することができます。

Selection Group は、Unity のワークフローをより快適にします。 複数の GameObject を共通の名前でグループ化して、グループ単位で操作を行うことができます。

docs.unity3d.com

またGoQLという条件を満たすGameObjectを抽出する機能は非常に便利なので是非習得してみるとよいと思います。

Selection Groupsを用いて○○Managerを抽出する

GoQL は「GameObject Query Language」の略です。 シーンのヒエラルキーから GameObject のセットを構築するために使用されます。
この API は単一のクエリ文字列をパラメーターとして受け取り、そのクエリにマッチする GameObject のセットを返します。

GoQL オーバービュー | Selection Groups | 0.9.1-preview


ただ現段階(2023/4/9時)ではExperimentalパッケージで中身が変更される可能性もあります。注意してください。

インストール方法

メニューバーのWindow -> PackageManagerからPackage Managerを立ち上げ、Add package from git URLにて以下の文字列を入力し、Addボタンを押せばインストールできます。

com.unity.selection-groups

Add package from git URL...

使い方

Selection Groups Windowの立ち上げ方法

メニューバーのWindow -> General -> Selection Groupsを選択し、Selection Groups Windowを立ち上げます。

Selection Groups Windowの立ち上げ方

Selection Groupの作成

Selection Groups Window+ボタンを押し、Create Empty Groupを選択すると、HierarchySelectionGroupコンポーネントがアタッチされたGameObjectが生成されます。

Create Empty Group
Selection Groupの作成

Selection GroupにGameObjectを追加する

Hierarchyから対象のGameObjectSelection Groupsの対象Selection Groupへとドラッグ&ドロップします。

グループにGameObjectを追加する

このときSelection GroupコンポーネントがアタッチされているGameObjectと階層構造になるわけではないということに注意してください。

Selection Groupコンポーネント

Selection Groupコンポーネント
プロパティ 説明
Group name GameObject グループの名前と同じです。
Color Selection Groups Window に表示されているグループのカラーです。
Group Query クエリを指定すると、そのクエリに合致する階層の GameObject を自動的にグループに割り当てます。詳しくは、GoQL のドキュメントを参照してください。

Group name

まずGroup nameを変更すると、GameObjectの名前も連動して変更されます。

Group name

Color

Colorに関してはSelection Groups Windowに表示されるグループカラーを変更することができます。

Color

Group Query

Group QuerySelection Groupsの一番特筆すべき機能といってもよいでしょう。

Group QueryGoQL(GameObject Query Language)という、イメージSQLGameObject版のような問い合わせ言語を用いて、条件に合ったGameObjectを自動的にグループに割り当てることができます。

クエリを指定すると、そのクエリに合致する階層の GameObject を自動的にグループに割り当てます。

例えば*ManagerGroup Queryに記載すると、GameManagerのようにManagerで終わるGameObjectをグループに割り当てることができます。

Group Query

グループに対して何かしらの操作をする

Hierarchy上

グループ内のGameObjectHierarchy上ですべて選択したい場合は、グループをダブルクリックするか、グループ上で右クリックしてSelect All Group Membersを選択します。

Select All Group Members

スクリプト上

コード上では、SelectionGroupコンポーネントに対して以下のコードを書くことでアクセスすることができます。

public class Test : MonoBehaviour
{
    [SerializeField] private SelectionGroup selectionGroup;

    private void Start()
    {
        // グループ内のゲームオブジェクトを取得する
        IList<GameObject> gameObjects = selectionGroup.Members;
        
        // 非アクティブにする
        foreach (var item in gameObjects)
        {
            item.SetActive(false);
        }
    }
}

補足

インストールするとPackages/Selection Groupsにパッケージが入るわけですが、そこまで規模の大きなものではないので割と読みやすく参考になったりします。

フォルダ構成とか、Testの書き方、AssemblyInfo.csとか結構参考になります。

というかチラッと眺めていたら気づいたのですが、Unity公式のパッケージなのにハンガリアン記法使っていないのですね。

// MonoBehavior.cs
namespace UnityEngine
{
  /// <summary>
  ///   <para>MonoBehaviour is a base class that many Unity scripts derive from.</para>
  /// </summary>
  [NativeHeader("Runtime/Mono/MonoBehaviour.h")]
  [NativeHeader("Runtime/Scripting/DelayedCallUtility.h")]
  [RequiredByNativeCode]
  [ExtensionOfNativeClass]
  public class MonoBehaviour : Behaviour
  {
    private CancellationTokenSource m_CancellationTokenSource;
    // 以下省略
// SelectionGroups内のコード
namespace Unity.SelectionGroups
{
    /// <summary>
    /// A HashSet which retains order of items as they are added or inserted.
    /// or
    /// A List which only contains unique references.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    internal class OrderedSet<T> : IList<T>
    {
        List<T> items = new List<T>();
        HashSet<T> uniqueIndex = new HashSet<T>();

        public IList<T> List => items;

        public T this[int index] { get => items[index]; set => items[index] = value; }

        public int Count => items.Count;

        public bool IsReadOnly => false;

        public void Add(T item)
        {
            if(!uniqueIndex.Contains(item)) {
                items.Add(item);
                uniqueIndex.Add(item);
            }
        }

        public void Clear()
        {
            items.Clear();
            uniqueIndex.Clear();
        }

        public bool Contains(T item)
        {
            return uniqueIndex.Contains(item);
        }

        public void CopyTo(T[] array, int arrayIndex)
        {
            items.CopyTo(array, arrayIndex);
        }

        public IEnumerator<T> GetEnumerator()
        {
            return items.GetEnumerator();
        }

        public int IndexOf(T item)
        {
            return items.IndexOf(item);
        }

        public void AddRange(IEnumerable<T> objectReferences)
        {
            foreach (var i in objectReferences)
            {
                Add(i);
            }
        }

        public void Insert(int index, T item)
        {
            if(!uniqueIndex.Contains(item)) {
                items.Insert(index, item);
                uniqueIndex.Add(item);
            }
        }

        public bool Remove(T item)
        {
            uniqueIndex.Remove(item);
            return items.Remove(item);
        }

        public void Remove(IList<T> items)
        {
            uniqueIndex.ExceptWith(items);
            foreach(var i in items) {
                this.items.Remove(i);
            }
        }

        public void RemoveAt(int index)
        {
            var item = items[index];
            uniqueIndex.Remove(item);
            items.RemoveAt(index);
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return items.GetEnumerator();
        }

    }
}

昔の名残なのか何でこんな変数名つけてんだろうなとか思っていた(もしかしたらC++のエンジン側の影響??)のですが、パッケージの中身は割とMicrosoftの規約に従っていそうな雰囲気を感じました。

learn.microsoft.com

ちょっと面白かったので補足として掲載しておきます。

さいごに

GoQLをもっと深ぼる記事を書いてみたさあります。

docs.unity3d.com