如何在Unity编译代码或关闭窗口后保存UI Toolkit编辑器窗口值?
Unity UI Toolkit编辑器窗口值持久化方案
针对编译后编辑器窗口值丢失的问题,结合你用到的ListView及ObjectField/Vector3/Toggle元素,提供以下几种实用的持久化方案:
方案一:用EditorPrefs存储(适合简单数据场景)
EditorPrefs是Unity提供的编辑器全局存储工具,适合存储轻量数据,步骤如下:
- 给列表条目生成唯一键名:为避免键名冲突,给每个条目加窗口前缀+索引+元素类型,比如
"MyWindow_ListItem_0_Object"。 - 监听元素变化并保存:给每个UI元素绑定值变更事件,实时将数据存入
EditorPrefs:// ObjectField保存示例 ObjectField objField = element.Q<ObjectField>(); objField.valueChanged += newValue => { EditorPrefs.SetString($"MyWindow_ListItem_{index}_Object", EditorJsonUtility.ToJson(newValue)); }; // Vector3保存示例 Vector3Field vec3Field = element.Q<Vector3Field>(); vec3Field.valueChanged += newValue => { EditorPrefs.SetString($"MyWindow_ListItem_{index}_Vector3", JsonUtility.ToJson(newValue)); }; // Toggle保存示例 Toggle toggle = element.Q<Toggle>(); toggle.valueChanged += newValue => { EditorPrefs.SetBool($"MyWindow_ListItem_{index}_Toggle", newValue); }; - 窗口初始化时恢复数据:在
ListView的BindItem方法中,根据索引读取EditorPrefs的值并赋值给元素:void BindItem(VisualElement element, int index) { // 恢复ObjectField var objField = element.Q<ObjectField>(); var objJson = EditorPrefs.GetString($"MyWindow_ListItem_{index}_Object", ""); if (!string.IsNullOrEmpty(objJson)) { objField.value = EditorJsonUtility.FromJson<Object>(objJson); } // 恢复Vector3Field var vec3Field = element.Q<Vector3Field>(); var vec3Json = EditorPrefs.GetString($"MyWindow_ListItem_{index}_Vector3", ""); if (!string.IsNullOrEmpty(vec3Json)) { vec3Field.value = JsonUtility.FromJson<Vector3>(vec3Json); } // 恢复Toggle var toggle = element.Q<Toggle>(); toggle.value = EditorPrefs.GetBool($"MyWindow_ListItem_{index}_Toggle", false); }
方案二:用ScriptableObject作为数据容器(推荐复杂结构场景)
将列表数据存在ScriptableObject中,数据会被Unity持久化到Project目录,支持版本控制,步骤如下:
- 定义数据容器类:
[CreateAssetMenu(fileName = "ListViewData", menuName = "Custom/ListView Data")] public class ListViewData : ScriptableObject { [System.Serializable] public class ListItemData { public Object targetObj; public Vector3 position; public bool isEnabled; } public List<ListItemData> items = new List<ListItemData>(); } - 在窗口中引用数据容器:可以在窗口顶部加一个
ObjectField,让用户选择或创建ListViewData实例。 - 同步UI与数据容器:UI元素值变化时,更新容器数据并标记为脏(触发Unity保存):
// 假设dataAsset是已引用的ListViewData实例 objField.valueChanged += newValue => { dataAsset.items[index].targetObj = newValue; EditorUtility.SetDirty(dataAsset); }; - 初始化窗口时从容器读数据:遍历
dataAsset.items,将数据填充到ListView的对应条目即可。 - 动态条目处理:添加/删除列表条目时,同步增删
dataAsset.items中的数据项。
方案三:监听编译/退出事件主动保存
通过Unity编辑器事件,在编译开始或窗口关闭前主动保存数据,避免丢失:
void OnEnable() { // 监听编译开始事件 CompilationPipeline.compilationStarted += OnCompilationStart; // 监听编辑器退出事件 EditorApplication.quitting += SaveAllData; } void OnDisable() { // 移除事件监听 CompilationPipeline.compilationStarted -= OnCompilationStart; EditorApplication.quitting -= SaveAllData; } void OnCompilationStart(object obj) { SaveAllData(); } void SaveAllData() { // 遍历ListView所有条目,收集数据并保存(用EditorPrefs或ScriptableObject均可) }
内容的提问来源于stack exchange,提问作者ouzari
相关产品推荐
相关产品推荐

