Как включить / отключить Список в Инспекторе Unity, используя bool? - PullRequest
1 голос
/ 18 октября 2019

У меня есть сценарий ScriptableObject с именем Level, внутри сценария уровня у меня есть список GameObjects и переменная bool с именем introduceNewEnemies.

Я пытаюсь сделать следующее:Я хочу включить этот Список игровых объектов, когда переменная bool включена, и Скрыть / Серая (мы не можем добавить к ней элементы), когда она выключена, с помощью пользовательских методов инспектора Unity или ящиков свойств. Это сложно сделать?

Ответы [ 3 ]

1 голос
/ 18 октября 2019

Лучший подход - это пользовательский атрибут. Я начну с показа конечного результата:

  • Использование поля, чтобы скрыть / показать другое поле:

    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 должен бытьв любой папке «Редактор»:

    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. Реализуйте обработчик обратных условий, то есть измените его, чтобы включить поле, если условия ложные, и наоборот.
  2. В настоящее время для списков и массивов модуль отключит / включит элементы списка, но сохранитполе списка счетчиков включено, попробуйте реализовать решение для этого случая.
0 голосов
/ 18 октября 2019
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;
            }
        }
}
0 голосов
/ 18 октября 2019

Чтобы скрыть переменную, взгляните здесь .

Но если вы хотите выделить поле серым цветом, здесь вы идете.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...