赞
踩
工欲善其事必先利其器
引言: 在项目开发中,编辑器扩展为开发者提供了开发自定义工具的功能,让开发者更加便利地使用编辑器开发项目。近期小生一直在学习编辑器扩展的知识,发现网络上关于编辑器知识点的博客较为零散且混乱。当然,有一些大佬已经总结的很好了,小生这就算是狗尾续貂,主要目的为自我学习,近期会整理一系列编辑器相关的博客,分享给每一位在学习道路上奋斗的童鞋。如若博客中存在错误,还请大佬们不吝赐教。所有参考的博客或者视频来源将在文末展示。
开发版本: Unity 2018.1.3f1
相关博客传送门
一、编辑器开发入门
八、EditorPrefs、ScriptableObject、Undo
using System.Collections.Generic;
using UnityEngine;
public class InspectorExample : MonoBehaviour
{
//序列化
[SerializeField]
public int[] intArray;
[SerializeField]
public List<string> stringList;
}
using UnityEditor; [CustomEditor(typeof(InspectorExample))] public class InspectorExampleEditor : Editor { private SerializedProperty intArray; private SerializedProperty stringList; private void OnEnable() { intArray = serializedObject.FindProperty("intArray"); stringList = serializedObject.FindProperty("stringList"); } public override void OnInspectorGUI() { serializedObject.Update(); EditorGUILayout.PropertyField(intArray,true); EditorGUILayout.PropertyField(stringList, true); serializedObject.ApplyModifiedProperties(); } }
ReorderableList可以实现通过鼠标拖动,修改列表元素的排列顺序,注意其命名空间为UnityEditorInternal
目标类挂载在场景对象上
using System.Collections.Generic;
using UnityEngine;
public class TargetExample : MonoBehaviour
{
[SerializeField]
public List<string> stringArray;
}
编辑器类放在Editor文件夹中
using UnityEngine; using UnityEditor; using UnityEditorInternal; [CustomEditor(typeof(TargetExample))] public class TargetExampleEditor : Editor { private ReorderableList _stringArray; private void OnEnable() { _stringArray = new ReorderableList(serializedObject, serializedObject.FindProperty("stringArray") , true, true, true, true); //自定义列表名称 _stringArray.drawHeaderCallback = (Rect rect) => { GUI.Label(rect, "StringArray"); }; //自定义绘制列表元素 _stringArray.drawElementCallback = (Rect rect,int index,bool selected,bool focused) => { //根据index获取对应元素 SerializedProperty item = _stringArray.serializedProperty.GetArrayElementAtIndex(index); rect.height = EditorGUIUtility.singleLineHeight; rect.y += 2; EditorGUI.PropertyField(rect, item, new GUIContent("Element "+index)); }; //当添加新元素时的回调函数,自定义新元素的值 _stringArray.onAddCallback = (ReorderableList list) => { if (list.serializedProperty != null) { list.serializedProperty.arraySize++; list.index = list.serializedProperty.arraySize - 1; SerializedProperty item = list.serializedProperty.GetArrayElementAtIndex(list.index); item.stringValue = "Default Value"; } else { ReorderableList.defaultBehaviours.DoAddButton(list); } }; //当删除元素时候的回调函数,实现删除元素时,有提示框跳出 _stringArray.onRemoveCallback = (ReorderableList list) => { if (EditorUtility.DisplayDialog("Warnning","Do you want to remove this element?","Remove","Cancel")) { ReorderableList.defaultBehaviours.DoRemoveButton(list); } }; } public override void OnInspectorGUI() { serializedObject.Update(); //自动布局绘制列表 _stringArray.DoLayoutList(); serializedObject.ApplyModifiedProperties(); } }
using System.Collections.Generic; using UnityEngine; public class TargetExample : MonoBehaviour { [SerializeField] public List<PlayerItem> playerItemArray = new List<PlayerItem>(); } [System.Serializable] public class PlayerItem { [SerializeField] public Texture icon; [SerializeField] public GameObject prefab; [SerializeField] public string name; [SerializeField] public int attack; }
using UnityEditor; using UnityEditorInternal; using UnityEngine; [CustomEditor(typeof(TargetExample))] public class TargetExampleEditor : Editor { private ReorderableList _playerItemArray; private void OnEnable() { _playerItemArray = new ReorderableList(serializedObject, serializedObject.FindProperty("playerItemArray") , true, true, true, true); //自定义列表名称 _playerItemArray.drawHeaderCallback = (Rect rect) => { GUI.Label(rect, "Player Array"); }; //定义元素的高度 _playerItemArray.elementHeight = 68; //自定义绘制列表元素 _playerItemArray.drawElementCallback = (Rect rect,int index,bool selected,bool focused) => { //根据index获取对应元素 SerializedProperty item = _playerItemArray.serializedProperty.GetArrayElementAtIndex(index); rect.height -=4; rect.y += 2; EditorGUI.PropertyField(rect, item,new GUIContent("Index "+index)); }; //当删除元素时候的回调函数,实现删除元素时,有提示框跳出 _playerItemArray.onRemoveCallback = (ReorderableList list) => { if (EditorUtility.DisplayDialog("Warnning","Do you want to remove this element?","Remove","Cancel")) { ReorderableList.defaultBehaviours.DoRemoveButton(list); } }; } public override void OnInspectorGUI() { serializedObject.Update(); //自动布局绘制列表 _playerItemArray.DoLayoutList(); serializedObject.ApplyModifiedProperties(); } }
目前实现效果如下:
通过PropertyDrawer来绘制PlayerItem的样式,注意这是对PlayerItem类的绘制,不是TargetExample类。同样是编辑器类,需要放在Editor文件夹下
using UnityEngine; using UnityEditor; [CustomPropertyDrawer(typeof(PlayerItem))] public class TargetExampleDrawer : PropertyDrawer { public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { using (new EditorGUI.PropertyScope(position,label,property)) { //设置属性名宽度 EditorGUIUtility.labelWidth = 60; position.height = EditorGUIUtility.singleLineHeight; var iconRect = new Rect(position) { width = 64, height = 64 }; var prefabRect = new Rect(position) { width = position.width - 80, x = position.x + 80 }; var nameRect = new Rect(prefabRect) { y = prefabRect.y + EditorGUIUtility.singleLineHeight + 5 }; var attackSliderRect = new Rect(nameRect) { y = nameRect.y + EditorGUIUtility.singleLineHeight + 5 }; var iconProperty = property.FindPropertyRelative("icon"); var prefabProperty = property.FindPropertyRelative("prefab"); var nameProperty = property.FindPropertyRelative("name"); var attackProperty = property.FindPropertyRelative("attack"); iconProperty.objectReferenceValue = EditorGUI.ObjectField(iconRect, iconProperty.objectReferenceValue, typeof(Texture), false); nameProperty.stringValue = EditorGUI.TextField(nameRect, nameProperty.displayName,nameProperty.stringValue); prefabProperty.objectReferenceValue = EditorGUI.ObjectField(prefabRect, prefabProperty.objectReferenceValue,typeof(GameObject),false); attackProperty.intValue = EditorGUI.IntSlider(attackSliderRect, attackProperty.intValue,0,100); } } }
实现效果如下:
实现添加元素的时候,出现下拉菜单,可以在场景中自动生成Assets/Prefabs文件下的预制体,并完成自动赋值
在TargetExampleEditor中定义枚举和结构体
public enum PrefabType
{
Player,
Enemy,
}
public struct Creation
{
public PrefabType prefabType;
public string path;
}
//通过委托onAddDropdownCallback和GenericMenu实现下拉列表功能
_playerItemArray.onAddDropdownCallback = (Rect rect, ReorderableList list) => { GenericMenu menu = new GenericMenu(); var guids = AssetDatabase.FindAssets("t:Prefab", new string[] { "Assets/Prefabs/Player" }); foreach (var guid in guids) { var path = AssetDatabase.GUIDToAssetPath(guid); menu.AddItem(new GUIContent("Player/" + System.IO.Path.GetFileNameWithoutExtension(path)) , false, ClickHandler, new Creation() { prefabType = PrefabType.Player, path = path }); } //添加分割线 menu.AddSeparator(""); guids = AssetDatabase.FindAssets("t:Prefab", new string[] { "Assets/Prefabs/Enemy" }); foreach (var guid in guids) { var path = AssetDatabase.GUIDToAssetPath(guid); menu.AddItem(new GUIContent("Enemy/" + System.IO.Path.GetFileNameWithoutExtension(path)) , false, ClickHandler, new Creation() { prefabType = PrefabType.Enemy, path = path }); } //显示鼠标下方的菜单 menu.ShowAsContext(); };
//添加GenericMenu的回调函数
private void ClickHandler(object target) { Creation creation = (Creation)target; int index = _playerItemArray.serializedProperty.arraySize; _playerItemArray.serializedProperty.arraySize++; _playerItemArray.index = index; SerializedProperty element = _playerItemArray.serializedProperty.GetArrayElementAtIndex(index); switch (creation.prefabType) { case PrefabType.Player: SpawnCharacter(creation,element,90); break; case PrefabType.Enemy: SpawnCharacter(creation, element, 80); break; } serializedObject.ApplyModifiedProperties(); } private void SpawnCharacter(Creation creation, SerializedProperty element,int atk) { GameObject character = AssetDatabase.LoadAssetAtPath<GameObject>(creation.path); GameObject obj = GameObject.Instantiate(character); obj.name = character.name; SerializedProperty prefabPreperty = element.FindPropertyRelative("prefab"); SerializedProperty iconPreperty = element.FindPropertyRelative("icon"); SerializedProperty namePreperty = element.FindPropertyRelative("name"); SerializedProperty attackPreperty = element.FindPropertyRelative("attack"); prefabPreperty.objectReferenceValue = character; iconPreperty.objectReferenceValue = GetPreviewTex(character); namePreperty.stringValue = character.name; attackPreperty.intValue = atk; } //获取预制体的预览图 private Texture GetPreviewTex(GameObject obj) { return AssetPreview.GetAssetPreview(obj) as Texture; }
ReorderableList的委托
名称 | 描述 |
---|---|
drawHeaderCallback | 绘制表头回调 |
drawFooterCallback | 绘制尾部回调 |
drawElementCallback | 绘制元素回调 |
drawElementBackgroundCallback | 绘制元素背景回调 |
onReorderCallback | 重新排序回调 |
onSelectCallback | 选中回调 |
onAddCallback | 添加按钮回调 |
onAddDropdownCallback | 添加下拉选项回调 |
onRemoveCallback | 移除元素回调 |
onMouseUpCallback | 鼠标抬起回调 |
onCanRemoveCallback | 是否显示可移除按钮回调 |
onChangedCallback | 列表改变回调 |
unity make your lists functional with reorderablelis
Unity编辑器拓展之二:ReorderableList可重新排序的列表框(复杂使用)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。