通过检查员选择要引用的列表

时间:2019-07-08 15:57:41

标签: c# unity3d

我有一个包含列表的非单行为类,该列表在编译时固定在石头上。我有一些需要重用的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 个答案:

答案 0 :(得分:3)

根据其他人的建议,我不会不推荐在这里使用Reflection

反射总是很慢,尤其是当您要逐帧访问和更改列表中的值时,这不是最好的主意。

课程的一个(也是唯一一个)优点:(一旦您终于实现了所需的反射和附加的EditorScript!),该选项将自动填充该类中的每个列表。

巨大的缺点:每当MonoBehaviour需要此功能时,您都必须重复此操作并实现一个新的编辑器脚本。


您可以使用简单的enumDictionary来代替,这会产生最小的开销,即将相应的列表名称添加到enumDictionary中,就像< / p>

[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}
        };
    }
}

然后,为了访问和更改特定列表,您可以通过脚本中的检查器设置枚举类型

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

...

var listToBeChanged = someBoat.ListByType[listToChange];

使用此检查器,您可以自动为您处理所有操作,并且可以在编辑器中和运行时工作,而无需任何额外的开销。


少量演示代码

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

答案 1 :(得分:2)

您可以尝试使用C#Reflection和编辑器自定义来自动使用字段名称填充下拉列表。假设您要定位的每个字段都以“广告资源”结尾。

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);
        // #######################################
    }
}

答案 2 :(得分:1)

设置一个公共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。