前言:
在编写UnityShader时,我们常常会使用特性来更换材质球面板的属性外观,除此之外,还可以使用自定义的扩展脚本来实现自定义的材质球界面,参考我之前的文章UnityShaderUI编辑器扩展
但是自定义扩展每次都要单独写一个Editor脚本来扩展对应的Shader,而不能像特性那样,只要加一个[HDR],[Range]就能更改材质面板的界面。
那么我们如何方便又快速的创建自定义的特性,方便我们只要写一个全功能脚本,就能快速自定义扩展不同的Shader呢?
原理:
如图,只需要编写脚本,类继承MaterialPropertyDrawer就可以了,类名的命名方式是特性名+Drawer,如:SingleLineTextureDrawer,或者不加Drawer也可以,Unity会自动添加并识别。
在Shader中使用:
写法:[特性类名(不加Drawer)]
材质面板显示:
自定义Group自动分组功能:
构想:一个特性Group,在Shader属性前添加时,作为折叠栏功能,如[Group]_group1表示一个折叠栏,如[Group(_MainTex,_IntTest)]_group2表示group2折叠栏下包含属性_MainTex与_IntTest属性的绘制或者不绘制。
如图:
效果:
实现:
在我们的构想中,Group可以包含子属性,也可以不包含,包含的子属性的数量不定,所以构建构造函数:
启用keys数组用来存放传递的命名,这些命名作为我们用来分组和查询属性的必要条件。
重写OnGUI函数:
这其中有一个GUIData调用,这是用来存放Shader的属性是否应该被绘制的数据类,如果不这样做,你就会发现就算使用了自定义的特性,这个属性还会被绘制一次,因为我们的实现逻辑需要在Group中处理其子属性的可见性。
GUIData类与自定义方法:
很简单,立面有一个字典,这个字典存放了属性的可见性,同样有两个方法,一个方法设置属性的可见性,一个方法获取属性的可见性。
然后,需要写一个自定义脚本,继承ShaderGUI并且在Shader中使用CustomEditor调用,我们自主实现Shader的OnGUI逻辑,如图:
为什么还要这样自定义ShaderGUI呢?因为上述说过,为了实现Group分组效果,需要避免其子属性不管有没有特性,都应该被Group折叠栏决定显影,若是不自主实现,你就会发现你自定义绘制了一遍,这些子属性不管有没有特性,也会再被绘制一遍,因为没有处理Unity默认的绘制逻辑,所以需要我们自定义一个ShaderGUI实现。
全部代码:
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;namespace CustomShaderPropertyDrawer
{#region Datapublic class GUIData{private static Dictionary<string, bool> propertyShow = new Dictionary<string, bool>();public static void SetPropertyVisible(string property, bool visiable){if (propertyShow.ContainsKey(property))propertyShow[property] = visiable;elsepropertyShow.Add(property, visiable);}public static bool GetPropertyVisible(string property){if (propertyShow.ContainsKey(property))return propertyShow[property];elsereturn false;}}#endregionpublic class GUIShow : ShaderGUI{internal MaterialProperty[] Pops { get; private set; }public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] properties){Pops = properties;var material = materialEditor.target as Material;var shader = material.shader;for (int i = 0; i < properties.Length; i++){if (System.String.Concat(shader.GetPropertyAttributes(i)).Contains("Group")){materialEditor.ShaderProperty(properties[i], properties[i].displayName);}else{if (GUIData.GetPropertyVisible(properties[i].name))materialEditor.ShaderProperty(properties[i], properties[i].displayName);}}}}#region MaterialPropertyDrawer/// <summary>/// Group分组,采样Flodout形式,可使用自定义GUIStyle/// </summary>public class GroupDrawer : MaterialPropertyDrawer{readonly string[] keys;public GroupDrawer() { }public GroupDrawer(params string[] keys){this.keys = keys;}public override void OnGUI(Rect position, MaterialProperty prop, GUIContent label, MaterialEditor editor){if (editor.customShaderGUI == null || !(editor.customShaderGUI is GUIShow))editor.DefaultShaderProperty(prop, prop.displayName);else{if (prop.type == MaterialProperty.PropType.Float || prop.type == MaterialProperty.PropType.Int){bool group_foldout = false;switch (prop.type){case MaterialProperty.PropType.Float:group_foldout = prop.floatValue == 1.0f ? true : false;EditorGUI.BeginChangeCheck();group_foldout = EditorGUI.Foldout(position, group_foldout, prop.displayName);if (EditorGUI.EndChangeCheck()){prop.floatValue = group_foldout ? 1f : 0f;if (keys != null && keys.Length > 0){foreach (var key in keys){GUIData.SetPropertyVisible(key, group_foldout);}}}break;case MaterialProperty.PropType.Int:group_foldout = prop.intValue == 1 ? true : false;EditorGUI.BeginChangeCheck();group_foldout = EditorGUI.Foldout(position, group_foldout, prop.displayName);if (EditorGUI.EndChangeCheck()){prop.intValue = group_foldout ? 1 : 0;if (keys != null && keys.Length > 0){foreach (var key in keys){GUIData.SetPropertyVisible(key, group_foldout);}}}break;}}}}}/// <summary>/// 单线贴图样式/// </summary>public class SingleLineTexture : MaterialPropertyDrawer{public override float GetPropertyHeight(MaterialProperty prop, string label, MaterialEditor editor){return 0;}public override void OnGUI(Rect position, MaterialProperty prop, GUIContent label, MaterialEditor editor){if (prop.type == MaterialProperty.PropType.Texture){editor.TexturePropertySingleLine(label, prop);}}}#endregion
}
注意:官方说明,为了性能,EditorGUILayout不能和MaterialPropertyDrawers一起使用,所以最好还是使用EditorGUI
更多内容可以参考官方MaterialEditor,MaterialPropertyDrawer,ShaderOnGUI,EditorGUI等编辑器内容,同时可以阅读我的编辑器扩展基础知识:Unity拓展编辑器基础知识