Выберите, какой список ссылаться через инспектора - PullRequest
1 голос
/ 08 июля 2019

У меня есть класс non-monobehaviour, содержащий списки, установленные во время компиляции.У меня есть некоторый монобихвичный код, который нужно многократно использовать, который берет один из этих списков и модифицирует его.Я хотел бы иметь возможность выбрать, какой список будет изменен через инспектора, но я не могу найти хороший ответ Google, чтобы получить меня там.

Желательно, чтобы я мог добавлять списки к немонобею без изменения кода, отвечающего за выбор списка.

Любые советы?

Примеры списков длябыть изменены:

[System.Serializable]
public class BOAT
{
    public List<BlockScriptableObject> DefendInventory = new List<BlockScriptableObject>();
    public List<BlockScriptableObject> AssistInventory = new List<BlockScriptableObject>();
    public List<BlockScriptableObject> MiscInventory = new List<BlockScriptableObject>();
}

Ответы [ 3 ]

3 голосов
/ 08 июля 2019

Я бы не рекомендовал , используя Отражение здесь, как предлагают другие!

Отражение всегда довольно медленное, особенно когда вы собираетесь получить доступ и изменить значения вваш список на основе фреймов - это не лучшая идея.

Единственное (и единственное) преимущество курса: (После того, как вы наконец-то реализовали необходимое отражение и дополнительный EditorScript!), параметры заполняются автоматически для каждогосписок в классе.

Огромный недостаток: вы должны повторять это и реализовывать новый скрипт редактора каждый раз, когда MonoBehaviour нуждается в этой функциональности.


Вы можете использовать простой enum и Dictionary вместо этого, который идет с минимальными затратами на добавление соответствующих имен списков к enum и к Dictionary как

[Serializable]
public class BOAT
{
    public enum ListType
    {
        DefendInventory,
        AssistInventory,
        MiscInventory
    }

    public List<BlockScriptableObject> DefendInventory = new List<BlockScriptableObject>();
    public List<BlockScriptableObject> AssistInventory = new List<BlockScriptableObject>();
    public List<BlockScriptableObject> MiscInventory = new List<BlockScriptableObject>();

    public Dictionary<ListType, List<BlockScriptableObject>> ListByType;

    // Initialize the Dictionary in the default constructor
    public BOAT()
    {
        ListByType = new Dictionary<ListType, List<BlockScriptableObject>>
        {
            {ListType.DefendInventory, DefendInventory},
            {ListType.AssistInventory, AssistInventory},
            {ListType.MiscInventory, MiscInventory}
        };
    }
}

Затем для доступа и изменения определенного списка, который вы установилитип enum через инспектора в вашем скрипте

// gives you a Dropdown for available ListType values
// in the Inspector
public BOAT.ListType listToChange;

...

var listToBeChanged = someBoat.ListByType[listToChange];

Используя это, инспектор обрабатывает все это автоматически, и этоrks в редакторе и во время выполнения без каких-либо дополнительных затрат.


Маленький демонстрационный код

public class blarf : MonoBehaviour
{
    public BOAT.ListType listToChange;
    public BOAT boat;

    public List<BlockScriptableObject> currentList;

    // only for the demo (later you would rather do this in a Property)
    // update the current accessed and changed list according to the 
    // selected ListType
    private void Update()
    {
        currentList = boat.ListByType[listToChange];
    }
}

enter image description here

2 голосов
/ 08 июля 2019

Вы можете попробовать использовать C # Reflection и настройки редактора, чтобы автоматически заполнять выпадающий список именами ваших полей.Предположим, что каждое поле, на которое вы можете нацелить, заканчивается "Inventory".

using UnityEditor;
using UnityEngine;
using System.Reflection;

[CustomEditor(typeof(ModifierMB))]
public class ModifierMBEditor : Editor
{
    public SerializedProperty nameOfListToEdit;

    void OnEnable() {
        nameOfListToEdit = serializedObject.FindProperty("nameOfListToEdit");
    }

    public override void OnInspectorGUI() {
        serializedObject.Update();
        // use Reflection to get the names of the fields
        string[] fieldNames = typeof(ModifierMB).GetFields()
            .Select(field => field.Name)
            .Where(name => name.EndsWith("Inventory"))
            .ToArray();
        int index = 0;
        index = EditorGUILayout.Popup(index, fieldNames);
        nameOfListToEdit.stringValue = fieldNames[index];
        serializedObject.ApplyModifiedProperties();
    }
}

Затем в MonoBehavior ...

using UnityEngine;
using System.Reflection;

public class ModifierMB : MonoBehavior
{
    string nameOfListToEdit;

    BOAT objectContainingLists;

    public void ModifyLists() {
        FieldInfo fieldToEdit = typeof(objectContainingLists).GetField(nameOfListToEdit);
        List<BlockScriptableObject> listToEdit = fieldToEdit.GetValue(objectContainingLists);
        // modify listToEdit here ################
        Debug.Log(listToEdit);
        // #######################################
    }
}

Обратите внимание, что это не идеальное решение,потому что он использует отражение каждый раз, когда вы хотите изменить список.Отражение, как правило, очень медленное и неэффективное, вы можете найти способ кэширования списка, который будет редактироваться.Например, вы можете выполнить Reflection, когда инициализируется MonoBehavior, и затем кэшировать listToEdit в поле, чтобы получить к нему доступ / изменить позже.

Кэширование listToEdit в поле можно выполнить вдольэти строки:

using UnityEditor;
using UnityEngine;
using System.Reflection;

[CustomEditor(typeof(ModifierMB))]
public class ModifierMBEditor : Editor
{
    public SerializedProperty nameOfListToEdit;

    void OnEnable() {
        nameOfListToEdit = serializedObject.FindProperty("nameOfListToEdit");
    }

    public override void OnInspectorGUI() {
        serializedObject.Update();
        // use Reflection to get the names of the fields
        string[] fieldNames = typeof(ModifierMB).GetFields()
            .Select(field => field.Name)
            .Where(name => name.EndsWith("Inventory"))
            .ToArray();
        int index = 0;
        index = EditorGUILayout.Popup(index, fieldNames);
        nameOfListToEdit.stringValue = fieldNames[index];
        serializedObject.ApplyModifiedProperties();

        ModifierMB modifier = (ModifierMB)target; 
        modifier.listToEdit = typeof(modifier.objectContainingLists).GetField(
                                      fieldNames[index]).GetValue(
                                              modifier.objectContainingLists);
    }
}

Затем в MonoBehavior ...

using UnityEngine;
using System.Reflection;

public class ModifierMB : MonoBehavior
{
    string nameOfListToEdit;

    public BOAT objectContainingLists;

    public List<BlockScriptableObject> listToEdit;

    public void ModifyLists() {
        // modify listToEdit here ################
        Debug.Log(listToEdit);
        // #######################################
    }
}
1 голос
/ 08 июля 2019

Сделайте public int и используйте его в качестве индекса, проверьте, когда он изменится, и присвойте требуемый список.

    public int Index;
    private int currentIndex;

    void Update()
    {
       // Check if was updated
       if(Index != currentIndex)
       {
          currentIndex = Index ;

          switch(Index)
         { 
            case 1:
            currestList = DefendInventory ;
             break;
         ...
         }
       }
    }

Вы можете использовать перечисление вместо int для раскрывающегося списка в инспекторе.

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