Generating a script, then using AddComponent to attach it to a GameObject

Hi all,
I am the author of GoogleFu, and I’m attempting to update a deprecated call that worked in 4.x

First, I’ll explain what I’m doing. GoogleFu is an editor utility that downloads, parses, and generates custom classes based on Google Spreadsheets. Then it attaches those custom classes to a Game Object and adds the data from the spreadsheet to the GameObject via the custom class.

For instance if I have a spreadsheet named Demeanor, it has 2 rows of data in it with the first column being a string and the second being an int:

Demeanor | Name | Val
TYPE         | string   | int
ROW_0      | Happy | 0
ROW_1      | Sad      | 1

Then GoogleFu will generate a Demeanor.cs that contains:

public class DemeanorRow {
public string Name;
public int Val;
public DemeanorRow( string inName, int inVal)
{
    Name = inName;
    Val = inVal;
}

public class Demeanor
{
List<DemeanorRow> Rows;
}

(of course this is extremely simplified, but shows a basic example of why I need this)

GoogleFu writes the Demeanor.cs to a Resources folder, and refreshes the AssetDatabase:

AssetDatabase.ImportAsset(dbInfo.ScriptName + ".cs", ImportAssetOptions.ForceSynchronousImport | ImportAssetOptions.ForceUpdate);
                    AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate);

Where dbInfo.ScriptName is “Demeanor”

In 4.x I would simply create a new GameObject and attach the freshly compiled script with

var component = go.AddComponent(dbInfo.ScriptName);

Once the new component has been attached, I could create new DemeanorRows and add them to the Demeanor.Rows variable.

In 5.x I can no longer use the dbInfo.ScriptName string. So I tried this:

var component = go.AddComponent(Type.GetType(dbInfo.ScriptName));

However I get this message:
AddComponent asking for invalid type

I’m also getting ‘Demeanor.cs’ does not exist when I call

AssetDatabase.ImportAsset(dbInfo.ScriptName + ".cs", ImportAssetOptions.ForceSynchronousImport | ImportAssetOptions.ForceUpdate);

Demeanor.cs is generated, and does exist at:
Assets\GoogleFuGen\ObjDB\Resources\Demeanor\Demeanor.cs

I’m sure this is a combination of issues, as this seems pretty elementary but I’m at a bit of a loss here. Any help would be appreciated.

Do your scripts derive from UnityEngine.Component? That may be a requirement for the type.

They derive from MonoBehaviour.

In the past, Unity 4.x I’ve had to use the following code for getting type. I found the usual GetType just never worked properly.

public static Type GetAssemblyType(string typeName) {
    var type = Type.GetType(typeName);
    if (type != null) return type;
    foreach (var a in AppDomain.CurrentDomain.GetAssemblies()) {
        type = a.GetType(typeName);
        if (type != null) return type;
    }
    return null;
}

It may or may not help you since you’re loading things in dynamically from the looks of it. It’s helped me in the past at least.

Using both the vanilla GetType and CWolf’s GetAssemblyType wrapper function, I still get NULL no matter how many times the assembly has been reloaded, or how many calls to ImportAsset I try to make.

The file is there, I see it show up in the project view which means the AssetDatabase has been successfully reloaded. But I still can’t seem to get a good Type out of it.

Here is the entire snippet of code:

_ObjDbExport is List that describes all of the scripts I need to process

            if (_ObjDbExport != null && _ObjDbExport.Count > 0)
            {

                var dbInfo = _ObjDbExport[0];

                if ((DateTime.Now - dbInfo.LastAttempt).TotalSeconds < 2)
                    return;

                if (dbInfo.ReloadedAssets == false)
                {
                    AssetDatabase.Refresh( ImportAssetOptions.ForceUpdate);
                    dbInfo.ReloadedAssets = true;
                    return;
                }

                dbInfo.LastAttempt = DateTime.Now;

                Component comp = null;
                var myAssetPath = string.Empty;


                Debug.Log("Looking for Database Script: " + dbInfo.ScriptName);
                var findAssetArray = AssetDatabase.FindAssets(dbInfo.ScriptName);
                if (findAssetArray.Length > 0)
                {
                    foreach (var s in findAssetArray)
                    {
                        var mypath = AssetDatabase.GUIDToAssetPath(s);

                        if (mypath.EndsWith(".cs"))
                        {
                            myAssetPath = mypath;
                            Debug.Log("Found Database Script at: " + mypath);
                        }
                    }

                   //var myType = Type.GetType(dbInfo.ScriptName);
                    var myType = GetAssemblyType(dbInfo.ScriptName);
                    Debug.Log(dbInfo.ScriptName + ": GetAssemblyType returns " + myType);
                    if (myType != null)
                    {
                        var go = GameObject.Find(dbInfo.ObjectName);
                        if (go == null)
                        {
                            go = new GameObject(dbInfo.ObjectName);
                        }


                        var toDestroy = go.GetComponent(dbInfo.ScriptName);
                        if (toDestroy != null)
                            DestroyImmediate(toDestroy);

                        comp = go.AddComponent(myType);
                    }
                }

                if (comp == null)
                {
                    if (!string.IsNullOrEmpty(myAssetPath))
                    {
                        Debug.Log("Attempting to compile: " + myAssetPath);
                        AssetDatabase.ImportAsset(myAssetPath,
                            ImportAssetOptions.ForceSynchronousImport | ImportAssetOptions.ForceUpdate);
                    }

                    if (_ImportTryCount < 5)
                    {
                        _ImportTryCount++;
                        return;
                    }
                    Debug.LogError("Could not add GoogleFu component base " + dbInfo.ScriptName);
                    _ObjDbExport.Clear();
                    _ImportTryCount = 0;
                    return;
                }

                _ImportTryCount = 0;
                Debug.Log("Database Script Attached!");
                _ObjDbExport.Remove(dbInfo);
            }

Hmm… from my side your code looks ok but I haven’t done a lot of AssetDatabase work for a while.

Just a thought. You say you import with…

AssetDatabase.ImportAsset(dbInfo.ScriptName + ".cs", ImportAssetOptions.ForceSynchronousImport | ImportAssetOptions.ForceUpdate);

but the location is…

Assets\GoogleFuGen\ObjDB\Resources\Demeanor\Demeanor.cs

Is the ‘dbInfo.ScriptName’ a path or the actual name? If it’s the name then I presume you need to change it so it’s the path. Since AssetDatabase.ImportAsset needs path.

I noticed that earlier, unfortunately I’ve since updated it and it still fails :frowning:
Line 62 of my last post where myAssetPath is the full path relative to /assets

I am getting this error now when I call ImportAsset:

Reload Assembly called from managed code directly. This will cause a crash. You should never refresh assets in synchronous mode or enter playmode synchronously from script code.
UnityEditor.AssetDatabase:ImportAsset(String, ImportAssetOptions)

I’m calling ImportAsset from an Update function. If I am not supposed to call it from Managed Code directly, how am I supposed to be calling it? Regardless it does pop up a “Compiling Scripts” box and doesn’t crash like it warns, but obviously that isn’t right…

Got it!

var myType = Type.GetType(dbInfo.ScriptName);

was always returning NULL, because the class I was looking for is wrapped in a namespace. It’s the same namespace the rest of the code is in, but as soon as I included the namespace in the GetType lookup it started finding a valid Type, and everything else is gravy.

Thanks for all of the help!!

2 Likes

Excellent! Not sure I was much help but great job =).