Search Unity

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

Discussion in 'Unity 5 Pre-order Beta' started by Shane-C, Feb 21, 2015.

  1. Shane-C

    Shane-C

    Joined:
    Nov 9, 2012
    Posts:
    211
    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:
    Code (CSharp):
    1. Demeanor | Name | Val
    2. TYPE         | string   | int
    3. ROW_0      | Happy | 0
    4. ROW_1      | Sad      | 1
    Then GoogleFu will generate a Demeanor.cs that contains:
    Code (CSharp):
    1. public class DemeanorRow {
    2. public string Name;
    3. public int Val;
    4. public DemeanorRow( string inName, int inVal)
    5. {
    6.     Name = inName;
    7.     Val = inVal;
    8. }
    9.  
    10. public class Demeanor
    11. {
    12. List<DemeanorRow> Rows;
    13. }
    14.  
    (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:

    Code (CSharp):
    1. AssetDatabase.ImportAsset(dbInfo.ScriptName + ".cs", ImportAssetOptions.ForceSynchronousImport | ImportAssetOptions.ForceUpdate);
    2.                     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
    Code (CSharp):
    1. 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:
    Code (CSharp):
    1. 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
    Code (CSharp):
    1. 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.
     
  2. seattlebluesky

    seattlebluesky

    Joined:
    Sep 2, 2014
    Posts:
    170
    Do your scripts derive from UnityEngine.Component? That may be a requirement for the type.
     
  3. Shane-C

    Shane-C

    Joined:
    Nov 9, 2012
    Posts:
    211
    They derive from MonoBehaviour.
     
  4. CWolf

    CWolf

    Joined:
    Oct 24, 2011
    Posts:
    106
    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.

    Code (CSharp):
    1.  
    2. public static Type GetAssemblyType(string typeName) {
    3.     var type = Type.GetType(typeName);
    4.     if (type != null) return type;
    5.     foreach (var a in AppDomain.CurrentDomain.GetAssemblies()) {
    6.         type = a.GetType(typeName);
    7.         if (type != null) return type;
    8.     }
    9.     return null;
    10. }
    11.  
    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.
     
  5. Shane-C

    Shane-C

    Joined:
    Nov 9, 2012
    Posts:
    211
    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<ExportInfo> that describes all of the scripts I need to process


    Code (CSharp):
    1.             if (_ObjDbExport != null && _ObjDbExport.Count > 0)
    2.             {
    3.  
    4.                 var dbInfo = _ObjDbExport[0];
    5.  
    6.                 if ((DateTime.Now - dbInfo.LastAttempt).TotalSeconds < 2)
    7.                     return;
    8.  
    9.                 if (dbInfo.ReloadedAssets == false)
    10.                 {
    11.                     AssetDatabase.Refresh( ImportAssetOptions.ForceUpdate);
    12.                     dbInfo.ReloadedAssets = true;
    13.                     return;
    14.                 }
    15.  
    16.                 dbInfo.LastAttempt = DateTime.Now;
    17.  
    18.                 Component comp = null;
    19.                 var myAssetPath = string.Empty;
    20.  
    21.  
    22.                 Debug.Log("Looking for Database Script: " + dbInfo.ScriptName);
    23.                 var findAssetArray = AssetDatabase.FindAssets(dbInfo.ScriptName);
    24.                 if (findAssetArray.Length > 0)
    25.                 {
    26.                     foreach (var s in findAssetArray)
    27.                     {
    28.                         var mypath = AssetDatabase.GUIDToAssetPath(s);
    29.  
    30.                         if (mypath.EndsWith(".cs"))
    31.                         {
    32.                             myAssetPath = mypath;
    33.                             Debug.Log("Found Database Script at: " + mypath);
    34.                         }
    35.                     }
    36.  
    37.                    //var myType = Type.GetType(dbInfo.ScriptName);
    38.                     var myType = GetAssemblyType(dbInfo.ScriptName);
    39.                     Debug.Log(dbInfo.ScriptName + ": GetAssemblyType returns " + myType);
    40.                     if (myType != null)
    41.                     {
    42.                         var go = GameObject.Find(dbInfo.ObjectName);
    43.                         if (go == null)
    44.                         {
    45.                             go = new GameObject(dbInfo.ObjectName);
    46.                         }
    47.  
    48.  
    49.                         var toDestroy = go.GetComponent(dbInfo.ScriptName);
    50.                         if (toDestroy != null)
    51.                             DestroyImmediate(toDestroy);
    52.  
    53.                         comp = go.AddComponent(myType);
    54.                     }
    55.                 }
    56.  
    57.                 if (comp == null)
    58.                 {
    59.                     if (!string.IsNullOrEmpty(myAssetPath))
    60.                     {
    61.                         Debug.Log("Attempting to compile: " + myAssetPath);
    62.                         AssetDatabase.ImportAsset(myAssetPath,
    63.                             ImportAssetOptions.ForceSynchronousImport | ImportAssetOptions.ForceUpdate);
    64.                     }
    65.  
    66.                     if (_ImportTryCount < 5)
    67.                     {
    68.                         _ImportTryCount++;
    69.                         return;
    70.                     }
    71.                     Debug.LogError("Could not add GoogleFu component base " + dbInfo.ScriptName);
    72.                     _ObjDbExport.Clear();
    73.                     _ImportTryCount = 0;
    74.                     return;
    75.                 }
    76.  
    77.                 _ImportTryCount = 0;
    78.                 Debug.Log("Database Script Attached!");
    79.                 _ObjDbExport.Remove(dbInfo);
    80.             }
     
  6. CWolf

    CWolf

    Joined:
    Oct 24, 2011
    Posts:
    106
    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...

    Code (CSharp):
    1. AssetDatabase.ImportAsset(dbInfo.ScriptName + ".cs", ImportAssetOptions.ForceSynchronousImport | ImportAssetOptions.ForceUpdate);
    but the location is...

    Code (CSharp):
    1. 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.
     
  7. Shane-C

    Shane-C

    Joined:
    Nov 9, 2012
    Posts:
    211
    I noticed that earlier, unfortunately I've since updated it and it still fails :(
    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..
     
  8. Shane-C

    Shane-C

    Joined:
    Nov 9, 2012
    Posts:
    211
    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!!
     
    imphenzia and CWolf like this.
  9. CWolf

    CWolf

    Joined:
    Oct 24, 2011
    Posts:
    106
    Excellent! Not sure I was much help but great job =).
     
unityunity