知って得するunity エディタ拡張編
TRANSCRIPT
知って得する Unity エディタ拡張編
株式会社ハ・ン・ド
プログラマ
馬場翔太
http://baba-s.hatenablog.com/
スライド内容
1. PropertyDrawerでUnityエディタを拡張する
2. AssetPostprocessorでアセットを監視する
3. MenuItemでソースコードを自動生成する
PropertyDrawerで
Unityエディタを拡張する
エディタ拡張は二通りの方法で行えます
• CustomEditorを使用する
スクリプト単位によるエディタ拡張
• PropertyDrawerを使用する
プロパティ単位によるエディタ拡張
このスライドで紹介する機能
使いまわしやすい!
標準で用意されているPropertyDrawer
• RangeAttribute
• MultilineAttribute
RangeAttributeを使うと…
値の入力範囲を制限できるようになります
using UnityEngine;
public class Character : MonoBehaviour
{
[Range(1, 100)] public int Level = 1;
}
MultilineAttributeを使うと…
文字列を複数行で入力できるようになります
using UnityEngine;
public class Character : MonoBehaviour
{
[Multiline(3)] public string Comment;
}
独自のPropertyDrawerを作成して使用する
RangeAttributeやMultilineAttributeのような
PropertyDrawerは自分で作成可能です
独自のPropertyDrawerを作成して使用する
プロパティ名を変更できる
PropertyDrawerを作成してみましょう
using UnityEngine;
public class Character : MonoBehaviour
{
[MyProperty("名前")] public string Name;
}
using UnityEngine;
public class MyPropertyAttribute : PropertyAttribute
{
public string Label;
public MyPropertyAttribute(string label)
{
Label = label;
}
}
1.PropertyAttributeを継承したクラス作成
using UnityEditor;
using UnityEngine;
[CustomPropertyDrawer(typeof(MyPropertyAttribute))]
public class MyPropertyDrawer : PropertyDrawer
{
}
2.PropertyDrawerを継承したクラスを作成
public override void OnGUI(
Rect position,
SerializedProperty property,
GUIContent label)
{
var myPropertyAttribute =
attribute as MyPropertyAttribute;
property.stringValue = EditorGUI.TextField(
position,
myPropertyAttribute.Label,
property.stringValue);
}
3. OnGUI関数をオーバーライドして実装
using UnityEngine;
public class Character : MonoBehaviour
{
[MyProperty("名前")] public string Name;
}
4.作成したPropertyDrawerを使用する
PropertyDrawer作成時の注意
PropertyDrawerを継承したクラスは
Editorフォルダに保存するか
#if UNITY_EDITOR ~ #endifで囲まないと
ビルド時にエラーになるので気をつけてください
MyPropertyAttribute.csを手に入れる
今回作成したMyPropertyAttributeは
Gistで公開しているので参考にしてください
1. 下記のサイトからMyPropertyAttribute.csを取得する
https://gist.github.com/baba-s/9430324
2. 下記のサイトからMyPropertyDrawer.csを取得する
https://gist.github.com/baba-s/9430335
3. MyPropertyAttribute.csを
Unityプロジェクトに追加する
4. MyPropertyDrawer.csを
UnityプロジェクトのEditorフォルダに追加する
PropertyDrawerのサンプル
https://github.com/anchan828/property-drawer-collection
http://blogs.unity3d.com/2012/09/07/property-drawers-in-unity-4/
ネット上で公開されている
PropertyDrawerを使用してみましょう
• CompactAttribute
• EnumLabelAttribute
• PopupAttribute
• SceneNameAttribute
• RegexAttribute
using UnityEngine;
public class Character : MonoBehaviour
{
public Vector3 Position;
}
Vector2やVector3の入力を楽にしたい
CompactAttribute.csを手に入れる
1. 下記のサイトからCompactAttribute.csを取得する
https://github.com/anchan828/property-drawer-collection
2. CompactAttribute.csをUnityプロジェクトに追加する
using UnityEngine;
public class Character : MonoBehaviour
{
[Compact] public Vector3 Position;
}
CompactAttributeを使用する
using UnityEngine;
public class Character : MonoBehaviour
{
public enum JobType
{
SOLDIER,
SORCERER,
}
public JobType Job;
}
列挙型の表示名を変えたい
EnumLabelAttribute.csを手に入れる
1. 下記のサイトからEnumLabelAttribute.csを取得する
https://github.com/anchan828/property-drawer-collection
2. EnumLabelAttribute.csをUnityプロジェクトに追加する
using UnityEngine;
public class Character : MonoBehaviour
{
public enum JobType
{
[EnumLabel("王国兵士")] SOLDIER,
[EnumLabel("魔法使い")] SORCERER,
}
[EnumLabel("ジョブ")] public JobType Job;
}
EnumLabelAttributeを使用する
using UnityEngine;
public class Character : MonoBehaviour
{
public string Job;
public int Money;
}
入力できる値をポップアップで制限したい
PopupAttribute.csを手に入れる
1. 下記のサイトからPopupAttribute.csを取得する
https://github.com/anchan828/property-drawer-collection
2. PopupAttribute.csをUnityプロジェクトに追加する
using UnityEngine;
public class Character : MonoBehaviour
{
[Popup("王国兵士", "魔法使い")]
public string Job;
[Popup(1, 5, 10, 50, 100, 500)]
public int Money;
}
PopupAttributeを使用する
using UnityEngine;
public class LoadSceneButton : MonoBehaviour
{
public string SceneName;
}
ポップアップでシーン名を設定したい
SceneNameAttribute.csを手に入れる
1. 下記のサイトからSceneNameAttribute.csを取得する
https://github.com/anchan828/property-drawer-collection
2. SceneNameAttribute.csをUnityプロジェクトに追加する
using UnityEngine;
public class LoadSceneButton : MonoBehaviour
{
[SceneName] public string SceneName;
}
SceneNameAttributeを使用する
using UnityEngine;
public class Config : MonoBehaviour
{
// IPアドレス
public string ServerAddress;
}
入力できる値を正規表現で制限したい
RegexAttribute.csを手に入れる
1. 下記のサイトからRegexAttribute.csを取得する
https://gist.github.com/baba-s/9430471
2. RegexAttribute.csをUnityプロジェクトに追加する
using UnityEngine;
public class Config : MonoBehaviour
{
// IPアドレス
[Regex(@"^(?:¥d{1,3}¥.){3}¥d{1,3}$", "無効なIPアドレスです!例:'127.0.0.1'")]
public string ServerAddress;
}
RegexAttributeを使用する
PropertyDrawerまとめ
PropertyDrawerを使用すると
スクリプト単位ではなく
プロパティ単位でエディタ拡張が可能なので
多くのスクリプトやプロジェクトで使いまわせます
AssetPostprocessorで
アセットを監視する
AssetPostprocessorを使うと…
• 特定のファイルのみ
Unityプロジェクトに追加不能にできる
• テクスチャやAudioClipの設定を
Unityプロジェクトに追加された時に変更できる
独自のAssetPostprocessorを作成してみる
全角文字が含まれたファイルが
プロジェクトに追加されたら自動で削除する
AssetPostprocessorを作成してみましょう
1.AssetPostprocessorのサブクラスを作成
using UnityEditor;
using UnityEngine;
public class MyAssetPostprocessor : AssetPostprocessor
{
}
2.全角文字を含むか判定する関数を追加
// 1バイト文字で構成された文字列かどうかを判定します
// 1バイト文字のみで構成された文字列の場合 true
// 2バイト文字が含まれている文字列の場合 false
private static bool IsOneByteStr(string str)
{
var bytes = System.Text.Encoding.GetEncoding(932).GetBytes(str);
return bytes.Length == str.Length;
}
http://7ujm.net/NET%20Framework/isOneByteChar.html
3. OnPostprocessAllAssets関数を定義
// すべてのアセットのインポートが終了した際に呼び出されます
// importedAssets :インポートされたアセットのパス
// deletedAssets :削除されたアセットのパス
// movedAssets :移動したアセットの移動後のパス
// movedFromPath:移動したアセットの移動前のパス
private static void OnPostprocessAllAssets(
string[] importedAssets,
string[] deletedAssets,
string[] movedAssets,
string[] movedFromPath)
{
}
4.AssetDatabase.DeleteAssetを利用
foreach (var importedAsset in importedAssets)
{
if (!IsOneByteStr(importedAsset))
{
// 指定されたパスに存在するアセットを削除します
if (AssetDatabase.DeleteAsset(importedAsset))
{
Debug.Log(importedAsset + "を削除しました");
}
}
}
MyAssetPostprocessor.csを手に入れる
今回作成したMyAssetPostprocessorは
Gistで公開しているので参考にしてください
1. 下記のサイトからMyAssetPostprocessor.csを取得する
https://gist.github.com/baba-s/9426854
2. MyAssetPostprocessor.csを
UnityプロジェクトのEditorフォルダに追加する
MenuItemで
ソースコードを自動生成する
MenuItemを使うと…
Unityエディタに独自の機能を追加できます
• すべてのシーンのオブジェクトから
不要なコンポーネントを削除する機能を追加したり…
• 選択中のゲームオブジェクトのパラメータを
一括で設定する機能を追加したり…
独自のMenuItemを作成してみる
ソースコードを自動生成する
MenuItemを作成してみましょう
using UnityEditor;
public static class MyClassCreator
{
// Unityエディタのメニューに
// ソースコード自動生成用のコマンドを追加します
[MenuItem("Tools/Create My Class")]
private static void Create()
{
}
}
1.MenuItemが適用された静的関数を作成
// ソースコードを表す文字列を作成します
var builder = new System.Text.StringBuilder();
builder.AppendLine("public class MyClass");
builder.AppendLine("{");
builder.AppendLine("}");
2.StringBuilderで文字列を作成
// 新しいファイルを作成して
// ソースコードを表す文字列を書き込みます
System.IO.File.WriteAllText(
"Assets/MyClass.cs",
builder.ToString(),
System.Text.Encoding.UTF8);
3.File.WriteAllTextでファイルを作成
// Unityのアセットデータベースをリフレッシュします
AssetDatabase.Refresh(
ImportAssetOptions.ImportRecursive);
4.AssetDatabase.Refreshを実行
5.用意したUnityエディタのコマンドを実行
public class MyClass
{
}
MyClassCreator.csを手に入れる
今回作成したMyClassCreatorは
Gistで公開しているので参考にしてください
1. 下記のサイトからMyClassCreator.csを取得する
https://gist.github.com/baba-s/9446762
2. MyClassCreator.csを
UnityプロジェクトのEditorフォルダに追加する
ソースコードの自動生成のサンプル
MenuItemを使用して
データを定数で管理するクラスの
自動生成を行ってみましょう
• シーン名を定数で管理するクラスの自動生成
• タグ名を定数で管理するクラスの自動生成
• レイヤー名を定数で管理するクラスの自動生成
シーン名を定数で管理するクラスの生成
シーン名の一覧を取得する方法
EditorBuildSettings.scenesを使用すると
シーン名の一覧を取得できます
foreach (var n in EditorBuildSettings.scenes)
{
Debug.Log(n);
}
シーン名を定数で管理するクラスの生成
1. 下記のサイトからSceneNameCreator.csを取得する
https://gist.github.com/baba-s/9286120
2. SceneNameCreator.csをEditorフォルダに追加する
3. Unityエディタの「Tools>Create>Scene Name」を実行する
4. SceneName.csが作成される
シーン名を定数で管理するクラスの生成
/// <summary>
/// シーン名を定数で管理するクラス
/// </summary>
public static class SceneName
{
public const string Title = "Title";
public const string MainMenu = "MainMenu";
}
シーン名を定数で管理するクラスの利用
// タイトル画面に遷移します
Application.LoadLevel(SceneName.Title);
// タイトル画面に遷移します
Application.LoadLevel("Title");
タグ名を定数で管理するクラスの生成
タグ名の一覧を取得する方法
InternalEditorUtility.tagsを使用すると
タグ名の一覧を取得できます
foreach (var n in InternalEditorUtility.tags)
{
Debug.Log(n);
}
タグ名を定数で管理するクラスの生成
1. 下記のサイトからTagNameCreator.csを取得する
https://gist.github.com/baba-s/9287103
2. TagNameCreator.csをEditorフォルダに追加する
3. Unityエディタの「Tools>Create>Tag Name」を実行する
4. TagName.csが作成される
タグ名を定数で管理するクラスの生成
/// <summary>
/// タグ名を定数で管理するクラス
/// </summary>
public static class TagName
{
public const string Untagged = "Untagged";
public const string Respawn = "Respawn";
public const string Finish = "Finish";
public const string EditorOnly = "EditorOnly";
public const string MainCamera = "MainCamera";
public const string Player = "Player";
public const string GameController = "GameController";
public const string Character = "Character";
}
タグ名を定数で管理するクラスの利用
// ゲームオブジェクトがキャラクターかどうか
if (gameObject.CompareTag(TagName.Character))
{
// ...
}
// ゲームオブジェクトがキャラクターかどうか
if (gameObject.CompareTag("Character"))
{
// ...
}
レイヤー名を定数で管理するクラスの生成
レイヤー名の一覧を取得する方法
InternalEditorUtility.layersを使用すると
レイヤー名の一覧を取得できます
foreach (var n in InternalEditorUtility.layers)
{
Debug.Log(n);
}
レイヤー名を定数で管理するクラスの生成
1. 下記のサイトからLayerNameCreator.csを取得する
https://gist.github.com/baba-s/9286212
2. LayerNameCreator.csをEditorフォルダに追加する
3. Unityエディタの「Tools>Create>Layer Name」を実行する
4. LayerName.csが作成される
レイヤー名を定数で管理するクラスの生成
/// <summary>
/// レイヤー名を定数で管理するクラス
/// </summary>
public static class LayerName
{
public const int Default = 0;
public const int TransparentFX = 1;
public const int IgnoreRaycast = 2;
public const int Water = 4;
public const int Character = 8;
public const int DefaultMask = 1;
public const int TransparentFXMask = 2;
public const int IgnoreRaycastMask = 4;
public const int WaterMask = 16;
public const int CharacterMask = 256;
}
レイヤー名を定数で管理するクラスの利用
// ゲームオブジェクトのレイヤーを変更します
gameObject.layer = LayerName.Character;
// ゲームオブジェクトのレイヤーを変更します
gameObject.layer = 8;
MenuItemまとめ
MenuItemを使用すると
Unity APIから取得したデータをもとに
ソースコードの自動生成を行うこともできます
シーン名やタグ名を定数で管理するクラスを
自動生成できるようにしておくことで
タイプセーフなゲーム開発を行うことが可能です
ありがとうございました