ゴマちゃんフロンティア

アザラシが大好きなエンジニアの開発日記です

【Unity】エディタ拡張でシーンビューからオブジェクトを選択した際の挙動を変えてみたお話

time 2019/08/07

今回は久々にUnityのエディタ拡張に手を出してみました。
というのも、私はUnityの「再生中にシーンビューからオブジェクトを選択すると子が優先して選択される」挙動が気に入らないのです!

子を選択したい場合はヒエラルキーを展開して行うことがほとんどなので、最初から親を選択して欲しいです。再生中にオブジェクト位置の微調整を行いたい時も子が選択されて面倒です。
しかしUnity2019になっても挙動は変わらないので仕様なのでしょう。であれば、エディタ拡張を使って自分で変えてしまいます。

ということで以下のスクリプトを作成しました。これをEditorフォルダに配置します。

using UnityEngine;
using UnityEditor;

public class SelectionHierarchyParent
{
    private static GameObject selectionTarget;
    private static GameObject lastSelectedParent;

    [InitializeOnLoadMethod]
    public static void Init()
    {
        Selection.selectionChanged += () => {
            if (EditorApplication.isPlaying && IsFocusedSceneViewWindow()) {
                SelectRootParent();
            }
        };

        EditorApplication.update += () => {
            if (selectionTarget != null && IsFocusedSceneViewWindow()) {
                if (lastSelectedParent == null || lastSelectedParent.name != selectionTarget.name) {
                    Selection.activeGameObject = selectionTarget;

                    lastSelectedParent = selectionTarget;
                    selectionTarget = null;
                }
            }
        };
    }

    private static void SelectRootParent()
    {
        Transform target = null;
        var parentObject = Selection.activeGameObject.transform.parent;

        while (true) {
            if (parentObject == null) {
                break;
            }

            target = parentObject;
            parentObject = parentObject.transform.parent;
        }

        if (target != null) {
            selectionTarget = target.gameObject;
        } else {
            lastSelectedParent = Selection.activeGameObject;
        }
    }

    private static bool IsFocusedSceneViewWindow()
    {
        if (EditorWindow.focusedWindow == null) {
            return false;
        }

        return EditorWindow.focusedWindow.titleContent.text == "Scene";
    }
}

init()時にSelection.selectionChangedEditorApplication.updateにそれぞれメソッドを設定します。
前者はシーンビューのオブジェクト選択時にSelectRootParent()を呼び出し、親オブジェクトの情報を取得します。
後者はselectionChangedで取得した親オブジェクト情報があれば、Selection.activeGameObjectにそのオブジェクトを代入し選択させます。

ポイントは「Selection.activeGameObjectへの代入条件とタイミング」です。
selectionChangedで指定したメソッド内でSelection.activeGameObjectに代入してもオブジェクトは選択されません。そのため代入処理は別で記述する必要があります。
また代入前に「最後に選択された親」と「選択対象の親」の名前を比較し、一致する場合は代入しないようにします。このチェック処理がない場合、選択されたオブジェクトの子をクリックしても、再度親オブジェクトが選択されてしまいます。

これが適用された状態で、実際に選択してみるとこんな感じです。

選択されるまで多少のタイムラグがありますが、まあほとんど気にならないでしょう。気に入らない挙動があっても自前でカスタマイズして変えられるのがUnityの良いところですね。

down

コメントする