[英]UNITY, How can I get all properties from another C# class and put them into an enum
I have two classes:我有两节课:
public class Stats : MonoBehaviour
{
// Primary Stats
public int strength;
public int agility;
public int intellect;
public int stamina;
public int spirit;
}
and和
public class EquipmentProperties : ItemProperties
{
public Stats stats;
}
public enum Stats
{//variables from "Stats" class to be in this enum
}
I am trying to get all the variables from the Stats class to be in the stats enum without having to manually enter them..我正在尝试从 Stats class 中获取所有变量,而无需手动输入它们。
"I am trying to get all the variables from the Stats class to be in the stats enum without having to manually enter them"
“我正在尝试从 Stats class 中获取所有变量,而无需手动输入它们”
Enums must be specified at compile time, you can't dynamically add enums during run-time.枚举必须在编译时指定,您不能在运行时动态添加枚举。 If you would like to stablish the fields of your enum dynamically with your class variables, guess that because the
Stats
class might change along the app's development, you would need to store that enum somewhere, because if not you would need to access the fields of the dynamic enum according to that generic way of setting the enumeration, in a kind of meta-programming templated way that would not make much sense.如果您想使用 class 变量动态建立枚举字段,请猜测因为
Stats
class 可能会随着应用程序的开发而改变,您需要将该枚举存储在某处,因为如果不是,您需要访问以下字段动态枚举根据设置枚举的通用方式,以一种没有多大意义的元编程模板方式。
So along with your question comes the question of how to store that enum to be used afterwards I guess.因此,随着您的问题而来的是我猜如何存储该枚举以供以后使用的问题。 For that you can check EnumBuilder class .
为此,您可以检查EnumBuilder class 。
Extending that example, you can build the enum according to the specific Stats
class like this:扩展该示例,您可以根据特定的
Stats
class 构建枚举,如下所示:
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
public class Stats
{
// Primary Stats
public int strength;
public int agility;
public int intellect;
public int stamina;
public int spirit;
}
class Example
{
public static List<string> getFields(Type type) {
var propertyValues = type.GetFields();
var result = new Stats[propertyValues.Length];
var retStr = new List<string>();
for (int i = 0; i < propertyValues.Length; i++) {
retStr.Add(propertyValues[i].Name);
}
return retStr;
}
public static void Main() {
// Get the current application domain for the current thread.
AppDomain currentDomain = AppDomain.CurrentDomain;
// Create a dynamic assembly in the current application domain,
// and allow it to be executed and saved to disk.
AssemblyName aName = new AssemblyName("TempAssembly");
AssemblyBuilder ab = currentDomain.DefineDynamicAssembly(
aName, AssemblyBuilderAccess.RunAndSave);
// Define a dynamic module in "TempAssembly" assembly. For a single-
// module assembly, the module has the same name as the assembly.
ModuleBuilder mb = ab.DefineDynamicModule(aName.Name, aName.Name + ".dll");
// Define a public enumeration with the name "Elevation" and an
// underlying type of Integer.
EnumBuilder eb = mb.DefineEnum("Stats", TypeAttributes.Public, typeof(int));
int fieldCount = 0;
getProperties(typeof(Stats)).ForEach(field => {
eb.DefineLiteral(field, fieldCount);
fieldCount++;
});
// Define two members, "High" and "Low".
//eb.DefineLiteral("Low", 0);
//eb.DefineLiteral("High", 1);
// Create the type and save the assembly.
Type finished = eb.CreateType();
ab.Save(aName.Name + ".dll");
foreach (object o in Enum.GetValues(finished)) {
Console.WriteLine("{0}.{1} = {2}", finished, o, ((int)o));
}
Console.ReadLine();
}
}
Output: Output:
Stats.strength = 0 Stats.strength = 0
Stats.agility = 1 Stats.agility = 1
Stats.intellect = 2 Stats.intellect = 2
Stats.stamina = 3 Stats.stamina = 3
Stats.spirit = 4 Stats.spirit = 4
This is of course not what you are asking directly since it is not automatic but I would suggest a Dictionary<Stats, int>
and do eg这当然不是您直接要求的,因为它不是自动的,但我建议使用
Dictionary<Stats, int>
并执行例如
public class StatsComponent : MonoBehaviour
{
// Make these only assignable via the Inspector
[SerializeField] private int strength;
[SerializeField] private int agility;
[SerializeField] private int intellect;
[SerializeField] private int stamina;
[SerializeField] private int spirit;
public readonly Dictionary<Stats, int> stats = new Dictionary<Stats, int>();
private void Awake ()
{
// Initialize once with values from the Inspector
stats.Add(Stats.Strength, strength);
stats.Add(Stats.Agility, agility);
stats.Add(Stats.Intellect, intellect);
stats.Add(Stats.Stamina, stamina);
stats.Add(Stats.Spirit, spirit);
}
}
public enum Stats
{
Strength,
Agility,
Intellect,
Stamina,
Spirit
}
Of course there are ways to automatize that via reflection but I'm sure it will bring you more headaches and issues then it is solving - that's only an opinion of course.当然,有一些方法可以通过反射实现自动化,但我相信它会给你带来更多的头痛和问题,然后它正在解决——这当然只是一种观点。
If you don't want to type things twice you could instead of an enum rather go by index or strings eg using SerializedDictionary
you could simply have a如果您不想输入两次内容,则可以通过索引或字符串代替枚举而不是 go,例如使用
SerializedDictionary
您可以简单地拥有一个
public SerializedDictionary<string, int> stats;
and fill it in the Inspector and not have your fields at all.并将其填写在 Inspector 中,并且根本没有您的字段。
However, if you still want at least automation with a minimal effort to build on top of this answer I made this a tool EditorWindow you can directly use within Unity.但是,如果您仍然希望在此答案之上以最少的努力实现自动化,我将其作为一个工具 EditorWindow,您可以直接在 Unity 中使用。
Just place this script anywhere in your project.只需将此脚本放在项目中的任何位置即可。
#if UNITY_EDITOR
using System;
using System.IO;
using System.Linq;
using System.Text;
using UnityEditor;
using UnityEngine;
public class EnumGeneratorWindow : EditorWindow
{
// This is of course optional but I thought it makes sense to filer for a specific field type
private enum FieldType
{
Int,
Float,
Bool,
// more could of course be added
}
private MonoScript sourceScript;
private MonoScript targetScript;
private FieldType fieldType;
private Type GetFieldType()
{
return fieldType switch
{
FieldType.Int => typeof(int),
FieldType.Float => typeof(float),
FieldType.Bool => typeof(bool),
// according to the enum add more cases
_ => null
};
}
[MenuItem("Window/ENUM GENERATOR")]
private static void Init()
{
var window = GetWindow<EnumGeneratorWindow>();
window.Show();
}
private void OnGUI()
{
EditorGUILayout.LabelField("ENUM GENERATOR", EditorStyles.boldLabel);
sourceScript = EditorGUILayout.ObjectField("Source", sourceScript, typeof(MonoScript), false) as MonoScript;
if (!sourceScript)
{
EditorGUILayout.HelpBox("Reference the script where to fetch the fields from", MessageType.None, true);
return;
}
var sourceType = sourceScript.GetClass();
if (sourceType == null)
{
EditorGUILayout.HelpBox("Could not get Type from source file!", MessageType.Error, true);
return;
}
targetScript = EditorGUILayout.ObjectField("Target", targetScript, typeof(MonoScript), false) as MonoScript;
if (!targetScript)
{
EditorGUILayout.HelpBox("Reference the script where write the generated enum to", MessageType.None, true);
return;
}
if (targetScript == sourceScript)
{
EditorGUILayout.HelpBox("The source and target script should probably rather not be the same file ;)", MessageType.Error, true);
return;
}
var targetType = targetScript.GetClass();
if (targetType == null)
{
EditorGUILayout.HelpBox("Could not get Type from target file!", MessageType.Error, true);
return;
}
fieldType = (FieldType)EditorGUILayout.EnumPopup("Field Type", fieldType);
EditorGUILayout.Space();
EditorGUILayout.LabelField("Preview", EditorStyles.boldLabel);
var fields = sourceType.GetFields().Where(f => f.FieldType == GetFieldType()).Select(f => f.Name).ToArray();
var fileContent = new StringBuilder("public enum ").Append(targetType.Name).Append(" { ");
for (var i = 0; i < fields.Length; i++)
{
if (i != 0)
{
fileContent.Append(", ");
}
fileContent.Append(fields[i]);
}
fileContent.Append(" }");
EditorGUILayout.LabelField(fileContent.ToString());
var color = GUI.color;
GUI.color = Color.red;
GUILayout.BeginVertical();
{
EditorGUILayout.LabelField("! DANGER ZONE !", EditorStyles.boldLabel);
EditorGUILayout.Space();
if (GUILayout.Button("GENERATE ENUM"))
{
var targetID = targetScript.GetInstanceID();
// e.g. Assets/SomeFolder/MyStats.cs
var targetAssetPath = AssetDatabase.GetAssetPath(targetID);
// just as a safety net
if (EditorUtility.DisplayDialog("Generate and overwrite with enum?", $"Attention\n\nThis will overwrite any content of {targetAssetPath} with the new content.\n\nAre you sure?", "Yes generate", "OMG NO! Cancel this!"))
{
// a bit of a hack but we need to convert the Unity asset path into a valid system path by erasing one duplicate "Assets"
var pathParts = targetAssetPath.Split('/').ToArray();
// overwrite the "Assets" with the full path to Assets
pathParts[0] = Application.dataPath;
// re-combine all path parts but this time use the according system file path separator char
var targetSystemPath = Path.Combine(pathParts);
// Write the content into the file via the normal file IO
File.WriteAllText(targetSystemPath, fileContent.ToString());
// trigger a refresh so unity re-loads and re-compiles
AssetDatabase.Refresh();
}
}
}
GUILayout.EndVertical();
GUI.color = color;
}
}
#endif
How it works:这个怎么运作:
MonoBehaviour
script into "Source"MonoBehaviour
脚本拖入“Source”Here is a little demo;)这是一个小演示;)
I just start of using Example.cs
我刚开始使用
Example.cs
public class Example : MonoBehaviour
{
public int someInt, anotherInt;
public float someFloat, anotherFloat, andOnMore;
public bool someBool, yetAnotherBool;
}
and ExampleEnum.cs
和
ExampleEnum.cs
public enum ExampleEnum
{
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.