如何在Unity新UI ToolKit编辑器窗口中添加GameObject数组?
在UI ToolKit编辑器窗口实现GameObject数组控件
核心思路
UI ToolKit本身没有直接提供数组组件,但可以通过自定义绑定+复用Unity原生属性控件或定制ListView来实现类似Inspector的数组编辑功能,以下是两种实用方案:
方案一:复用SerializedProperty与PropertyField(推荐)
这种方式直接复用Unity内置的数组编辑UI,完全贴合原生Inspector体验,无需额外开发交互逻辑:
- 在编辑器窗口类中,获取目标对象的序列化对象和对应数组属性:
private SerializedObject targetSO; private SerializedProperty gameObjectArrayProp; void CreateGUI() { // 替换为你的目标对象(比如ScriptableObject或MonoBehaviour实例) MyTargetComponent target = MyTargetComponent.Instance; targetSO = new SerializedObject(target); // 替换为你脚本中数组字段的名称 gameObjectArrayProp = targetSO.FindProperty("myGameObjectArray"); // 创建PropertyField并绑定属性 var arrayField = new PropertyField(gameObjectArrayProp); arrayField.Bind(targetSO); rootVisualElement.Add(arrayField); } // 编辑器窗口更新时同步序列化对象 void Update() { targetSO.Update(); }
- 生成的UI会和Inspector中的数组完全一致,支持添加/删除元素、拖拽GameObject赋值等所有原生交互。
方案二:自定义ListView实现(灵活定制)
如果需要完全自定义元素样式或交互逻辑,可以手动构建ListView:
- 先定义单个数组元素的UI结构:
private VisualElement CreateListItem(GameObject item) { var container = new VisualElement(); container.style.flexDirection = FlexDirection.Row; container.style.alignItems = Align.Center; // 用于选择GameObject的ObjectField var objectField = new ObjectField(); objectField.objectType = typeof(GameObject); objectField.value = item; objectField.RegisterValueChangedCallback(evt => { int index = listView.itemsSource.IndexOf(item); if (index >= 0) { (listView.itemsSource as List<GameObject>)[index] = evt.newValue as GameObject; } }); // 删除按钮 var deleteBtn = new Button(() => { (listView.itemsSource as List<GameObject>).Remove(item); listView.Rebuild(); }); deleteBtn.text = "-"; deleteBtn.style.width = 30; container.Add(objectField); container.Add(deleteBtn); return container; }
- 初始化ListView并添加元素添加按钮:
private ListView listView; private List<GameObject> gameObjectList = new List<GameObject>(); void CreateGUI() { // 初始化ListView,指定数据源、元素高度和创建方法 listView = new ListView(gameObjectList, 20, CreateListItem); listView.style.flexGrow = 1; rootVisualElement.Add(listView); // 添加元素的按钮 var addBtn = new Button(() => { gameObjectList.Add(null); listView.Rebuild(); }); addBtn.text = "Add GameObject"; rootVisualElement.Add(addBtn); }
这种方式可以完全控制UI样式,但需要手动处理数据与目标对象的同步逻辑。
注意事项
- 使用
PropertyField时,确保数组字段是公开的或带有[SerializeField]标签,否则SerializedProperty无法识别。 - 编辑器窗口激活或目标对象变更时,需重新绑定属性,避免引用失效。
- 自定义ListView方案中,若需持久化数据,要手动将
gameObjectList同步到目标对象的数组字段中。
内容的提问来源于stack exchange,提问作者ouzari
相关产品推荐
相关产品推荐

