Unityエディター拡張のカスタムプレビュー
Unityエディター拡張のカスタムプレビュー:
この記事はカヤックUnityアドベントカレンダー2018の16日目の記事になります。
今日の記事ではUnityエディター拡張のカスタムプレビューを紹介していきたいと思います。
プレビューできないものは、エディター拡張を使ってカスタムプレビューを作れば、プレビューができるようになります。
ここから、簡単なSpriteプレビュアーを作りながら、カスタムプレビューを作るためによく使う関数を見てみましょう
この中で注目してほしいのはOnPreviewGUIです。描画の処理はこの関数に書きます

例としては、スプライトを半分のサイズで描画する切り替えボタンを作ります。

なんか、インスペクターが変わりました。
何故かと言うと、CustomEditorアトリビュートはプレビューだけではなく、エンジン内部で実装されたインスペクターなどの要素も上書きするからです。
一般的に、上書きしないようにそのエディタークラスを継承すればいいですけど、Unity内部のSpriteRendererEditorはinternalで継承できないです。
今回はプレビューのカスタムだけ求めるので、CustomPreview*1とObjectPreviewを使えば解決できます。
これでSpriteRendererでスプライトのプレビューができるようになりました。
最終のコードはこうなります
UISpriteAnimationはコマアニメを作るためのコンポーネントですが、エディター再生中しか動きを見れないので、いつでも見れるようにプレビュアーを作りました

サンプルコード
実際の作業に使われないし、実現するためにNGUIコードの改造も少し必要なので、今回割愛します。

上記の例に限らず、パーティクルのプレビューとか、AnimationClipにあるSpriteアニメーションのプレビューとか、3Dモデルのプレビューとか、複数のプレビューを使って各アニメーションのプレビューとか、いろんなことができるでしょう。
明日は浅利さんによる「HTC Vive で両手を使った transform 操作を実現する」の話になります。
はじめに
こんにちは、ソーシャルゲーム事業部のUnityエンジニアの魏です。この記事はカヤックUnityアドベントカレンダー2018の16日目の記事になります。
今日の記事ではUnityエディター拡張のカスタムプレビューを紹介していきたいと思います。
カスタムプレビュー
TextureやMaterialなどプレビューできるものを選択すると、インスペクターのプレビューウィンドウで中身を確認できます。プレビューできないものは、エディター拡張を使ってカスタムプレビューを作れば、プレビューができるようになります。
ここから、簡単なSpriteプレビュアーを作りながら、カスタムプレビューを作るためによく使う関数を見てみましょう
最低限の関数
コンポーネントのコードusing UnityEngine;
public class SpritePreviewer : MonoBehaviour
{
public Sprite sprite;
}
カスタムエディターのコード。Editorフォルダ内へ入れます。この中で注目してほしいのはOnPreviewGUIです。描画の処理はこの関数に書きます
[CustomEditor(typeof(SpritePreviewer))]
public class SpritePreviewerEditor : Editor
{
private GUIContent _title = new GUIContent("スプライトプレビュアー");
// プレビューウィンドウを表示するかどうか
public override bool HasPreviewGUI()
{
return true;
}
// 名前通り、プレビューウィンドウ名を設定する関数
public override GUIContent GetPreviewTitle()
{
return _title;
}
// プレビューウィンドウで描画させたい内容はここで書く
public override void OnPreviewGUI(Rect r, GUIStyle background)
{
var previewer = target as SpritePreviewer;
GUI.DrawTexture(r, AssetPreview.GetAssetPreview(previewer.sprite));
}
}
SpritePreviewerコンポーネントをGameObjectに追加して、Sprite画像をコンポーネントにつけたら、こうなりました。
OnPreviewSettings
AnimationClipのプレビューみたいに、動画の再生や再生速度の調整などのUIはOnPreviewSettings関数で実装されています。例としては、スプライトを半分のサイズで描画する切り替えボタンを作ります。
private bool _halfSize = false;
// プレビューウィンドウで描画させたいものはここで書く
public override void OnPreviewGUI(Rect r, GUIStyle background)
{
var previewer = target as SpritePreviewer;
var rect = _halfSize ? new Rect(r.x, r.y, r.width / 2, r.height / 2) : r;
GUI.DrawTexture(rect, AssetPreview.GetAssetPreview(previewer.sprite));
}
// プレビューウィンドウのヘッダーバーをカスタムする関数
public override void OnPreviewSettings()
{
_halfSize = GUILayout.Toggle(_halfSize, "0.5x");
}
| off | on |
|---|---|
![]() |
![]() |
CustomPreviewAttributeとObjectPreview
新コンポーネントではなく、SpriteRendererに直接プレビューウィンドウを追加すればいいじゃないかと思って、Typeを変更してみたら//[CustomEditor(typeof(SpritePreviewer))]
[CustomEditor(typeof(SpriteRenderer))]
public class SpritePreviewerEditor : Editor
{
...
public override void OnPreviewGUI(Rect r, GUIStyle background)
{
// var previewer = target as SpritePreviewer;
var previewer = target as SpriteRenderer;
var rect = _halfSize ? new Rect(r.x, r.y, r.width / 2, r.height / 2) : r;
GUI.DrawTexture(rect, AssetPreview.GetAssetPreview(previewer.sprite));
}
}

なんか、インスペクターが変わりました。
何故かと言うと、CustomEditorアトリビュートはプレビューだけではなく、エンジン内部で実装されたインスペクターなどの要素も上書きするからです。
一般的に、上書きしないようにそのエディタークラスを継承すればいいですけど、Unity内部のSpriteRendererEditorはinternalで継承できないです。
今回はプレビューのカスタムだけ求めるので、CustomPreview*1とObjectPreviewを使えば解決できます。
// [CustomEditor(typeof(SpritePreviewer))]
[CustomPreview(typeof(SpriteRenderer))]
// CustomPreviewを使うために、ObjectPreviewを継承しなければいけない
// public class SpritePreviewerEditor : Editor
public class SpritePreviewerEditor : ObjectPreview
{
...
}
| 変更前 | CustomPreview |
|---|---|
![]() |
![]() |
最終のコードはこうなります
using UnityEngine;
using UnityEditor;
[CustomPreview(typeof(SpriteRenderer))]
public class SpritePreviewerEditor : Editor
{
private GUIContent _title = new GUIContent("スプライトプレビュアー");
private bool _halfSize = false;
// プレビューウィンドウを表示するかどうか
public override bool HasPreviewGUI()
{
return true;
}
// 名前通り、プレビューウィンドウ名を設定する関数
public override GUIContent GetPreviewTitle()
{
return _title;
}
// プレビューウィンドウで描画させたいものはここで書く
public override void OnPreviewGUI(Rect r, GUIStyle background)
{
var previewer = target as SpritePreviewer;
var rect = _halfSize ? new Rect(r.x, r.y, r.width / 2, r.height / 2) : r;
GUI.DrawTexture(rect, AssetPreview.GetAssetPreview(previewer.sprite));
}
// プレビューウィンドウのヘッダーバーをカスタムする関数
public override void OnPreviewSettings()
{
_halfSize = GUILayout.Toggle(_halfSize, "0.5x");
}
}
使用例
NGUIのUISpriteとUISpriteAnimationに基づいて実装したUISpriteAnimationプレビュアーを紹介します。UISpriteAnimationはコマアニメを作るためのコンポーネントですが、エディター再生中しか動きを見れないので、いつでも見れるようにプレビュアーを作りました

サンプルコード
using UnityEngine;
using UnityEditor;
using System.Reflection;
using System.Collections.Generic;
[CustomEditor(typeof(UISprite))]
// UISpriteInspectorのOnPreviewGUIを利用したいので継承する
public class UISpriteAnimationPreviewer : UISpriteInspector
{
AnimationSetting _animSetting = null;
bool _isPlaying = false;
bool _hasAnimation = false;
float _speedScale = 1f;
protected override void OnEnable()
{
base.OnEnable();
_hasAnimation = false;
var targetSprite = target as UISprite;
var spriteAnim = targetSprite.GetComponent<UISpriteAnimation>();
if (spriteAnim != null)
{
_animSetting = new AnimationSetting(spriteAnim);
_hasAnimation = true;
}
}
// プレビューウィンドウで描画させたい内容はここで書く
public override void OnPreviewGUI(Rect rect, GUIStyle background)
{
var t = target as UISprite;
AnimationSetting setting = _animSetting;
if (!Application.isPlaying && _hasAnimation)
{
var spriteAnim = setting.anim;
BindingFlags flags = BindingFlags.NonPublic | BindingFlags.Instance;
var field = typeof(UISpriteAnimation).GetField("mSpriteNames",flags);
var spriteNames = field.GetValue(spriteAnim) as List<string>;
// UISpriteAnimationのUpdate関数を参考にして、
// 何枚目のUISpriteを表示するか経過時間で計算する
setting.delta += ((float)EditorApplication.timeSinceStartup - setting.lastTime) * _speedScale;
setting.lastTime = (float)EditorApplication.timeSinceStartup;
if (spriteNames.Count > 0)
{
if (_isPlaying)
{
float rate = 1f / spriteAnim.framesPerSecond;
if (rate < setting.delta)
{
setting.delta = Mathf.Repeat(setting.delta, rate);
setting.index++;
}
setting.index %= spriteNames.Count;
// プレビューウィンドウで表示するために、UISpriteのspriteNameを変える
// 保存する時spriteNameの差分が出るかもしれない
t.spriteName = spriteNames[setting.index];
}
// どの画像を表示しているか分かるように、画像名を表示する
EditorGUI.DropShadowLabel(rect, spriteNames[setting.index]);
rect.height -= 15;
}
else
{
return;
}
}
// UISpriteのプレビューを利用して、spriteNameで指定しているUISpriteを描画させる
base.OnPreviewGUI(rect, background);
}
// プレビューウィンドウのヘッダーバーをカスタムする関数
public override void OnPreviewSettings()
{
base.OnPreviewSettings();
if (!_hasAnimation)
{
return;
}
// 再生するボタン
var playButton = EditorGUIUtility.IconContent("preAudioPlayOn");
var pauseButton = EditorGUIUtility.IconContent("preAudioPlayOff");
EditorGUI.BeginChangeCheck();
_isPlaying = GUILayout.Toggle(_isPlaying, _isPlaying ? playButton : pauseButton, (GUIStyle)"preButton");
if (EditorGUI.EndChangeCheck())
{
_animSetting.lastTime = (float)EditorApplication.timeSinceStartup;
}
// AnimationClipのような再生速度を制御するUI
// 実際の作業はUISpriteAnimationのFramerateで調整しているので、ここは練習だけ^_^
var speedScale = EditorGUIUtility.IconContent("SpeedScale", "Speed Scale");
if (GUILayout.Button(speedScale, (GUIStyle)"preButton"))
{
_speedScale = 1;
}
_speedScale = GUILayout.HorizontalSlider(_speedScale, 0f, 5f, (GUIStyle)"preSlider", (GUIStyle)"preSliderThumb");
GUILayout.Box(_speedScale.ToString("0.000"), new GUIStyle("preLabel"));
}
public override bool HasPreviewGUI()
{
return true;
}
//常に再描画される必要があるかどうか
public override bool RequiresConstantRepaint()
{
return _isPlaying;
}
private class AnimationSetting
{
public int index;
public float delta;
public float lastTime;
public UISpriteAnimation anim;
public AnimationSetting(UISpriteAnimation anim)
{
index = 0;
delta = 0;
lastTime = (float)EditorApplication.timeSinceStartup;
this.anim = anim;
}
}
}
CanEditMultipleObjectsとEditor.targetsを利用すれば、下のGIFのように複数のUISpriteAnimationのプレビューもできますが、 実際の作業に使われないし、実現するためにNGUIコードの改造も少し必要なので、今回割愛します。

おわりに
ということで、Unityエディター拡張のカスタムプレビューを紹介しました。上記の例に限らず、パーティクルのプレビューとか、AnimationClipにあるSpriteアニメーションのプレビューとか、3Dモデルのプレビューとか、複数のプレビューを使って各アニメーションのプレビューとか、いろんなことができるでしょう。
明日は浅利さんによる「HTC Vive で両手を使った transform 操作を実現する」の話になります。
*1:CustomPreviewを使えば、1つのObjectで複数のプレビューを作ることができます。
| オリジナルのエンクロージャ: |
| 48698224-e6839f00-ec29-11e8-8a1a-ec740f4207a3.png |



コメント
コメントを投稿