Unity如何实现Component属性“复制粘贴”功能

发表于2015-09-22
评论2 9.6k浏览

    在Unity提供了Inpsector面板用于配置各种属性,这极大地提高了工作效率。但是,在使用Inspector面板提供的便捷性的同时,开发者偶尔也会抱怨在Unity Inspector面板对Component属性进行“赋值粘贴”操作不够便捷。
    如下图所示,Inpsecort面板提供的“复制”功能只能实现对整个Component的“复制”。

    但是,如果遇到“只需要复制Component中的部分属性值到另一个Game Object中”的应用场景,Unity提供的复制功能就不能够满足需求了。 本文将实现这种对“Component功能进行按下选择复制的功能”。

实现功能预览

具体实现效果如下图所示。

实现的功能包括:

  • 在一个GameObject上“右键”选择“Copy Components”弹出上图左边的选择框,列出当前GameObject的所有属性,并通过选择功能
  • 提供选择需要复制的Component中一个或多个属性功能
  • 复制后,在另外一个GameObject中,“右键”选择“Paste Components”,即可以将上一步选择的属性粘贴到新的GameObject上

实现方法

为了实现对Component属性的“复制粘贴”操作,我们需要实现如下几点功能:

  • 右键菜单功能
  • 显示当前Component的所有属性
  • “复制并粘贴”选中的Component属性

实现“复制粘贴”功能的界面

1.实现“上下文菜单”

    Unity提供了UnityEditor命名空间,该命名空间下提供多种操作Unity界面的类。可以实现如“Unity窗口创建”、“Unity菜单创建”等功能。本文实现菜单创建时,也是使用的该命名空间下的类。具体实现如下代码所示。

[MenuItem("GameObject/Copy Components", false, 0)]private static void CopyComponents(){ //实现Copy Components的业务逻辑}[MenuItem("GameObject/Paste Components",false,0)]private static void PasteComponents(){//实现Paste Components属性的业务逻辑}

    以上代码简单的实现了在Game Object上创建了"上下文菜单"功能。对于菜单的更多详细介绍,请参见文章“http://unity3d.com/cn/learn/tutorials/modules/intermediate/editor/menu-items
2.实现“选择Components”的界面
    “选择Components”的界面实现业务逻辑比较负责,这里展示出代码框架,并没有完全显示所有代码。感兴趣的同学,可以自行填充代码完成具体的显示功能。

public class ObjectCopierWindow : EditorWindow{private void OnEnable(){//实现界面显示数据的初始化}///该方法被Unity调用,一直刷新,完成界面绘制和显示工作private void OnGUI(){//创建可滚动的scrollbar_scrollPosition = EditorGUILayout.BeginScrollView(_scrollPosition, GUILayout.ExpandWidth(true), GUILayout.ExpandHeight(true));//显示标题EditorGUILayout.TextField(_objectName+"组件属性列表",EditorStyles.boldLabel);EditorGUILayout.Space();//实现显示获得的components列表功能ShowPropertyList();EditorGUILayout.EndScrollView();//实现“复制”按钮的显示和业务逻辑ShowCopyButton();}/// /// 显示copybutton/// 实现copy的业务逻辑/// private void ShowCopyButton(){if (GUILayout.Button("复制", GUILayout.Height(30))){Debug.Log("copy click");//实现copy功能的业务逻辑CollectionSelectedData();this.Close();}}}

实现Inspector上Components属性的操作

一、相关概念
    Serialize:序列化,使绑定于游戏对象上的脚本中的可序列化数据可以保存,以及在编辑器中编辑。
    Serializable data:可序列化数据,所有如整形字符串枚举等基本数据都是可序列化数据。类和struct需要使用Serializable声明序列化。
    unity在实现Inpsector上可编辑属性时,使用的是SerializedObject和SerializedProperty分别实现对Component对象和属性对象的序列化操作。通过SerializedObject和SerializedProperty既能获得现在Inpsector面板上的值,也能创建自定义的Inpsector面板,这里不讨论如何创建自定义Inpsector面板,仅仅讨论如何实现获取Inpsector面板上的数据。
二、实现方法
    以下代码均为代码片段,只为说明最主要几步功能的实现方法。完整代码太长,这里不一一展示。
1.获取的当前选中Game Object上的所有Components的SerializedObject和SerializedProperty列表

private static void GetSeialized(){ Component[] components = Selection.activeGameObject.GetComponents(); //获得所有组件的serializedObjects SerializedObject[] SerializedObjectList = new SerializedObject[components.Length]; for (int i = 0; i < components.Length; i++) { SerializedObjectList[i] = new SerializedObject(components[i]); } //获得所有组件的serializedProperties SerializedProperty[] SerializedPropertieList = new SerializedProperty[components.Length]; for (int i = 0; i < ObjectCopierWindow.SerializedObjectList.Length; i++) { //获取SerializedProperty迭代器 SerializedPropertieList[i] = ObjectCopierWindow.SerializedObjectList[i].GetIterator(); }}

2.实现对SerializedProperty的遍历

/// /// 实现对一个SerializedProperty的遍历/// /// private static void IteratorSerializedProperty(SerializedProperty property){ property.Next(true); //遍历当前Component下的所有property,并获得property的显示名称 while (property.NextVisible(true)) { Debug.Log(property.displayName); } //重置迭代器 property.Reset();}

3.实现对选中SerializedProperty的粘贴功能

/// /// 实现对选中component中选中的SerializedProperty的粘贴功能/// /// 选中的要复制的component对象的名称列表/// 每个选中的component的所有选中的SerializedProperty列表private static void PasteSelectedComponentsData(string componentType[],List[] selectedPropertyList){ foreach (string typeName in componentType) { //获得需要粘贴的Component对象 Component componentToCopy = Selection.activeGameObject.GetComponent(typeName); //获得对应的SerializedObject SerializedObject serializedObject = new SerializedObject(componentToCopy); //先update一下 serializedObject.Update(); //遍历该component下的所有选中的SerializedProperty foreach (SerializedProperty property in selectedPropertyList) { //将serializedProperty复制到serializedObject下对应的property上 serializedObject.CopyFromSerializedProperty(serializedProperty); } SerializedProperty serializedProperty = ObjectCopierWindow.SerializedPropertieList[componentIndex[i]]; //应用修改 serializedObject.ApplyModifiedProperties(); }}

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引