我有一个包含列表的非单行为类,该列表在编译时固定在石头上。我有一些需要重用的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>();
}
答案 0 :(得分:3)
根据其他人的建议,我不会不推荐在这里使用Reflection!
反射总是很慢,尤其是当您要逐帧访问和更改列表中的值时,这不是最好的主意。
课程的一个(也是唯一一个)优点:(一旦您终于实现了所需的反射和附加的EditorScript!),该选项将自动填充该类中的每个列表。
巨大的缺点:每当MonoBehaviour需要此功能时,您都必须重复此操作并实现一个新的编辑器脚本。
您可以使用简单的enum
和Dictionary
来代替,这会产生最小的开销,即将相应的列表名称添加到enum
和Dictionary
中,就像< / 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];
}
}
答案 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。