简体   繁体   English

UNITY,我如何从另一个 C# class 获取所有属性并将它们放入枚举

[英]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

Prolog Prolog

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.当然,有一些方法可以通过反射实现自动化,但我相信它会给你带来更多的头痛和问题,然后它正在解决——这当然只是一种观点。


Intermediate Solution中间解决方案

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 中,并且根本没有您的字段。


Enum Generator Window枚举生成器 Window

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:这个怎么运作:

  • Open the window via the header menu -> Window -> ENUM GENERATOR通过 header 菜单打开 window -> Window -> ENUM GENERATOR
  • Drag in your MonoBehaviour script into "Source"MonoBehaviour脚本拖入“Source”
  • Create a new empty script with the enum name you like使用您喜欢的枚举名称创建一个新的空脚本
  • Drag your enum target script into "Target"将您的枚举目标脚本拖入“目标”
  • Select the type of fields we are looking for Select 我们要查找的字段类型
  • Finally hit generate最后点击生成

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.csExampleEnum.cs

public enum ExampleEnum
{

}

在此处输入图像描述

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何从Listbox(在C#中)中获取所有元素/字段并将它们放入DataTable中? - How can I take all the elements / fields from a Listbox (in C#) and put them into a DataTable? UNITY C# - 如何获取标有自定义属性(字段)的 class 实例的所有字段? - UNITY C# - How can I get all the fields of the an instance of a class marked with my custom atribute(the fields)? 如何在C#中从IDataReader Func调用的另一个属性中访问类属性 - How can i access a class properties from within another one called from a IDataReader Func in C# 我如何获得多个价格数据并使用 Selenium 将它们排序并放入带有 c# 的标签? - How can i get plural price data and put them order and put in labels with c# using Selenium? 如何在 switch 语句中使用枚举 [Unity c#] - How can I use Enum in switch statement [Unity c#] 获取另一个类的属性的所有名称-Unity 5 C# - Get all names of property of another class - Unity 5 C# 如何从其他 class 获取帐号和密码并在 c# 中查看登录屏幕 - How can I get the account number and password from the other class and check them for the login screen in c# 如何从另一个脚本统一获取枚举 - how to get an enum from another script in unity 如何在另一个 class 方法中使用 class 和构造函数并将它们存储到我的列表中? C# - How can I use a class and constructor in a another class, method and store them to my list? C# C#:从另一个类访问枚举 - C#: Access Enum from another class
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM