0 前言
今天遇到了一个需求,以下举例进行描述:
现有一个自定义类型Test,它有一个枚举类型变量speciesType和一个float类型变量height。
Test.cs
using UnityEngine;
public enum SpeciesType
{
People,
Null,
}
public class Test : MonoBehaviour
{
[SerializeField]
public SpeciesType speciesType;
public float height = 160.0f;
}
height变量仅在speciesType==SpeciesType.People时用来描述身高,也就是说,height变量仅在条件满足时起作用。
因此,我希望height变量仅在条件满足时才显示在Inspector窗口中。
理想的情况:
当speciesType的值为SpeciesType.People时:
当speciesType的值为其他时:
1 工具相关代码
1.UnityEditorHelper.cs
该脚本主要是为了辅助获取对应的属性值。
#if UNITY_EDITOR
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEditor;
public static class UnityEditorHelper
{
public static T GetActualObject<T>(this SerializedProperty property) {
try {
if (property == null)
return default(T);
var serializedObject = property.serializedObject;
if (serializedObject == null) {
return default(T);
}
var targetObject = serializedObject.targetObject;
var slicedName = property.propertyPath.Split('.').ToList();
List<int> arrayCounts = new List<int>();
for (int index = 0; index < slicedName.Count; index++) {
arrayCounts.Add(-1);
var currName = slicedName[index];
if (currName.EndsWith("]")) {
var arraySlice = currName.Split('[', ']');
if (arraySlice.Length >= 2) {
arrayCounts[index - 2] = Convert.ToInt32(arraySlice[1]);
slicedName[index] = string.Empty;
slicedName[index - 1] = string.Empty;
}
}
}
while (string.IsNullOrEmpty(slicedName.Last())) {
int i = slicedName.Count - 1;
slicedName.RemoveAt(i);
arrayCounts.RemoveAt(i);
}
return DescendHierarchy<T>(targetObject, slicedName, arrayCounts, 0);
}
catch {
return default(T);
}
}
public static object GetActualObjectParent(this SerializedProperty property) {
try {
if (property == null)
return default;
//获取当前序列化的Object
var serializedObject = property.serializedObject;
if (serializedObject == null) {
return default;
}
//获取targetObject,这里的targetObject就是
//我不好描述直接举个例子:a.b.c.d.e.f,比如serializedObject就是f,那么targetObject就是a
var targetObject = serializedObject.targetObject;
//还是上面的例子propertyPath其实就是a.b.c.d.e.f
//但是如果其中某一个是Array的话假设是b那么就会变成a.b.Array.data[x].c.d.e.f
//其中x为index
var slicedName = property.propertyPath.Split('.').ToList();
List<int> arrayCounts = new List<int>();
//根据"."分好后还需要获取其中的数组及其index保存在一个表中
for (int index = 0; index < slicedName.Count; index++) {
arrayCounts.Add(-1);
var currName = slicedName[index];
if (currName.EndsWith("]")) {
var arraySlice = currName.Split('[', ']');
if (arraySlice.Length >= 2) {
arrayCounts[index - 2] = Convert.ToInt32(arraySlice[1]);
slicedName[index] = string.Empty;
slicedName[index - 1] = string.Empty;
}
}
}
//清除数组导致的空
while (string.IsNullOrEmpty(slicedName.Last())) {
int i = slicedName.Count - 1;
slicedName.RemoveAt(i);
arrayCounts.RemoveAt(i);
}
//如果和属性名称相同则清除
if (slicedName.Last().Equals(property.name)) {
int i = slicedName.Count - 1;
slicedName.RemoveAt(i);
arrayCounts.RemoveAt(i);
}
//如果空了那么返回targetObject为当前的父对象
if (slicedName.Count == 0) return targetObject;
//继续清除数组,防止父对象也是数组
while (string.IsNullOrEmpty(slicedName.Last())) {
int i = slicedName.Count - 1;
slicedName.RemoveAt(i);
arrayCounts.RemoveAt(i);
}
//如果空了那么返回targetObject为当前的父对象
if (slicedName.Count == 0) return targetObject;
//获取父物体
return DescendHierarchy<object>(targetObject, slicedName, arrayCounts, 0);
}
catch {
return default;
}
}
//自己看
static T DescendHierarchy<T>(object targetObject, List<string> splitName, List<int> splitCounts, int depth) {
if (depth >= splitName.Count)
return default(T);
var currName = splitName[depth];
if (string.IsNullOrEmpty(currName))
return DescendHierarchy<T>(targetObject, splitName, splitCounts, depth + 1);
int arrayIndex = splitCounts[depth];
var newField = targetObject.GetType().GetField(currName,
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
if (newField == null) {
Type baseType = targetObject.GetType().BaseType;
while (baseType != null && newField == null) {
newField = baseType.GetField(currName,
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
baseType = baseType.BaseType;
}
}
var newObj = newField.GetValue(targetObject);
if (depth == splitName.Count - 1) {
T actualObject = default(T);
if (arrayIndex >= 0) {
if (newObj.GetType().IsArray && ((System.Array)newObj).Length > arrayIndex)
actualObject = (T)((System.Array)newObj).GetValue(arrayIndex);
var newObjList = newObj as IList;
if (newObjList != null && newObjList.Count > arrayIndex) {
actualObject = (T)newObjList[arrayIndex];
}
}
else {
actualObject = (T)newObj;
}
return actualObject;
}
else if (arrayIndex >= 0) {
if (newObj is IList) {
IList list = (IList)newObj;
newObj = list[arrayIndex];
}
else if (newObj is System.Array) {
System.Array a = (System.Array)newObj;
newObj = a.GetValue(arrayIndex);
}
}
return DescendHierarchy<T>(newObj, splitName, splitCounts, depth + 1);
}
}
#endif
2.PropertyActiveAttribute.cs
定义特性。
using UnityEngine;
namespace InspectorEx
{
public enum CompareType
{
Equal,
NonEqual,
Less,
LessEqual,
More,
MoreEqual,
}
/// <summary>
/// 特性:条件判断Inspector窗口属性的显隐
/// </summary>
public class PropertyActiveAttribute : PropertyAttribute
{
public string field;
public CompareType compareType;
public object compareValue;
public PropertyActiveAttribute(string fieldName, CompareType type, object value) {
field = fieldName;
compareType = type;
compareValue = value;
}
}
}
3.PropertyActiveEditor.cs
该脚本实现了条件控制Inspector属性显隐(主要通过重写PropertyDrawer的GetPropertyHeight和OnGUI方法实现)。
using System;
using System.Collections;
using UnityEditor;
using UnityEngine;
namespace InspectorEx
{
[CustomPropertyDrawer(typeof(PropertyActiveAttribute), false)]
public class PropertyActiveEditor : PropertyDrawer
{
private bool isShow = false;
/// <summary>
/// 修改属性占用高度
/// </summary>
public override float GetPropertyHeight(SerializedProperty property, GUIContent label) {
var attr = attribute as PropertyActiveAttribute;
var field = attr.field;
var compareType = attr.compareType;
var compareValue = attr.compareValue;
var parent = property.GetActualObjectParent();
var fieldInfo = parent.GetType().GetField(field);
var fieldValue = fieldInfo?.GetValue(parent);
isShow = IsMeetCondition(fieldValue, compareType, compareValue);
if (!isShow) return 0;
return base.GetPropertyHeight(property, label);
}
/// <summary>
/// 符合条件
/// </summary>
private bool IsMeetCondition(object fieldValue, CompareType compareType, object compareValue) {
if (compareType == CompareType.Equal) {
return fieldValue.Equals(compareValue);
}
else if (compareType == CompareType.NonEqual) {
return !fieldValue.Equals(compareValue);
}
else if (IsValueType(fieldValue.GetType()) && IsValueType(compareValue.GetType())) {
switch (compareType) {
case CompareType.Less:
return Comparer.DefaultInvariant.Compare(fieldValue, compareValue) < 0;
case CompareType.LessEqual:
return Comparer.DefaultInvariant.Compare(fieldValue, compareValue) <= 0;
case CompareType.More:
return Comparer.DefaultInvariant.Compare(fieldValue, compareValue) > 0;
case CompareType.MoreEqual:
return Comparer.DefaultInvariant.Compare(fieldValue, compareValue) >= 0;
}
}
return false;
}
/// <summary>
/// 是否是值类型
/// </summary>
private bool IsValueType(Type type) {
//IsPrimitive就是系统自带的类,IsValueType就是值类型,再排除char剩下的就是int,float这些了
return (type.IsPrimitive && type.IsValueType && type != typeof(char));
}
/// <summary>
/// 绘制
/// </summary>
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) {
if (isShow) EditorGUI.PropertyField(position, property, label, true);
}
}
}
2 结果
1.代码
using InspectorEx;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public enum SpeciesType
{
People,
Null,
}
public class Test : MonoBehaviour
{
[SerializeField]
public SpeciesType speciesType;
[PropertyActive("speciesType",CompareType.Equal,SpeciesType.People)]
public float height = 160.0f;
}
2.效果
同0中描绘的理想情况。