编辑器工具的编写 编辑器工具大致可分为脚本Inspector拓展和独立窗口两大部分,这两大部分又涉及SceneView绘制和Preview窗口绘制等。
Inspector面板拓展
参考
Unity游戏开发拓展编辑器1 拓展Inspector面板
Unity游戏开发拓展编辑器2
脚本序列化 编辑器拓展
新建一个MonoBehaviour脚本,命名为Enemy_Type1
1 2 3 4 5 6 7 8 using UnityEngine;public class Enemy_Type1 : MonoBehaviour { public int hp = 100 ; public float speed = 300 ; public GameObject go; }
我们在Project窗口中按“Assets——Scripts——AG——Chapter2——EditorExtensions——Editor”结构创建一系列文件夹
在Editor文件夹内新建Enemy_Type1Inspector
文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 using UnityEngine;using UnityEditor;[CustomEditor(typeof(Enemy_Type1)) ] public class Enemy_Type1Inspector : Editor { public override void OnInspectorGUI () { base .OnInspectorGUI(); var concertTarget = base .target as Enemy_Type1; if (GUILayout.Button("preset1" )) { concertTarget.hp = 100 ; concertTarget.speed = 600 ; } if (GUILayout.Button("preset2" )) { concertTarget.hp = 200 ; concertTarget.speed = 550 ; } } }
将Enemy_Type1
脚本挂载时,就会出现应用预设的按钮
在实际开发中,更推荐采用直接获取serializedObject
的写法。下面就来演示一下如何获取序列化字段的绘制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 using UnityEngine;using UnityEditor;[CustomEditor(typeof(Enemy_Type1)) ] public class Enemy_Type1Inspector : Editor { public override void OnInspectorGUI () { serializedObject.Update(); var hpProp = serializedObject.FindProperty("hp" ); var speedProp = serializedObject.FindProperty("speed" ); var goProp = serializedObject.FindProperty("go" ); using (var change = new EditorGUI.ChangeCheckScope()) { EditorGUILayout.PropertyField(hpProp,new GUIContent("敌人生命" )); EditorGUILayout.PropertyField(speedProp,new GUIContent("敌人速度" )); EditorGUILayout.PropertyField(goProp,new GUIContent("敌人挂载" )); if (GUILayout.Button("preset1" )) { hpProp.intValue = 100 ; speedProp.floatValue = 600 ; } if (GUILayout.Button("preset2" )) { hpProp.intValue = 200 ; speedProp.floatValue = 550 ; } if (change.changed) { serializedObject.ApplyModifiedProperties(); } } } }
我们在脚本序列化 中演示过脚本Inspector拓展方法,当我们使用serializedObject拓展时,注意在OnInspector()
内的代码规范,我们比较一下它和直接从“target”中获取对象区别
使用target和serializedObject都能获得脚本的序列化信息,前者属于直接获取,后者属于在编辑器序列化反序列化流程中获取。前者不能获取private字段,后者能够获取[SerializeField] private字段
我们在脚本序列化 中提到过EditorGUILayout.PropertyField
是使用Unity原生方式绘制序列化对象的方法,它返回的是bool。相对地,我们使用EditorGUILayout.IntField
、EditorGUILayout.TextField
等方法就属于自定义绘制,它们返回应该返回的值
我们在以前的文章中写过property.intValue = EditorGUILayout.IntField("主键", property.intValue)
这样的写法,它等价于EditorGUILayout.PropertyField(property,new GUIContent("主键"))
我们在以前的文章脚本序列化 中写过EditorGUI.BeginChangeCheck()
和EditorGUI.EndChangeCheck()
的来监听面板修改的办法,这里使用的是using (var change = new EditorGUI.ChangeCheckScope()){}
的方法,更加直观,相对应的,Unity也提供了HorizontalScope
、ScrollViewScope
等方法,可以配合Unity游戏开发拓展编辑器2 中的Inspector面板拓展部分来扩宽思路
下面将展示PreviewGUI的使用。PreviewGUI可以提供监视面板下的内容预览、调试等
1 2 3 4 5 6 7 8 9 10 11 12 13 public override bool HasPreviewGUI (){ return true ; } public override GUIContent GetPreviewTitle (){ return new GUIContent("Enemy_Type1 Debug" ); } public override void OnPreviewGUI (Rect r, GUIStyle background ){ base .OnPreviewGUI(r, background); GUILayout.Box("EnemyState:..." ); }
我们也在Unity游戏开发拓展编辑器2 里面介绍过创建预览窗口的信息,不过在那里我们使用的是[CustomPreview]特性并继承ObjectView
基类,在这里我们override的是Editor
基类。
注意上图中的“GUILayout.Box
”是从下向上排列的。
使用EidtorWindow自定义窗口 在Unity开发中我们还可以使用自定义窗口进行编辑器拓展。
在Editor文件夹内新建BattleDebugerEditorWindow
文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 using UnityEngine;using UnityEditor;public class BattleDebugEditorWindow : EditorWindow { [MenuItem("Tools/Battle Debugger" ) ] static void Setup () { GetWindow<BattleDebugEditorWindow>(); } private void OnGUI () { if (GUILayout.Button("Generate Enemy_Type1" )) { } if (GUILayout.Button("Generate Enemy_Type1 x10" )) { } if (GUILayout.Button("Killed All Enemy" )) { } } private void Awake () { SceneView.duringSceneGui += DuringSceneGUIBind; } private void OnDestroy () { SceneView.duringSceneGui -= DuringSceneGUIBind; } private void DuringSceneGUIBind (SceneView sceneView ) { var enemies = GetEnemies(); for (int i = 0 ; i < enemies.Length; i++) { var enemy = enemies[i]; Handles.DrawWireCube(enemy.Position, new Vector3(0.5f , 1f , 0.5f )); } } private struct EnemyInfo { public Vector3 Position { get ;set ; } } private EnemyInfo[] GetEnemies () { return new EnemyInfo[] { new EnemyInfo(){ Position = new Vector3(0f ,0f ,0f )}, new EnemyInfo(){ Position = new Vector3(1.5f ,0f ,0f )}, }; } }
我们在Unity的工具栏里面选择“Tools——Battle Debugger”,就可以弹出我们自定义的窗口了
我们在Unity游戏开发拓展编辑器1 里面就已经介绍了Scene窗口拓展的方法,我们在这里总结一下
直接在窗口启动函数里面调用GetWindow(EditorWindow editorWindow)
就可以打开窗口,不需要调用EditorWindow.Show
方法
我们在MonoBehaviour脚本里面的OnDrawGizmo
方法中提供的监听代码是在Game窗口下显示的,Gizmo绘制是在游戏中显示。我们在编辑器拓展中使用的是Handles类绘制的,它是在Scene窗口中显示。
关联游戏配置数据 在游戏开发中,策划与程序需要有良好的配置环境来处理数据,可以直接使用ScriptableObject来处理数据,或者通过Excel转JSON的形式将数据表直接从Excel里抓取过来,也可以使用Sqlite进行数据存储。不过Sqlite在跨平台上存在一些兼容性问题。这里只介绍前两种进行数据配置的方案。
ScriptableObject数据配置