C# Unity:如何将未知脚本动态附加到游戏对象(自定义编辑器)

C# Unity:如何将未知脚本动态附加到游戏对象(自定义编辑器),c#,unity3d,unity5,C#,Unity3d,Unity5,我目前正在为Unity编辑器(自定义检查器和自定义窗口)制作一个系统,该系统将使制作游戏的艺术家实现自动化并使事情变得更容易,但我遇到了麻烦 我试图找到一种方法,通过编辑器文本字段输入和GUI按钮,将未知脚本动态添加到场景中的游戏对象中。 艺术家/程序员将在文本字段中键入脚本的名称,它将搜索并添加到gameobject,但我不知道如何继续,特别是因为自Unity 5.3起,gameobject.AddComponent()的某些函数已被弃用 以下是我试图做的: public string scr

我目前正在为Unity编辑器(自定义检查器和自定义窗口)制作一个系统,该系统将使制作游戏的艺术家实现自动化并使事情变得更容易,但我遇到了麻烦

我试图找到一种方法,通过编辑器文本字段输入和GUI按钮,将未知脚本动态添加到场景中的游戏对象中。 艺术家/程序员将在文本字段中键入脚本的名称,它将搜索并添加到gameobject,但我不知道如何继续,特别是因为自Unity 5.3起,
gameobject.AddComponent()
的某些函数已被弃用

以下是我试图做的:

public string scriptname;
GameObject obj = null;
scriptname = EditorGUILayout.TextField("Script name:", scriptname, GUILayout.MaxHeight(25));
if (GUILayout.Button("Attach script"))
{
    //search for the script to check if it exists, using DirectoryInfo
    DirectoryInfo dir = new DirectoryInfo(Application.dataPath);
    FileInfo[] info = dir.GetFiles("*.*", SearchOption.AllDirectories);
    foreach (FileInfo f in info) // cycles through all the files
    {
        if(f.Name == scriptname)
        {
            //attaches to the gameobject (NOT WORKING)
            System.Type MyScriptType = System.Type.GetType(scriptname + ",Assembly-CSharp"); 
            obj.AddComponent(MyScriptType);
        }
    }
}
(当然,这是一个总结版本,我从脚本的不同部分复制了相关行)

但它不起作用。
有什么想法吗?

我建议这样做:

if(GUILayout.Button("Attach script"))
{
    // check if type is contained in your assembly:
    Type type = typeof(MeAssemblyType).Assembly.GetTypes().FirstOrDefault(t => t.Name == scriptname);
    if(type != null)
    {
        // script exists in the same assembly that MeAssemblyType is
        obj.AddComponent(type); // add the component
    }
    else
    { 
        // display some error message
    }
}
当然,如果您使用一些包含其他组件的插件(依赖项),这将失败,但要处理此问题,您只需检查程序集的依赖项:

typeof(MeAssemblyType) // your type from Assembly-CSharp 
    .Assembly // Assembly-CSharp assembly
    .GetReferencedAssemblies() // get referenced assemblies
    .FirstOrDefault(m => 
        m.Assembly // from this assembly
        .GetTypes() // get all types
        .FirstOrDefault(t => 
            t.Name == scriptname // select first one that matches the name
        )
    )
备注:

getReferenceAssembly
方法将仅返回程序集“使用”(加载)的程序集。为了清楚起见,假设您正在引用这些程序集:

  • Xml
  • 纽顿软件公司
  • 这段代码:

    static void Main()
    {
        XmlDocument doc = new XmlDocument();
        doc.LoadXml(<some_xml_input>);
    }
    
    这意味着它不会加载
    NewtonsoftJson
    ,因为它没有在该程序集中使用

    更好的建议:


    我建议您混合使用answer中的方法,但不要加载程序集,因为当Unity的编辑器开始您的项目时,已经加载了程序集。而是使用
    getReferenceAssemblys
    方法,从那里您应该调用
    GetTypes
    方法来检索该程序集中所有可能的类型。(这会很慢,但会保证您获得所需的结果)之后,您可以使用
    FirstOrDefault
    或自己迭代
    键入[]
    来找到所需的内容。

    这仍然是可能的。用这个

    UnityEngineInternal.APIUpdaterRuntimeServices.AddComponent(GameObject go, "", string componentName);
    

    希望这对我有所帮助经过广泛的实验后,我能得到这个。这也涵盖了所有Unity组件。只是把它作为一种扩展方法,让生活更轻松

    public static class ExtensionMethod
    {
        public static Component AddComponentExt(this GameObject obj, string scriptName)
        {
            Component cmpnt = null;
    
    
            for (int i = 0; i < 10; i++)
            {
                //If call is null, make another call
                cmpnt = _AddComponentExt(obj, scriptName, i);
    
                //Exit if we are successful
                if (cmpnt != null)
                {
                    break;
                }
            }
    
    
            //If still null then let user know an exception
            if (cmpnt == null)
            {
                Debug.LogError("Failed to Add Component");
                return null;
            }
            return cmpnt;
        }
    
        private static Component _AddComponentExt(GameObject obj, string className, int trials)
        {
            //Any script created by user(you)
            const string userMadeScript = "Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null";
            //Any script/component that comes with Unity such as "Rigidbody"
            const string builtInScript = "UnityEngine, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null";
    
            //Any script/component that comes with Unity such as "Image"
            const string builtInScriptUI = "UnityEngine.UI, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null";
    
            //Any script/component that comes with Unity such as "Networking"
            const string builtInScriptNetwork = "UnityEngine.Networking, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null";
    
            //Any script/component that comes with Unity such as "AnalyticsTracker"
            const string builtInScriptAnalytics = "UnityEngine.Analytics, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null";
    
            //Any script/component that comes with Unity such as "AnalyticsTracker"
            const string builtInScriptHoloLens = "UnityEngine.HoloLens, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null";
    
            Assembly asm = null;
    
            try
            {
                //Decide if to get user script or built-in component
                switch (trials)
                {
                    case 0:
    
                        asm = Assembly.Load(userMadeScript);
                        break;
    
                    case 1:
                        //Get UnityEngine.Component Typical component format
                        className = "UnityEngine." + className;
                        asm = Assembly.Load(builtInScript);
                        break;
                    case 2:
                        //Get UnityEngine.Component UI format
                        className = "UnityEngine.UI." + className;
                        asm = Assembly.Load(builtInScriptUI);
                        break;
    
                    case 3:
                        //Get UnityEngine.Component Video format
                        className = "UnityEngine.Video." + className;
                        asm = Assembly.Load(builtInScript);
                        break;
    
                    case 4:
                        //Get UnityEngine.Component Networking format
                        className = "UnityEngine.Networking." + className;
                        asm = Assembly.Load(builtInScriptNetwork);
                        break;
                    case 5:
                        //Get UnityEngine.Component Analytics format
                        className = "UnityEngine.Analytics." + className;
                        asm = Assembly.Load(builtInScriptAnalytics);
                        break;
    
                    case 6:
                        //Get UnityEngine.Component EventSystems format
                        className = "UnityEngine.EventSystems." + className;
                        asm = Assembly.Load(builtInScriptUI);
                        break;
    
                    case 7:
                        //Get UnityEngine.Component Audio format
                        className = "UnityEngine.Audio." + className;
                        asm = Assembly.Load(builtInScriptHoloLens);
                        break;
    
                    case 8:
                        //Get UnityEngine.Component SpatialMapping format
                        className = "UnityEngine.VR.WSA." + className;
                        asm = Assembly.Load(builtInScriptHoloLens);
                        break;
    
                    case 9:
                        //Get UnityEngine.Component AI format
                        className = "UnityEngine.AI." + className;
                        asm = Assembly.Load(builtInScript);
                        break;
                }
            }
            catch (Exception e)
            {
                //Debug.Log("Failed to Load Assembly" + e.Message);
            }
    
            //Return if Assembly is null
            if (asm == null)
            {
                return null;
            }
    
            //Get type then return if it is null
            Type type = asm.GetType(className);
            if (type == null)
                return null;
    
            //Finally Add component since nothing is null
            Component cmpnt = obj.AddComponent(type);
            return cmpnt;
        }
    }
    

    了解我是如何做到这一点很重要,这样您就可以在将来的任何Unity更新中添加对新组件的支持

    对于用户创建的任何脚本

    gameObject.AddComponentExt("YourScriptOrComponentName");
    
    1。找出需要加入的内容???在
    Assembly.Load
    功能中

    Assembly asm = Assembly.Load("???");
    
    Assembly asm = Assembly.Load("Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null");
    Type type = asm.GetType(???); 
    
    Assembly asm = Assembly.Load("???");
    
    Assembly asm = Assembly.Load("UnityEngine, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null");
    Type type = asm.GetType(???); 
    
    您可以在脚本中添加以下内容:

    Debug.Log("Info: " + this.GetType().Assembly);
    
    ParticleSystem pt = gameObject.AddComponent<ParticleSystem>();
    Debug.Log("Info11: " + pt.GetType().Assembly);
    
    ParticleSystem pt = gameObject.AddComponent<ParticleSystem>();
    Debug.Log("Info: " + pt.GetType());
    
    我得到:
    Assembly-CSharp,Version=0.0.0,Culture=neutral,PublicKeyToken=null

    我们现在应该替换???用这个

    Assembly asm = Assembly.Load("Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null");
    
    Assembly asm = Assembly.Load("UnityEngine, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null");
    
    2。找出需要加入的内容???在
    asm.GetType
    函数中

    Assembly asm = Assembly.Load("???");
    
    Assembly asm = Assembly.Load("Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null");
    Type type = asm.GetType(???); 
    
    Assembly asm = Assembly.Load("???");
    
    Assembly asm = Assembly.Load("UnityEngine, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null");
    Type type = asm.GetType(???); 
    
    在本例中,它只是要添加到游戏对象的脚本的名称

    假设脚本名为
    NathanScript

    Assembly asm = Assembly.Load("Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null");
    Type type = asm.GetType("NathanScript"); 
    gameObject.AddComponent(type);
    

    对于非用户创建的Unity内置脚本/组件脚本

    gameObject.AddComponentExt("YourScriptOrComponentName");
    
    例如,
    刚体
    线条渲染器
    图像
    组件。只是用户未创建的任何组件

    1。找出需要加入的内容???在
    Assembly.Load
    功能中

    Assembly asm = Assembly.Load("???");
    
    Assembly asm = Assembly.Load("Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null");
    Type type = asm.GetType(???); 
    
    Assembly asm = Assembly.Load("???");
    
    Assembly asm = Assembly.Load("UnityEngine, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null");
    Type type = asm.GetType(???); 
    
    您可以在脚本中添加以下内容:

    Debug.Log("Info: " + this.GetType().Assembly);
    
    ParticleSystem pt = gameObject.AddComponent<ParticleSystem>();
    Debug.Log("Info11: " + pt.GetType().Assembly);
    
    ParticleSystem pt = gameObject.AddComponent<ParticleSystem>();
    Debug.Log("Info: " + pt.GetType());
    
    2。找出需要加入的内容???在
    asm.GetType
    函数中

    Assembly asm = Assembly.Load("???");
    
    Assembly asm = Assembly.Load("Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null");
    Type type = asm.GetType(???); 
    
    Assembly asm = Assembly.Load("???");
    
    Assembly asm = Assembly.Load("UnityEngine, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null");
    Type type = asm.GetType(???); 
    
    您可以在脚本中添加以下内容:

    Debug.Log("Info: " + this.GetType().Assembly);
    
    ParticleSystem pt = gameObject.AddComponent<ParticleSystem>();
    Debug.Log("Info11: " + pt.GetType().Assembly);
    
    ParticleSystem pt = gameObject.AddComponent<ParticleSystem>();
    Debug.Log("Info: " + pt.GetType());
    
    假设要添加的组件是:


    将其组合到扩展方法中

    gameObject.AddComponentExt("YourScriptOrComponentName");
    

    如您所见,添加您创建的脚本和Unity附带的脚本/组件需要完全不同的过程。您可以通过检查类型if
    null
    来修复此问题。如果类型为
    null
    ,请执行其他步骤。如果另一个步骤也是
    null
    ,那么脚本只会不退出。

    反编译AddComponentUnity窗口。还添加了链接:

    然后对窗口进行如下调用:

    string typeString = "UnityEngine." + componentName;
    
      ws.winx.editor.windows.AddComponentWindow.Show(rect);
    
                ws.winx.editor.windows.AddComponentWindow.OnClose += OnCloseComponentSelectedFromPopUpMenu;
                ws.winx.editor.windows.AddComponentWindow.ComponentSelected += (menuPath) => ComponentSelectedFromPopUpMenu(positionData.Item1, menuPath);
    
    手柄返回

        private void ComponentSelectedFromPopUpMenu(Vector2 position, string menuPath) {
            
            
                        MonoScript monoScript;
            
                        char[] kPathSepChars = new char[]
                        {
                            '/',
                            '\\'
                        };
            
                        menuPath = menuPath.Replace(" ", "");
                        string[] pathElements = menuPath.Split(kPathSepChars);
            
                        string fileName = pathElements[pathElements.Length - 1].Replace(".cs", "");
            
            
            
            
                        if (pathElements[0] == "Assets") {
            
                            Debug.LogWarning("Unity need to compile new added file so can be included");
            
            
                        } else if (pathElements.Length == 2) {
            
    //use fileName
                            //do something
            
                            
                        } else if (pathElements[1] == "Scripts") {//Component/Scripts/MyScript.cs
                            
            
                            string[] guids = AssetDatabase.FindAssets("t:Script " + fileName.Replace(".cs", ""));
            
                            if (guids.Length > 0) {
            
                                for (int i = 0; i < guids.Length; i++) {
                                    monoScript = AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath(guids[i]), typeof(MonoScript)) as MonoScript;
                                    Type typet = monoScript.GetClass();
            
                                    if (typet == null) continue;
            
            
                                   
            
                        } else {//Component/Physics/Rigidbody
                            //try to find by type, cos probably Unity type
                            Type unityType = ReflectionUtility.GetType("UnityEngine." + fileName);
            
                            if (unityType != null) {
            
        //do something
            
                                return;
            
                            }
            
            
            
            
            
            //Based on attribute  [AddComponentMenu("Logic/MyComponent")] 
                            //Component/Logics/MyComponent
                            string[] guids = AssetDatabase.FindAssets("t:Script " + fileName.Replace(".cs", ""));
            
                            if (guids.Length > 0) {
            
                                for (int i = 0; i < guids.Length; i++) {
                                    monoScript = AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath(guids[i]), typeof(MonoScript)) as MonoScript;
                                    Type typet = monoScript.GetClass();
            
                                    if (typet == null) continue;
            
                                    object[] addComponentMenuAttributes = typet.GetCustomAttributes(typeof(AddComponentMenu), true);
            
            
            
                                    if (addComponentMenuAttributes != null && addComponentMenuAttributes.Length > 0 && "Component/" + ((AddComponentMenu)addComponentMenuAttributes[0]).componentMenu == menuPath)
                                    {
            
                                        //do somethings
            
                                    }
                                }
            
            
                            }
            
            
                        }
                    }
    
    private void component从弹出菜单中选择(矢量2位置,字符串菜单路径){
    单字单字;
    char[]kPathSepChars=新字符[]
    {
    '/',
    '\\'
    };
    menuPath=menuPath.Replace(“,”);
    string[]pathElements=menuPath.Split(kPathSepChars);
    字符串文件名=pathElements[pathElements.Length-1]。替换(“.cs”,”);
    if(路径元素[0]=“资产”){
    LogWarning(“Unity需要编译新添加的文件以便可以包含在内”);
    }else if(pathElements.Length==2){
    //使用文件名
    //做点什么
    }else if(pathElements[1]=“Scripts”){//Component/Scripts/MyScript.cs
    字符串[]guids=AssetDatabase.FindAssets(“t:Script”+fileName.Replace(“.cs”,”);
    如果(guids.Length>0){
    for(int i=0;i