引言
最近项目有个需求,就是读取外部的文件,然后序列化文件,将文件的信息展示在面板上,记录一下在开发过程中所积累的一些技巧。
1.面板数据的存储和加载
在做编辑器开发时,需要面临两个问题
1.面板上的数据如何加载
2.面板上的数据如何保存
下面是我在做实际项目时用到的方法,其实两个方法本质是相同的。
方案1:使用Json、xml或二进制文件
这个方案的核心就是将数据存储为一个保存的本地的文件,当打开面板时,在OnEnable中加载文件,但是这个方案有缺点.
缺点1:当一不小心关闭面板时,可能会导致刚才修改的数据丢失。这就需要我们一直考虑,数据存储的时机。
缺点2:因为数据是在OnEnable中加载的,因此如果数据过多,会造成打开面板时有明显卡顿
方案2:使用ScriptableObject
这是我较推荐的解决方案,接下来我会详细说明如何使用。
首先,我们可以利用AssetDatabase.LoadAssetAtPath
来加载ScriptableObject文件。如果加载失败,可以通过ScriptableObject.CreateInstance
和AssetDatabase.CreateAsset
来创建文件。
这一方法解决了第一个方案中存在的两个缺陷。使用此方法,无需考虑数据存储时机,只需通过加载来获取ScriptableObject的引用,所有面板上的操作都是对ScriptableObject属性的修改。
下面是代码示例:
TestEditorSO.cs
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(fileName = "TestEditorSO", menuName = "Custom/Editor/TestEditorSO")]
public class TestEditorSO : ScriptableObject
{
public List<TestEditorItem> testEditorItemList;
}
[System.Serializable]
public class TestEditorItem
{
public int id;
public string Name;
public bool isToggle;
public TestEditorItem(int id, string name, bool isToggle)
{
this.id = id;
Name = name;
this.isToggle = isToggle;
}
}
TestEditor.cs
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
public class TestEditor : EditorWindow
{
private static TestEditor m_Instance;
private TestEditorSO testEditorSO;
private const string configPath = "Assets/TestEditorSO.asset";
private void OnEnable()
{
testEditorSO = AssetDatabase.LoadAssetAtPath<TestEditorSO>(configPath);
}
[MenuItem("CustomTest/TestEditor")]
private static void ShowWindow()
{
m_Instance = GetWindow<TestEditor>();
}
private void OnGUI()
{
if (testEditorSO == null)
{
EditorGUILayout.HelpBox("配置文件为空,点击创建配置文件", MessageType.Error);
if (GUILayout.Button("生成配置文件"))
{
CreateAsset();
}
return;
}
foreach (var item in testEditorSO.testEditorItemList)
{
item.isToggle = GUILayout.Toggle(item.isToggle, item.Name);
}
}
/// <summary>
/// 创建配置文件
/// </summary>
private void CreateAsset()
{
testEditorSO = ScriptableObject.CreateInstance<TestEditorSO>();
//构建数据
testEditorSO.testEditorItemList = new List<TestEditorItem>
{
new TestEditorItem(1, "小明", true),
new TestEditorItem(2, "小红", false),
new TestEditorItem(3, "小灰", false),
};
AssetDatabase.CreateAsset(testEditorSO, configPath);
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
Debug.Log("配置文件创建成功");
}
}
效果图
2.区域检测
当我们想在一个区域,处理一些逻辑,比如当鼠标进入某个区域,就变成手指状。当鼠标点击某个区域,这个区域的颜色发生变化。此时我们就可以用到GUILayoutUtility.GetLastRect()
方法
下面时代码示例:当鼠标进入区域后,会变成手指状,右键点击,区域会变成绿色
using UnityEditor;
using UnityEngine;
public class TestEditor : EditorWindow
{
private static TestEditor m_Instance;
private bool isClick = false;
[MenuItem("CustomTest/TestEditor")]
private static void ShowWindow()
{
m_Instance = GetWindow<TestEditor>();
}
private void OnGUI()
{
if (isClick)
GUI.color = Color.green;
else
GUI.color = Color.white;
EditorGUILayout.BeginVertical(GUILayout.Width(300),GUILayout.Height(300));
EditorGUILayout.HelpBox("配置文件为空,点击创建配置文件", MessageType.Info);
if (GUILayout.Button("测试"))
{
}
EditorGUILayout.EndVertical();
Rect verticalRect = GUILayoutUtility.GetLastRect();
EditorGUIUtility.AddCursorRect(verticalRect, MouseCursor.Link);
if (Event.current.type == EventType.ContextClick && verticalRect.Contains(Event.current.mousePosition))
{
isClick = true;
}
}
}
3.控件变化检测
在做编辑器开发时,我们想修改数据时,可以实时检测到。那么使用EditorGUI.BeginChangeCheck()
。
EditorGUI.BeginChangeCheck()
是一个用于检查GUI元素是否被修改的方法,这可以大大优化性能
下面是代码示例
using UnityEditor;
using UnityEngine;
[CustomEditor(typeof(MyScript))]
public class MyScriptEditor : Editor
{
public override void OnInspectorGUI()
{
EditorGUI.BeginChangeCheck();
DrawDefaultInspector(); // 或者使用其他GUI绘制方法
if (EditorGUI.EndChangeCheck())
{
// 如果用户修改了某些属性,执行相应的操作
Debug.Log("GUI元素已更改");
}
}
}
总结
unity编辑器开发的技巧还有很多,还有很多技巧可以挖掘和学习,上述方法只是我一般用到的,不一定是最优解。GPT是一个很好用的工具,在编辑器开发的过程中可以对我们提供很大的帮助。
推荐一个知乎很好的关于unity编辑器的文章:Unity3d Editor 编辑器扩展功能详解。
最后贴一张