【Unity】【エディタ拡張】HandleCapがクリックされたことを取得する

f:id:halya_11:20180320002113g:plain

かなり地味ですがわかりづらいのでメモ。
エディタ拡張でHandleCapのクリックイベントを取得する方法です。

ソースコード

いきなりですがソースコードです。

using UnityEngine;
using UnityEditor;

public class SceneGUIExample : MonoBehaviour {
}

[CustomEditor(typeof(SceneGUIExample))]
public class SceneGUIExampleEditor : Editor{

    private bool _cached = false;
    private int _controlId;
    private SceneGUIExample _target;
    private bool _isSelected = false;

    private void OnSceneGUI(){

        if (!_cached) {
            // ControlIdを割り振る
            _controlId = GUIUtility.GetControlID(FocusType.Passive);

            _target = target as SceneGUIExample;
            _cached = true;
        }

        // 今のEventTypeにより処理を分ける
        if (Event.current.type == EventType.Repaint || Event.current.type == EventType.Layout) {
            Handles.color = _isSelected ? Color.red : Color.white;
            Handles.SphereHandleCap(_controlId, _target.transform.position, _target.transform.rotation, 1.0f, Event.current.type);
        }
        else if (Event.current.type == EventType.MouseDown) {
            // クリックした部分にあるもののControlIdが一致したら選択中とする
            _isSelected = HandleUtility.nearestControl == _controlId;
        }
    }
}

まず、このHandleCapを識別するためのControlIdを取得する必要があります。
これに関してはGUIUtility.GetControlID(FocusType.Passive)で取得することでUnityにより割り振られます。

docs.unity3d.com

また、OnSceneGUIはレイアウトの前処理や描画時、マウスイベント時など様々なイベントをトリガーとして呼ばれます。
これを判定するのがEvent.current.typeです。

HandleCapのイベントを取る時には、上例のようにイベントがEvent.LayoutとEvent.Repaintの時の両方でHandleCapのメソッドを呼びます。
(Handles.Colorの変更はRepaint時だけでも問題ないです)

そしてMouseDownイベントでクリックを判定します。
HandleUtility.nearestControlでクリック箇所にあるHandleのControlIdが得られるため、これを最初に発行したControlIdと比較します。

これでHandleのクリックを取ることができます。
もちろん他のHandleCapでも同様です。

ちなみにEvent.Layoutは全てのイベントの前に行われる処理で、自動レイアウトシステムから使われるとのこと。
詳細は不明ですが、Event.Layoutで処理を行わないとクリック判定が行われませんでした。

docs.unity3d.com

また、Event.Repaintは描画時の処理で、ここでHandleの処理を書かないと描画がされませんでした。

docs.unity3d.com

参考

gui - Use Unity Handles for interaction in the scene view - Game Development Stack Exchange

Cygames Tech Fes フォローアップ: 内製タイムラインツールCuttの紹介 | Cygames Engineers' Blog