はじめに
作ってみたエディタ拡張をいろいろと紹介したいと思います。
UniRxでObservableにしてみたEditorWindowやSerializeFieldに関するものなどいろいろ作ってみたのでどうぞみてみてください。
環境
- Unity2018.3.0f2
- Scripting Runtime Version (.NET 4.X Equivalent)
- UniRx ver6.2.2
作ったみたリスト
今回作ってみたものリストとなります。
作ったコードは個人のGitHubGistにて公開しましたのでご自由にご利用ください。
- UniRxなエディターウィンドウ
- リフレクションでSerializeFieldをゲットしnullだったらエラーを出す
- SerializeField付きスクリプト生成ウィザード
- EditorのAssemblyDefinitionを右クリックメニューで追加できるようにしてみた
解説
作ってみたものを解説いたします。
UniRxでエディターウィンドウをObservableにしてみた
コード一部略
public abstract class ObservableWindowBase : EditorWindow
{
Subject<Unit> update;
Container Root = new Container();
public IObservable<Unit> UpdateAsObservable()
{
return update ?? (update = new Subject<Unit>());
}
public TElement AddElement<TElement>(TElement element) where TElement : IElement
{
return Root.AddElement<TElement>(element);
}
void Update()
{
if (update != null) { update.OnNext(Unit.Default); }
}
void OnGUI()
{
(Root as IElement).Draw();
}
}
public class ObservableTestEditorWindow : ObservableWindowBase
{
[MenuItem("Window/ObservableTestEditorWindow")]
static void Init()
{
GetWindow<ObservableTestEditorWindow>();
}
Toggle updateToggle;
void OnEnable()
{
new Button(this, "label1")
.OnClickAsObservable()
.Subscribe(_ => { Debug.Log("clicked button 1"); });
new Button(this, "label2")
.OnClickAsObservable()
.Subscribe(_ => { Debug.Log("clicked button 2"); });
updateToggle = new Toggle(this, false, "isUpdate");
updateToggle.OnValueChangedAsObservable().Subscribe(v =>
{
Debug.Log("on value changed to:" + v);
});
UpdateAsObservable()
.Where(x => updateToggle.RValue.Value)
.Subscribe(_ =>
{
Debug.Log("Update...");
});
}
}
public class Button : IElement
{
Subject<Unit> onClick;
string label;
public Button(ObservableWindowBase window, string label)
{
window.AddElement(this);
this.label = label;
}
void IElement.Draw()
{
if (GUILayout.Button(label)) onClick?.OnNext(Unit.Default);
}
public IObservable<Unit> OnClickAsObservable()
{
return onClick ?? (onClick = new Subject<Unit>());
}
}
実際にObservableになっているのはEditorWindow.Updateのみですが、
UniRxのコードっぽく書けるようにできました。
しかしながら試しに作ってみた程度のものでございます。。
SerializeFieldをリフレクションでゲットしてnullならエラー
現在のシーンでSerializeFieldのオブジェクトがnullのものがあったらエラーを出します。
// 必要なusing
using UnityEngine;
using UnityEditor;
using System.Reflection;
using System.Linq;
[MenuItem("Test/SerializeField Null Check in this Scene")]
static void SerializeFieldNullCheck()
{
foreach (var component in GameObject.FindObjectsOfType<Component>())
{
var fields = component.GetType()
.GetFields(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy)
.SelectMany(x => x.CustomAttributes.Where(t => t.AttributeType == typeof(SerializeField)).Select(_ => x));
foreach (var field in fields)
{
var value = field.GetValue(component);
UnityEngine.Assertions.Assert.IsNotNull(value, "Error! [SerializeField] " + field.Name + " is null !!");
}
}
}
リフレクションでSerializeFieldをゲットする
FieldInfo
のCustomAttributes
でAttributeType
がSerializeFieldなものでとってこれます。
そのフィールドのGetValue
の値がnullだったらエラー表示する、という感じにすればOKです。
用途
外れてはいけないSerializeFieldがいつのまにか(誰かが気づかぬまま?!)外してしまっていることに気づかないことがあったとしても、上のようなテストコードを書いてCIするといいかも?!
追記(2021/09)
UnityEngine.Object
がnullかどうかの判定でUnity2019.4系では
SerializeFieldがnullかどうかは下記のようにとらないといけない模様でした。
var value = field.GetValue(component);
if (field.FieldType.IsSubclassOf(typeof(UnityEngine.Object)) &&
value as UnityEngine.Object == null)
SerializeFieldをリンクしてスクリプト生成ウィザード
スクリプトの生成とSerializeFieldのリンクを同時に行ってくれるスクリプト生成ウィザードです。
Editorフォルダ以下に置くEditorのAssemblyDefinitionを右クリックメニューから作成できるようにするエディタ拡張
EditorのAssemblyDefinitionを作成するときの手順が少し面倒だったため一発で作成できるのをつくってみました。
まとめ
いかがでしたでしょうか。
役に立ちそうなものがあれば幸いです。