如何将Class对象列表传递给枚举?
当前,我有一个从XML文件中提取数据的类结构。它们是这样的:
[XmlRoot("ManufacturerContainer")]
public class ManufacturerContainer
{
[XmlArray("Manufacturers")]
[XmlArrayItem("Manufacturer")]
public List<Manufacturer> Manufacturers { get; set; }
}
public class Manufacturer
{
[XmlAttribute("name")]
public string Name { get; set; }
[XmlElement("Model")]
public List<Model> Models { get; set; }
}
public class Model
{
[XmlAttribute("name")]
public string Name { get; set; }
我想做的是创建2个枚举下拉框,“模型”下拉列表取决于制造商。您将选择制造商,并且型号将更新。您如何动态更改枚举?
谢谢!
答案 0 :(得分:0)
一个不能/不应该在运行时尝试更改enum
。简单地说,enum
相当于constant
字典,您无法在运行时对其进行修改。
即使可能,任何代码(例如switch-case或取决于该枚举的if-else可能也会失败)。
没有简单的快速解决方案。对于您想做的事情,您将必须深入了解Unity Editor脚本并使用string[]
作为选项(例如,此处的名称)和
首先,我将向您的类添加一些getter方法(所有示例中的Name
都是唯一的,并且任何制造商或一个制造商内的模型都不具有相同的名称):
[XmlRoot("ManufacturerContainer")]
public class ManufacturerContainer
{
[XmlArray("Manufacturers")]
[XmlArrayItem("Manufacturer")]
public List<Manufacturer> Manufacturers { get; set; }
// Get all available Manufacturers' names
public IEnumerable<string> GetAvailableManufacturers()
{
// use Linq to get a list of only the names
return Manufacturers.Select(manufacturer => manufacturer.Name);
}
// Get a specific Manufacturer by name
public Manufacturer GetManufacturerByName(string name)
{
// a little sanaty check
if (GetAvailableManufacturers().Contains(name))
{
// again use Linq to return the Manufacturer by name
// (provided that they are unique of course)
return Manufacturers.Find(manufacturer => string.Equals(name, manufacturer.Name));
}
Debug.LogWarningFormat("No Manufacturer with name {0} found!", name);
return null;
}
// and finally get a specific Model by manufacturer and model name
public Model GetModelByName(string manufacturerId, string modelId)
{
// first get the according manufacturer
var manufacturer = GetManufacturerByName(manufacturerId);
if (manufacturer != null)
{
// get the model by name (see method below)
return manufacturer.GetModelByName(modelId);
}
Debug.LogWarningFormat("Couldn't get Manufacturer with id {0}", manufacturerId);
return null;
// then the according model
}
}
public class Manufacturer
{
[XmlAttribute("name")] public string Name { get; set; }
[XmlElement("Model")] public List<Model> Models { get; set; }
// Get all available Models' names
public List<string> GetAvailableModels()
{
return Models.Select(model => model.Name).ToList();
}
// The same as for getting a Manufacturer by name
public Model GetModelByName(string name)
{
// a little sanaty check
if (GetAvailableModels().Contains(name))
{
// again use Linq to return the Model by name
// (provided that they are unique of course)
return Models.Find(model => string.Equals(name, model.Name));
}
Debug.LogWarningFormat("No Manufacturer with name {0} found!", name);
return null;
}
}
public class Model
{
[XmlAttribute("name")] public string Name { get; set; }
}
现在您可以
所以下一步就是根据您的需要为您的班级写一个CustomEditor或CustomPropertyDrawer。
我将为您提供有关PropertyDrawer外观的非常简化的想法。不幸的是,编辑器脚本总是会变得越来越庞大,并且您必须手动执行许多操作:
[CustomPropertyDrawer(typeof(ModelSelection))]
public class ModelSelectionDrawer : PropertyDrawer
{
// Lets just say we want the property to have a height of 3 lines
// one for the label, and one for each dropdown
// It makes it easier for now to place the dropdowns
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
return EditorGUIUtility.singleLineHeight * 3;
}
// Kind of like the Inspectors update method
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
// Using BeginProperty / EndProperty on the parent property means that
// prefab override logic works on the entire property.
EditorGUI.BeginProperty(position, label, property);
// Draw label
EditorGUI.LabelField(position, label);
// go to next line
position.y += EditorGUIUtility.singleLineHeight;
// Don't make child fields be indented
var indent = EditorGUI.indentLevel;
EditorGUI.indentLevel = 0;
// first of all link the according variables
var _manufacturerId = property.FindPropertyRelative("ManufacturerId");
var _modelId = property.FindPropertyRelative("ModelId");
// You somehow need access to the reference of ManufacturerContainer
// I will assume here you now how to make it accessable all over your scene
// and pretend it is already stored in this variable
// I just create one right here in order to have something to show already
var container = new ManufacturerContainer
{
Manufacturers = new List<Manufacturer>
{
new Manufacturer
{
Name = "Man_1",
Models = new List<Model>
{
new Model
{
Name = "Model_A"
},
new Model
{
Name = "Model_B"
}
}
},
new Manufacturer
{
Name = "Man_2",
Models = new List<Model>
{
new Model
{
Name = "Model_C"
},
new Model
{
Name = "Model_D"
}
}
}
}
};
// Now you would get the available manufacturers
var availableManufacturers = container.GetAvailableManufacturers().ToArray();
// Make the dropdown field for manufacturer
// Get the position for this field
var field1_rect = new Rect(position.x, position.y, position.width, EditorGUIUtility.singleLineHeight);
// add a label
field1_rect = EditorGUI.PrefixLabel(field1_rect, new GUIContent("Manufacturer"));
position.y += EditorGUIUtility.singleLineHeight;
// get index of currently selected manufacturer
var currentManIndex = availableManufacturers.ToList().IndexOf(_manufacturerId.stringValue);
// clamp to minimum 0 (it returns -1 if the string is incorrect/not found in the list)
currentManIndex = Mathf.Max(currentManIndex, 0);
// problem it returns an int -> index
// => if you add or delete an item later in middle the XML it breaks
// you would have to fix this on your own
var newManIndex = EditorGUI.Popup(field1_rect, currentManIndex, availableManufacturers.ToArray());
// now we have the new index but we have to write back the selected object to the target class
_manufacturerId.stringValue = availableManufacturers[newManIndex];
// Now that you have a manufacturer selected you can get and select the model
var availableModels = container.GetManufacturerByName(_manufacturerId.stringValue).GetAvailableModels().ToArray();
// Make the dropdown field for manufacturer
// Get the position for this field
var field2_rect = new Rect(position.x, position.y, position.width, EditorGUIUtility.singleLineHeight);
// add a label
field2_rect = EditorGUI.PrefixLabel(field2_rect, new GUIContent("Model"));
position.y += EditorGUIUtility.singleLineHeight;
// get index of currently selected manufacturer
var currentModIndex = availableModels.ToList().IndexOf(_modelId.stringValue);
// also clamp this to minimum 0
currentModIndex = Mathf.Max(currentModIndex, 0);
// problem it returns an int -> index
// => if you add or delete an item later in middle the XML it breaks
// you would have to fix this on your own
var newModIndex = EditorGUI.Popup(field2_rect, currentModIndex, availableModels.ToArray());
// now we have the new index but we have to write back the selected object to the target class
_modelId.stringValue = availableModels[newModIndex];
// Set indent back to what it was
EditorGUI.indentLevel = indent;
EditorGUI.EndProperty();
}
}
如前所述,这只是一个示例,肯定还有许多其他检查和应添加的内容。但是您稍后将在诸如此类的任何类中使用此属性
public class Blarf : MonoBehaviour
{
public ModelSelection selection;
}
,它在编辑器中将如下所示:
剩下的取决于您选择了这两个值之后您要做什么。您可以例如只是想使用{p> 1获得相应的Model
// reference to your container
container.GetModelByName(selection.ManufacturerId, selection.ModelId);