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

Nat*_*ann 3 c# unity-game-engine unity5

我目前正在为 Unity 编辑器(自定义检查器和自定义窗口)制作一个系统,该系统将自动化并使我们正在制作的游戏的美术师的工作更容易,但我遇到了障碍。

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

这是我尝试做的:

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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

(当然,这是一个总结版,我从脚本的不同部分复制了相关的行)。

但它不起作用。有任何想法吗?

Pro*_*mer 6

经过广泛的实验,我得到了这个。这也涵盖了所有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;
    }
}
Run Code Online (Sandbox Code Playgroud)

用法

gameObject.AddComponentExt("YourScriptOrComponentName");
Run Code Online (Sandbox Code Playgroud)

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

对于用户创建的任何脚本

1.找出需要在??? Assembly.Load函数中。

Assembly asm = Assembly.Load("???");
Run Code Online (Sandbox Code Playgroud)

你可以通过把它放在你的脚本中来做到这一点:

Debug.Log("Info: " + this.GetType().Assembly);
Run Code Online (Sandbox Code Playgroud)

我有: Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null

我们现在应该更换???接着就,随即。

Assembly asm = Assembly.Load("Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null");
Run Code Online (Sandbox Code Playgroud)

2.找出需要在??? asm.GetType函数中。

Assembly asm = Assembly.Load("Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null");
Type type = asm.GetType(???); 
Run Code Online (Sandbox Code Playgroud)

在这种情况下,它只是您要添加到游戏对象的脚本的名称。

假设您的脚本名称是NathanScript

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

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

这方面的例子是Rigidbody, Linerenderer,Image组件。只是任何不是由用户创建的组件。

1.找出需要在??? Assembly.Load函数中。

Assembly asm = Assembly.Load("???");
Run Code Online (Sandbox Code Playgroud)

你可以通过把它放在你的脚本中来做到这一点:

ParticleSystem pt = gameObject.AddComponent<ParticleSystem>();
Debug.Log("Info11: " + pt.GetType().Assembly);
Run Code Online (Sandbox Code Playgroud)

我有: UnityEngine, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null

我们现在应该更换???接着就,随即。

Assembly asm = Assembly.Load("UnityEngine, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null");
Run Code Online (Sandbox Code Playgroud)

2.找出需要在??? asm.GetType函数中。

Assembly asm = Assembly.Load("UnityEngine, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null");
Type type = asm.GetType(???); 
Run Code Online (Sandbox Code Playgroud)

你可以通过把它放在你的脚本中来做到这一点:

ParticleSystem pt = gameObject.AddComponent<ParticleSystem>();
Debug.Log("Info: " + pt.GetType());
Run Code Online (Sandbox Code Playgroud)

我有: UnityEngine.ParticleSystem

请记住,ParticleSystem这里用作示例。因此,将进入该asm.GetType函数的最终字符串将按如下方式计算:

string typeString = "UnityEngine." + componentName;
Run Code Online (Sandbox Code Playgroud)

假设您要添加的组件是LineRenderer

Assembly asm = Assembly.Load("UnityEngine, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null");
string typeString = "UnityEngine." + "LineRenderer";
Type type = asm.GetType(typeString); 
gameObject.AddComponent(type);
Run Code Online (Sandbox Code Playgroud)

将它放在一个扩展方法中

如您所见,添加您创建的脚本和 Unity 附带的脚本/组件需要完全不同的过程。您可以通过检查类型 if 来解决此问题null。如果类型为null,则执行其他步骤。如果其他步骤是null太那么脚本根本就没有退出。