如何使用bool在Unity检查器中启用/禁用列表?

时间:2019-10-17 22:58:34

标签: c# unity3d unity-editor

我有一个名为ScriptableObject的{​​{1}}脚本,在关卡脚本中有一个GameObjects列表和一个名为Level的布尔变量。

我想做的是:我想在启用bool变量时启用该游戏对象列表,并在关闭时使用Unity自定义检查器启用它的“隐藏/灰色”显示(我们不能向其中添加元素)方法或属性抽屉。很难吗?

3 个答案:

答案 0 :(得分:1)

最好的方法是自定义属性。我将从显示最终结果开始:

  • 使用一个字段隐藏/显示另一个字段:

    public bool showHideList = false; 
    [ShowIf(ActionOnConditionFail.DontDraw, ConditionOperator.And, nameof(showHideList))]
    public string aField = "item 1";
    

    Image from Gyazo

  • 使用一个字段来启用/禁用另一个字段:

    public bool enableDisableList = false;
    
    [ShowIf(ActionOnConditionFail.JustDisable, ConditionOperator.And, 
    nameof(enableDisableList))]
    public string anotherField = "item 2";
    

    Image from Gyazo

  • 使用一种获取条件值的方法:

    [ShowIf(ActionOnConditionFail.JustDisable, ConditionOperator.And,nameof(CalculateIsEnabled))]
    public string yetAnotherField = "one more";    public 
    bool CalculateIsEnabled()    
    {
        return true;    
    }
    

    Image from Gyazo

  • 在同一字段上使用多个条件:

    public bool condition1;    
    public bool condition2;    
    [ShowIf(ActionOnConditionFail.JustDisable, ConditionOperator.And, nameof(condition1), 
    nameof(condition2))]    
    public string oneLastField= "last field";
    

    Image from Gyazo


它是如何做到的?

  1. 定义用于一次允许多重条件的选项:

    public enum ConditionOperator
    {
        // A field is visible/enabled only if all conditions are true.
        And,
        // A field is visible/enabled if at least ONE condition is true.
        Or,
    }
    
  2. 定义条件失败时如何绘制字段:

    public enum ActionOnConditionFail
    {
        // If condition(s) are false, don't draw the field at all.
        DontDraw,
        // If condition(s) are false, just set the field as disabled.
        JustDisable,
    }
    
  3. 现在创建一个自定义属性类,以保存有关条件的数据:

    using System;
    using UnityEngine;
    [AttributeUsage(AttributeTargets.Field, AllowMultiple = false, Inherited = true)]
    public class ShowIfAttribute : PropertyAttribute
    {
        public ActionOnConditionFail Action {get;private set;}
        public ConditionOperator Operator {get;private set;}
        public string[] Conditions {get;private set;}
    
         public ShowIfAttribute(ActionOnConditionFail action, ConditionOperator conditionOperator, params string[] conditions)
        {
            Action  = action;
            Operator = conditionOperator;
            Conditions = conditions;
        }
    }
    
  4. 我们要告诉人们如何使用ShowIfAttribute处理字段的最棘手的部分,此Drawer脚本必须位于任何“ Editor”文件夹下:

    using System.Reflection;
    using UnityEditor;
    using System.Collections.Generic;
    using System;
    using System.Linq;
    using UnityEngine;
    
    [CustomPropertyDrawer(typeof(ShowIfAttribute), true)]
    public class ShowIfAttributeDrawer : PropertyDrawer
    {
    
        #region Reflection helpers.
        private static MethodInfo GetMethod(object target, string methodName)
        {
            return GetAllMethods(target, m => m.Name.Equals(methodName, 
                      StringComparison.InvariantCulture)).FirstOrDefault();
        }
    
        private static FieldInfo GetField(object target, string fieldName)
        {
            return GetAllFields(target, f => f.Name.Equals(fieldName, 
                  StringComparison.InvariantCulture)).FirstOrDefault();
        }
        private static IEnumerable<FieldInfo> GetAllFields(object target, Func<FieldInfo, 
                bool> predicate)
        {
            List<Type> types = new List<Type>()
                {
                    target.GetType()
                };
    
            while (types.Last().BaseType != null)
            {
                types.Add(types.Last().BaseType);
            }
    
            for (int i = types.Count - 1; i >= 0; i--)
            {
                IEnumerable<FieldInfo> fieldInfos = types[i]
                    .GetFields(BindingFlags.Instance | BindingFlags.Static | 
       BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.DeclaredOnly)
                    .Where(predicate);
    
                foreach (var fieldInfo in fieldInfos)
                {
                    yield return fieldInfo;
                }
            }
        }
        private static IEnumerable<MethodInfo> GetAllMethods(object target, 
      Func<MethodInfo, bool> predicate)
        {
            IEnumerable<MethodInfo> methodInfos = target.GetType()
                .GetMethods(BindingFlags.Instance | BindingFlags.Static | 
      BindingFlags.NonPublic | BindingFlags.Public)
                .Where(predicate);
    
            return methodInfos;
        }
        #endregion
    
        private bool MeetsConditions(SerializedProperty property)
        {
            var showIfAttribute = this.attribute as ShowIfAttribute;
            var target = property.serializedObject.targetObject;
            List<bool> conditionValues = new List<bool>();
    
            foreach (var condition in showIfAttribute.Conditions)
            {
                FieldInfo conditionField = GetField(target, condition);
                if (conditionField != null &&
                    conditionField.FieldType == typeof(bool))
                {
                    conditionValues.Add((bool)conditionField.GetValue(target));
                }
    
                MethodInfo conditionMethod = GetMethod(target, condition);
                if (conditionMethod != null &&
                    conditionMethod.ReturnType == typeof(bool) &&
                    conditionMethod.GetParameters().Length == 0)
                {
                    conditionValues.Add((bool)conditionMethod.Invoke(target, null));
                }
            }
    
            if (conditionValues.Count > 0)
            {
                bool met;
                if (showIfAttribute.Operator == ConditionOperator.And)
                {
                    met = true;
                    foreach (var value in conditionValues)
                    {
                        met = met && value;
                    }
                }
                else
                {
                    met = false;
                    foreach (var value in conditionValues)
                    {
                        met = met || value;
                    }
                }
                return met;
            }
            else
            {
                Debug.LogError("Invalid boolean condition fields or methods used!");
                return true;
            }
        }
        public override float GetPropertyHeight(SerializedProperty property, GUIContent 
                     label)
        {
            // Calcluate the property height, if we don't meet the condition and the draw 
        mode is DontDraw, then height will be 0.
            bool meetsCondition = MeetsConditions(property);
            var showIfAttribute = this.attribute as ShowIfAttribute;
    
            if (!meetsCondition && showIfAttribute.Action == 
                                           ActionOnConditionFail.DontDraw)
                return 0;
            return base.GetPropertyHeight(property, label);
        }
    
        public override void OnGUI(Rect position, SerializedProperty property, GUIContent 
               label)
        {
            bool meetsCondition = MeetsConditions(property);
            // Early out, if conditions met, draw and go.
            if (meetsCondition)
            {
                EditorGUI.PropertyField(position, property, label, true);
                return; 
            }
    
            var showIfAttribute = this.attribute as ShowIfAttribute;
            if(showIfAttribute.Action == ActionOnConditionFail.DontDraw)
            {
                return;
            }
            else if (showIfAttribute.Action == ActionOnConditionFail.JustDisable)
            {
                EditorGUI.BeginDisabledGroup(true);
                EditorGUI.PropertyField(position, property, label, true);
                EditorGUI.EndDisabledGroup();
            }
    
        }
    }
    

下一步

  1. 实施逆向条件处理程序,即,如果条件为false,则对其进行编辑以启用字段,反之亦然。
  2. 当前,对于列表和数组,unit将禁用/启用列表的元素,但保持列表计数字段处于启用状态,尝试实施一种解决方案来处理这种情况。

答案 1 :(得分:0)

public class Test : MonoBehaviour
{
    [SerializeField]
    bool openFlag = false;
    bool prevOpenFlag = false;
    void Update()
    {
        if(prevOpenFlag != openFlag)
        {
            if (openFlag == true)
            {
                Open();
                prevOpenFlag = openFlag;
            }else
            {
                Close();
                prevOpenFlag = openFlag;
            }
        }
}

答案 2 :(得分:-1)

要隐藏变量,请看here

但是,如果您希望将字段变灰,here就会消失。