(:3[kanのメモ帳]

個人ゲーム開発者kan.kikuchiのメモ的技術ブログ。月木更新でUnity関連がメイン。

(:3[kanのメモ帳]

Unityエディタの左上に独自の機能を追加出来るEditorTool【Unity】【エディタ拡張】


このエントリーをはてなブックマークに追加


この記事でのバージョン
Unity 2019.1.0b3


はじめに

先日、面白そうなエディタ拡張のツイートを見かけました。



内容もさることながら、初めて見たタイプの拡張で驚いたので、

今回はこれを調べて試してみた感じの記事です。


EditorTool

先程のツイートはEditorToolという2019.1で追加された機能が使われています。



これは何かを実行するボタンを付けるというよりかは、

Unityにデフォルトで付いている移動や回転のように、

「Hierarchy上のオブジェクトを操作するような機能」を追加するのが主な使い方っぽいです。


例えばドキュメントに載っているサンプルだと

「オブジェクトを一方向だけに移動させる機能」を実装しています。

using System;
using UnityEngine;
using UnityEditor;
using UnityEditor.EditorTools;

// Tagging a class with the EditorTool attribute and no target type registers a global tool. Global tools are valid for any selection, and are accessible through the top left toolbar in the editor.
[EditorTool("Platform Tool")]
class PlatformTool : EditorTool {
  // Serialize this value to set a default value in the Inspector.
  [SerializeField]
  Texture2D m_ToolIcon;

  GUIContent m_IconContent;

  void OnEnable() {
    m_IconContent = new GUIContent() {
      image = m_ToolIcon,
      text = "Platform Tool",
      tooltip = "Platform Tool"
    };
  }

  public override GUIContent toolbarIcon {
    get { return m_IconContent; }
  }

  // This is called for each window that your tool is active in. Put the functionality of your tool here.
  public override void OnToolGUI(EditorWindow window) {
    EditorGUI.BeginChangeCheck();

    Vector3 position = Tools.handlePosition;

    using (new Handles.DrawingScope(Color.green)) {
      position = Handles.Slider(position, Vector3.right);
    }

    if (EditorGUI.EndChangeCheck()) {
      Vector3 delta = position - Tools.handlePosition;

      Undo.RecordObjects(Selection.transforms, "Move Platform");

      foreach (var transform in Selection.transforms)
        transform.position += delta;
    }
  }
}

f:id:kan_kikuchi:20190213134232g:plain:w700


より分かりやすいように、諸々のコメントを追加して

選択している全Transformを回転させるメニューを実装してみたのが以下のものになります。

ただし、Sceneビューを開いていないと使えません。(OnToolGUIが呼ばれない)

using UnityEngine;
using UnityEditor;
using UnityEditor.EditorTools;

/// <summary>
/// 選択している全Transformを回転させるメニュー
/// </summary>
[EditorTool("Rotate Tool")]
class RotateTool : EditorTool {
  
  //アイコンの画像
  [SerializeField]
  private Texture2D _toolIcon = null;

  //アイコンの画像や説明などGUI要素を設定するもの
  private GUIContent _content = null;

  //GUI要素を取得する用のプロパティ
  public override GUIContent toolbarIcon {
    get { return _content; }
  }

  //=================================================================================
  //初期化
  //=================================================================================

  private void OnEnable() {
    //GUIの要素を作成
    _content = new GUIContent() {
      image   = _toolIcon,     //アイコンの画像
      text    = "Rotate Tool", //メニューの名前
      tooltip = "回転します!"   //メニューの説明
    };
  }

  //=================================================================================
  //更新
  //=================================================================================

  /// <summary>
  /// このツールを選択中に呼ばれ続けるメソッド(Sceneビューを開いてないと呼ばれない)
  /// </summary>
  public override void OnToolGUI(EditorWindow window) {
    //選択している全Transformを回転させる
    foreach (var transform in Selection.transforms) {
      transform.rotation = Quaternion.Euler(transform.rotation.eulerAngles + new Vector3(0, 5, 0));
    }
  }
}

f:id:kan_kikuchi:20190213142134g:plain:w700


なお、メニューの名前(Rotate Tool)は右クリックした時、

メニューの説明(回転します!)はカーソルを上に置いてる時に表示されます。


f:id:kan_kikuchi:20190213141329j:plain
f:id:kan_kikuchi:20190213141338j:plain


また、メニューは複数作る事も可能で、その場合は右クリックで使うものを選びます。


f:id:kan_kikuchi:20190213141601j:plain


さらにEditorToolの第二引数を使う事で、

特定のコンポーネントが付いているオブジェクトを選択してる時にだけ使えるメニュー

というのも実装可能です。

//MeshFilterが付いてるオブジェクトを選択している時にだけ実行可能
[EditorTool("Vertex Tool", typeof(MeshFilter))]

f:id:kan_kikuchi:20190213142116j:plain
f:id:kan_kikuchi:20190213142122j:plain


ちなみにアイコン画像はInspectorから設定する事が出来ます。

//アイコンの画像
[SerializeField]
private Texture2D _toolIcon = null;

f:id:kan_kikuchi:20190213135509j:plain


EditorToolをボタン代わりに使う

EditorToolはOnActivateというメソッドで選択された瞬間が分かり、

(OnDeactivateで選択が解除された瞬間も分かる)

EditorTools.RestorePreviousToolを実行する事で前選択していたツールに戻す事も可能なので、

ボタン代わりにする事も出来ます。


例えば以下の記事の内容を使えば、



「Scenes in Buildの一番上に登録されているシーンから再生を開始するボタン」

(再生が終わったら再生前に開いていたシーンに戻る)

なんてものも作れます。(EditorPlayer.csも必要)



f:id:kan_kikuchi:20190213144042g:plain


ちなみに、EditorTools.RestorePreviousToolはOnActivateとOnDeactivateからは使えません。

public override void OnActivate() {
  EditorTools.RestorePreviousTool();
}

InvalidOperationException: Attempting to set the active tool from EditorTool.OnActivate or EditorTool.OnDeactivate. This is not allowed.

f:id:kan_kikuchi:20190213143908j:plain