首页 >Unity3D频道 >【Unity3D研究院之游戏开发】 > Unity3D研究院之Inspector面板枚举的别名与排序(八十九)
2016
07-13

Unity3D研究院之Inspector面板枚举的别名与排序(八十九)

虽然mono是支持unicode的。可以在枚举里写中文,但是我还是觉得写英文好一些。可是在编辑器上策划是希望看到的是中文的,还有就是枚举的展示排序功能,策划在编辑的时候为了方便希望把常用的枚举排上前面。

把如下代码放到你的工程里就可以直接用了。

using UnityEngine;
using System;
#if UNITY_EDITOR
using UnityEditor;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
#endif 

[AttributeUsage(AttributeTargets.Enum | AttributeTargets.Field)]
public class EnumLabelAttribute : PropertyAttribute
{
    public string label;
    public int[] order = new int[0] ;
    public EnumLabelAttribute(string label)
    {
        this.label = label;
    }

    public EnumLabelAttribute(string label,params int[] order)
    {
        this.label = label;
        this.order = order;
    }
}


#if UNITY_EDITOR
[CustomPropertyDrawer(typeof(EnumLabelAttribute))]
public class EnumLabelDrawer : PropertyDrawer
{
    private Dictionary<string, string> customEnumNames = new Dictionary<string, string>();


    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        SetUpCustomEnumNames(property, property.enumNames);

        if (property.propertyType == SerializedPropertyType.Enum)
        {
            EditorGUI.BeginChangeCheck();
            string[] displayedOptions = property.enumNames
                    .Where(enumName => customEnumNames.ContainsKey(enumName))
                    .Select<string, string>(enumName => customEnumNames[enumName])
                    .ToArray();

            int[] indexArray = GetIndexArray (enumLabelAttribute.order);
            if(indexArray.Length != displayedOptions.Length)
            {
                indexArray = new int[displayedOptions.Length];
                for(int i =0; i< indexArray.Length; i++){
                    indexArray[i] = i;
                }
            }
            string[] items = new string[displayedOptions.Length];
            items[0] = displayedOptions[0];       
            for (int i=0; i<displayedOptions.Length; i++) {
                items[i] =  displayedOptions[indexArray[i]];
            }
            int index = -1;
            for (int i=0; i<indexArray.Length; i++) {
                if (indexArray[i] == property.enumValueIndex) {
                    index = i;
                    break;
                }
            }
            if ( (index == -1) && (property.enumValueIndex != -1) ) { SortingError (position,property,label); return; }
            index = EditorGUI.Popup(position, enumLabelAttribute.label,index, items);
            if (EditorGUI.EndChangeCheck())
            {
                if (index >= 0)
                    property.enumValueIndex = indexArray[index];
            }
        }
    }

    private EnumLabelAttribute enumLabelAttribute
    {
        get
        {
            return (EnumLabelAttribute)attribute;
        }
    }

    public void SetUpCustomEnumNames(SerializedProperty property, string[] enumNames)
    {


            object[] customAttributes = fieldInfo.GetCustomAttributes(typeof(EnumLabelAttribute), false);
            foreach (EnumLabelAttribute customAttribute in customAttributes)
            {
                Type enumType = fieldInfo.FieldType;

                foreach (string enumName in enumNames)
                {
                    FieldInfo field = enumType.GetField(enumName);
                    if (field == null) continue;
                    EnumLabelAttribute[] attrs = (EnumLabelAttribute[])field.GetCustomAttributes(customAttribute.GetType(), false);

                    if (!customEnumNames.ContainsKey(enumName))
                    {
                        foreach (EnumLabelAttribute labelAttribute in attrs)
                        {
                            customEnumNames.Add(enumName, labelAttribute.label);
                        }
                    }
                }
            }
    }


    int[] GetIndexArray (int[] order) 
    {
        int[] indexArray = new int[order.Length];
        for (int i = 0; i < order.Length; i++) {
            int index = 0;
            for (int j = 0; j < order.Length; j++) {                
                if (order[i] > order[j]) {                  
                    index++;                
                }               
            }
            indexArray[i] = index;
        }
        return (indexArray);
    }

    void SortingError (Rect position, SerializedProperty property, GUIContent label) 
    {
        EditorGUI.PropertyField(position, property, new GUIContent(label.text + " (sorting error)"));
        EditorGUI.EndProperty();
    }
}
public class EnumLabel
{
    static public object GetEnum(Type type, SerializedObject serializedObject, string path)
    {
        SerializedProperty property =  GetPropety(serializedObject,path);
        return  System.Enum.GetValues(type).GetValue(property.enumValueIndex);
    }
    static public object DrawEnum(Type type, SerializedObject serializedObject, string path)
    {
        return DrawEnum(type,serializedObject, GetPropety(serializedObject,path));
    } 
    static public object DrawEnum(Type type, SerializedObject serializedObject,SerializedProperty property)
    {
        serializedObject.Update();
        EditorGUILayout.PropertyField(property);
        serializedObject.ApplyModifiedProperties();
        return  System.Enum.GetValues(type).GetValue(property.enumValueIndex);
    } 
    static public SerializedProperty GetPropety(SerializedObject serializedObject, string path)
    {
        string []contents = path.Split('/');
        SerializedProperty property =  serializedObject.FindProperty(contents[0]);
        for(int i=1; i< contents.Length; i++){
            property = property.FindPropertyRelative(contents[i]);
        }
        return property;
    }
}
#endif

 

使用是这样的,第二个参数就是排序。接收int的不固定参数。

using UnityEngine;
using System.Collections;

public class NewBehaviourScript : MonoBehaviour 
{
    [EnumLabel("我是的类型",10,1,5,2)]
    public NewType newType = NewType.One;
}

public enum NewType : byte
{
    [EnumLabel("我是1")]
    One = 10,
    [EnumLabel("我是2")]
    Two = 1,
    [EnumLabel("我是3")]
    Three = 5,
    [EnumLabel("我是4")]
    Four = 2
}

OK 中文与排序都OK了。

tmp3f8e8d53

但是,有时候我们做编辑器的时候是自己调用OnInspectorGUI来绘制面板的。而且我的枚举对象可能会在另外一个子对象里,或者在子对象里的一个List<T>里面的子对象里。

比如这样, class.data.newType 就是这个类对象的结构,你可以按照你自己类的结构去拼这个字符串。

        public override void OnInspectorGU()
        {

            NewType oldType = (NewType)EnumLabel.GetEnum(typeof(NewType),serializedObject,"class/data/newType");
            NewType newType = (NewType)EnumLabel.DrawEnum(typeof(NewType),serializedObject,"class/data/newType");
            if(oldType != newType)
            {
                //类型发生改变
            }
        }

还有一种特殊的就是可能枚举在list<T>里,这样在绘制的时候是需要遍历的。

       public override void OnInspectorGU()
        {
            SerializedProperty property = EnumLabel.GetPropety(serializedObject,"class/datas");
            for(int i =0; i<  count; i++)
            {
                SerializedProperty eProperty = property.GetArrayElementAtIndex(i);
                NewType newType = (NewType)EnumLabel.DrawEnum(typeof(NewType),serializedObject
                            ,eProperty.FindPropertyRelative("newType"));
            }
        }

OK大功告成。

参考文章:

 https://github.com/anchan828/property-drawer-collection/blob/master/EnumLabel/EnumLabelAttribute.cs

http://forum.unity3d.com/threads/enum-inspector-sorting-attribute.357558/

最后编辑:
作者:雨松MOMO
专注移动互联网,Unity3D游戏开发
捐 赠写博客不易,如果您想请我喝一杯星巴克的话?就进来看吧!

Unity3D研究院之Inspector面板枚举的别名与排序(八十九)》有 12 条评论

  1. 雨松大大,问个问题吗,有个需求是想在场景内的3D模型上显示一些UI信息,感觉UGUI的有些特性用2D的sprite无法完全表达,这个要咋个弄哦?

  2. 向往阳光说:

    7月13日的,好新,充满希望!

  3. 远去之情说:

    现在mmorpg手游的角色位置同步。应该传输摇杆的,还是坐标的信息到服务器去? 持续传还是出发传

    • 其实都可以,但是我倾向客户端传输摇杆,比如摇杆一直朝着一个方向,就可以不用每一帧都发送。。

      • 远去之情说:

        我也是这样想。去模拟摇杆还是比直接改位置好些,不然人可能都会跑地底下去

      • 远去之情说:

        如果这样,那么后进入场景的玩家,怎么初始化已有玩家的位置信息,是个问题。服务器是不存玩家位置信息的,如果存,就要一直更新。现在想法是,进入时,服务器就把已有的人的位置信息请求一遍发给他,这样有延误。不知道有什么好的解决办法

        • 服务器要存玩家位置。 回来的消息都是要带玩家的位置 如果客户端发现与服务器相差较大 直接拉回 采用服务器的位置。。

          • 远去之情说:

            完成了。摇杆变化才会发送数据,对方接收到后以transform.Translate的方式移动。不过只有摇杆一直变化才能持续移动,又不愿在update里面写(如果没办法的话)。传统网游移动方式是什么

留下一个回复

你的email不会被公开。 必填项已用 * 标注。