注:转载请标明原文地址:https://segmentfault.com/a/11...

汉化效果

利用Attribute+Editor可以自定义一些属性,实现汉化:

汉化效果图

用法示例

RenameTest.cs

using UnityEngine;
using UnityEngine.Events;

[System.Serializable]
public class Item {

    [Rename("名称")]
    public string name;
    [Rename("类型")]
    public Type type = Type.A;

    public enum Type {
        [Rename("消耗品")]
        A,
        [Rename("任务道具")]
        B
    }
}

/// <summary>
/// 重命名属性测试
/// <para>ZhangYu 2018-06-21</para>
/// </summary>
public class RenameTest : MonoBehaviour {

    [Title("标题", "green")]
    public string title;
    [RenameInEditor("等级")]
    public int level;
    [RenameInEditor("姓名")]
    public string name;
    [Rename("武器")]
    public Weapon weapon;
    [Rename("铠甲")]
    public Armor armor;
    [RenameInEditor("道具列表")]
    public Item[] items;
    [RenameInEditor("死亡回调方法")]
    public UnityEvent onDead;
    [RenameInEditor("复活回调方法")]
    public UnityEvent onRevive;

    public enum Weapon {
        [Rename("蛇矛")]
        A,
        [Rename("胜宗刀")]
        B,
        [Rename("青缸剑")]
        C,
        [Rename("李广弓")]
        D
    }

    public enum Armor {
        [Rename("钢甲")]
        One,
        [Rename("火焰甲")]
        Two,
        [Rename("青龙甲")]
        Three,
        [Rename("藤甲")]
        Four
    }

    private void Start () {
        
    }
    
}

CustomRenameEditors.cs

using UnityEditor;

[CustomEditor(typeof(RenameTest))][CanEditMultipleObjects]
public class RenameTestEditor : RenameEditor { }

使用方式

需要导入两个类: RenameEditor.cs和RenameAttribute.cs 配合使用
RenameEditor.cs要放在Editor文件夹下 RenameAttribute.cs不要放在Editor下

1.使用[RenameInEditor]标记需要重命名的属性
2.创建一个Editor继承RenameEditor 绑定需要重命名的类

这样Editor绘制Inspector的时候 检查到RenameInEditor的属性 会优先使用重命名的名称
注意:没有子集的属性 可以直接使用Rename 不需要Editor 但是有子集的属性 需要Editor和RenameInEditor配合使用才能正常改名 所以为了完全汉化 还是写一个Editor吧
由于RenameEditor和Attribute仅在Editor下有效,所以不用担心发布后的体积问题

RenameAttribute脚本

RenameAttribute.cs

using UnityEngine;
#if UNITY_EDITOR
using System;
using UnityEditor;
using System.Reflection;
using System.Text.RegularExpressions;
#endif

/// <summary>
/// 重命名属性
/// <para>ZhangYu 2018-06-21</para>
/// </summary>

#if UNITY_EDITOR
[AttributeUsage(AttributeTargets.Field)]
#endif
public class RenameAttribute : PropertyAttribute {

    /// <summary> 枚举名称 </summary>
    public string name = "";
    /// <summary> 文本颜色 </summary>
    public string htmlColor = "#000000";

    /// <summary> 重命名属性 </summary>
    /// <param name="name">新名称</param>
    public RenameAttribute(string name) {
        this.name = name;
    }

    /// <summary> 重命名属性 </summary>
    /// <param name="name">新名称</param>
    /// <param name="htmlColor">文本颜色 例如:"#FFFFFF" 或 "black"</param>
    public RenameAttribute(string name, string htmlColor) {
        this.name = name;
        this.htmlColor = htmlColor;
    }

}

#if UNITY_EDITOR
[CustomPropertyDrawer(typeof(RenameAttribute))]
public class RenameDrawer : PropertyDrawer {

    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) {
        // 替换属性名称
        RenameAttribute rename = (RenameAttribute)attribute;
        label.text = rename.name;

        // 重绘GUI
        Color defaultColor = EditorStyles.label.normal.textColor;
        EditorStyles.label.normal.textColor = htmlToColor(rename.htmlColor);
        bool isElement = Regex.IsMatch(property.displayName, "Element \\d+");
        if (isElement) label.text = property.displayName;
        if (property.propertyType == SerializedPropertyType.Enum) {
            drawEnum(position, property, label);
        } else {
            EditorGUI.PropertyField(position, property, label, true);
        }
        EditorStyles.label.normal.textColor = defaultColor;
    }

    // 绘制枚举类型
    private void drawEnum(Rect position, SerializedProperty property, GUIContent label) {
        EditorGUI.BeginChangeCheck();

        // 获取枚举相关属性
        Type type = fieldInfo.FieldType;
        string[] names = property.enumNames;
        string[] values = new string[names.Length];
        Array.Copy(names, values, names.Length);
        while (type.IsArray) type = type.GetElementType();

        // 获取枚举所对应的RenameAttribute
        for (int i = 0; i < names.Length; i++) {
            FieldInfo info = type.GetField(names[i]);
            RenameAttribute[] atts = (RenameAttribute[])info.GetCustomAttributes(typeof(RenameAttribute), true);
            if (atts.Length != 0) values[i] = atts[0].name;
        }

        // 重绘GUI
        int index = EditorGUI.Popup(position, label.text, property.enumValueIndex, values);
        if (EditorGUI.EndChangeCheck() && index != -1) property.enumValueIndex = index;
    }

    /// <summary> Html颜色转换为Color </summary>
    /// <param name="hex"></param>
    /// <returns></returns>
    public static Color htmlToColor(string hex) {
        // 编辑器默认颜色
        if (string.IsNullOrEmpty(hex)) return new Color(0.705f, 0.705f, 0.705f);

        #if UNITY_EDITOR
        // 转换颜色
        hex = hex.ToLower();
        if (hex.IndexOf("#") == 0 && hex.Length == 7) {
            int r = Convert.ToInt32(hex.Substring(1, 2), 16);
            int g = Convert.ToInt32(hex.Substring(3, 2), 16);
            int b = Convert.ToInt32(hex.Substring(5, 2), 16);
            return new Color(r / 255f, g / 255f, b / 255f);
        } else if (hex == "red") {
            return Color.red;
        } else if (hex == "green") {
            return Color.green;
        } else if (hex == "blue") {
            return Color.blue;
        } else if (hex == "yellow") {
            return Color.yellow;
        } else if (hex == "black") {
            return Color.black;
        } else if (hex == "white") {
            return Color.white;
        } else if (hex == "cyan") {
            return Color.cyan;
        } else if (hex == "gray") {
            return Color.gray;
        } else if (hex == "grey") {
            return Color.grey;
        } else if (hex == "magenta") {
            return Color.magenta;
        } else if (hex == "orange") {
            return new Color(1, 165 / 255f, 0);
        }
        #endif

        return new Color(0.705f, 0.705f, 0.705f);
    }

}
#endif

/// <summary>
/// 添加标题属性
/// <para>ZhangYu 2018-06-21</para>
/// </summary>
#if UNITY_EDITOR
[AttributeUsage(AttributeTargets.Field)]
#endif
public class TitleAttribute : PropertyAttribute {

    /// <summary> 标题名称 </summary>
    public string title = "";
    /// <summary> 文本颜色 </summary>
    public string htmlColor = "#B3B3B3";

    /// <summary> 在属性上方添加一个标题 </summary>
    /// <param name="title">标题名称</param>
    public TitleAttribute(string title) {
        this.title = title;
    }

    /// <summary> 在属性上方添加一个标题 </summary>
    /// <param name="title">标题名称</param>
    /// <param name="htmlColor">文本颜色 例如:"#FFFFFF" 或 "black"</param>
    public TitleAttribute(string title, string htmlColor) {
        this.title = title;
        this.htmlColor = htmlColor;
    }

}

#if UNITY_EDITOR
[CustomPropertyDrawer(typeof(TitleAttribute))]
public class TitleAttributeDrawer : DecoratorDrawer {

    // 文本样式
    private GUIStyle style = new GUIStyle(EditorStyles.label);

    public override void OnGUI(Rect position) {
        // 获取Attribute
        TitleAttribute rename = (TitleAttribute)attribute;
        style.fixedHeight = 18;
        style.normal.textColor = RenameDrawer.htmlToColor(rename.htmlColor);

        // 重绘GUI
        position = EditorGUI.IndentedRect(position);
        GUI.Label(position, rename.title, style);
    }

    public override float GetHeight() {
        return base.GetHeight() - 3;
    }

}
#endif

/// <summary>
/// 重命名脚本编辑器中的属性名称
/// <para>ZhangYu 2018-06-21</para>
/// </summary>
#if UNITY_EDITOR
[AttributeUsage(AttributeTargets.Field)]
#endif
public class RenameInEditorAttribute : PropertyAttribute {

    /// <summary> 新名称 </summary>
    public string name = "";

    /// <summary> 重命名属性 </summary>
    /// <param name="name">新名称</param>
    public RenameInEditorAttribute(string name) {
        this.name = name;
    }

}

RenameEditor脚本

using UnityEditor;
using UnityEngine;
using System;
using System.Reflection;
using System.Collections.Generic;

/// <summary>
/// 重命名 编辑器
/// <para>ZhangYu 2018-06-21</para>
/// </summary>
//[CanEditMultipleObjects][CustomEditor(typeof(ClassName))]
public class RenameEditor : Editor {

    // 绘制GUI
    public override void OnInspectorGUI() {
        EditorGUI.BeginChangeCheck();
        drawProperties();
        if (EditorGUI.EndChangeCheck()) serializedObject.ApplyModifiedProperties();
    }

    // 绘制属性
    protected virtual void drawProperty(string property, string label) {
        SerializedProperty pro = serializedObject.FindProperty(property);
        if (pro != null) EditorGUILayout.PropertyField(pro, new GUIContent(label), true);
    }

    // 绘制所有属性
    protected virtual void drawProperties() {
        // 获取类型和可序列化属性
        Type type = target.GetType();
        List<FieldInfo> fields = new List<FieldInfo>();
        FieldInfo[] array = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly);
        fields.AddRange(array);

        // 获取父类的可序列化属性
        while (IsTypeCompatible(type.BaseType) && type != type.BaseType) {
            type = type.BaseType;
            array = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly);
            fields.InsertRange(0, array);
        }
        
        // 绘制所有属性
        for (int i = 0; i < fields.Count; i++) {
            FieldInfo field = fields[i];

            // 非公有但是添加了[SerializeField]特性的属性
            if (!field.IsPublic) {
                object[] serials = field.GetCustomAttributes(typeof(SerializeField), true);
                if (serials.Length == 0) continue;
            }

            // 公有但是添加了[HideInInspector]特性的属性
            object[] hides = field.GetCustomAttributes(typeof(HideInInspector), true);
            if (hides.Length != 0) continue;

            // 绘制符合条件的属性
            RenameInEditorAttribute[] atts = (RenameInEditorAttribute[])field.GetCustomAttributes(typeof(RenameInEditorAttribute), true);
            drawProperty(field.Name, atts.Length == 0 ? field.Name : atts[0].name);
        }

    }

    // 脚本类型是否符合序列化条件
    protected virtual bool IsTypeCompatible(Type type) {
        if (type == null || !(type.IsSubclassOf(typeof(MonoBehaviour)) || type.IsSubclassOf(typeof(ScriptableObject))))
            return false;
        return true;
    }

}

Editor文件夹
CustomEditor

以上实现了重命名的功能,其实利用Attribute+Editor还可以实现更丰富的效果,完全可以自定义一套模板,利用简单的特性添加,就可以实现丰富的编辑器样式

PS1:感谢雨松momo和他的文章:《Unity3D研究院之Inspector面板枚举的别名与排序(八十九)》
PS2:转载请注明原文地址:https://segmentfault.com/a/11...


冰封百度
233 声望43 粉丝

Unity游戏程序员一枚。生命不息,学习不止。


« 上一篇
Java面试题