Search Unity

How to replace one derived class with another without losing references?

Discussion in 'Scripting' started by f5fairy, Apr 19, 2021.

  1. f5fairy

    f5fairy

    Joined:
    Nov 21, 2012
    Posts:
    10
    For example, there is class "Item" derived from ScriptableObject, and classes like "Consumable", "Wearable", "Weapon", "Tool" derived from "Item". There is EditorWindow to create items, save them as assets and edit them. List<Item> with references to assets is in MonoBehaviour script.

    I'd like an option to easily change item type in EditorWindow (like from "weapon" to "tool") without losing reference to it. But if I understand correctly, I can't just change class of object. I create instance of "Tool", copy concurrent data from existing "Weapon" and then save new tool in asset where weapon was. Something like this
    Code (CSharp):
    1.     public void Convert<T>(SerializedObject source)
    2.     {
    3.         var serialized = JsonConvert.SerializeObject(source.targetObject);
    4.         var result = JsonConvert.DeserializeObject<T>(serialized);
    5.         AssetDatabase.CreateAsset(result as Object, AssetDatabase.GetAssetPath(source.targetObject));
    6.         AssetDatabase.SaveAssets();
    7.     }
    This way references stick to the old object which shouldn't exist anymore. So, how to make it correctly?
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,742
    In code I suppose you could use reflection to identify fields to copy over.

    In editor time, one handy way is to find the GUID for a Weapon.cs object and the GUID for Tool.cs object (which are both derivatives of your Item.cs object) and then hand-edit the .asset file for the item to change it.

    The GUID for a script is located in the .meta file for the C# file

    The GUID specifying the C# object for an asset is in the actual .asset file.

    In both cases they can be opened with your favorite text editor.
     
  3. f5fairy

    f5fairy

    Joined:
    Nov 21, 2012
    Posts:
    10
    it didn't occur to me that there are 2 GUIDs. AssetDatabase.AssetPathToGUID gets guid from *meta, which doesn't change. And if I copy guid from asset before replacing object, then paste it after, it doesn't help.
     
    Last edited: Apr 19, 2021
  4. f5fairy

    f5fairy

    Joined:
    Nov 21, 2012
    Posts:
    10
    As I wrote, I want to edit list of all items in EditorWindow, i. e. change item type with a simple click on EnumPopup. Unfortunately to change type of item means to change derived class too. I'd better not use them at all, but it's too late to revert.
    Anyway, I edited original function to keep guid in asset. References still stick to the old object.
    Code (CSharp):
    1.  
    2.     public void Convert<T>(SerializedObject source)
    3.  
    4.     {
    5.  
    6.         var serialized = JsonConvert.SerializeObject(source.targetObject);
    7.  
    8.         T result = JsonConvert.DeserializeObject<T>(serialized);
    9.  
    10.         var path = AssetDatabase.GetAssetPath(source.targetObject);
    11.  
    12.         string s = File.ReadAllText(@Path);
    13.  
    14.         var guidPos = s.IndexOf("guid:") + 6;
    15.  
    16.         var guid = s.Substring(guidPos, 32);
    17.  
    18.         AssetDatabase.CreateAsset(result as Object, path);
    19.  
    20.         AssetDatabase.SaveAssets();
    21.  
    22.         s = File.ReadAllText(@Path);
    23.  
    24.         guidPos = s.IndexOf("guid:") + 6;
    25.  
    26.         var newguid = s.Substring(guidPos, 32);
    27.  
    28.         File.WriteAllText(@Path, s.Replace(newguid, guid));
    29.  
    30.     }
     
  5. f5fairy

    f5fairy

    Joined:
    Nov 21, 2012
    Posts:
    10
    I found solution. INSANELY easy!
    Code (CSharp):
    1.     public void Cast<T>(SerializedObject source)
    2.     {
    3.         foreach (var script in Resources.FindObjectsOfTypeAll<MonoScript>())
    4.         {
    5.             if (script.GetClass() == typeof(T))
    6.             {
    7.                 source.FindProperty("m_Script").objectReferenceValue = script;
    8.                 break;
    9.             }
    10.         }
    11.         source.ApplyModifiedProperties();
    12.     }
    It's a shame that it took so much time to find and noone pointed me to it. Maybe my question was not clear enough?
     
    chelnok and Kurt-Dekker like this.
  6. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,742
    I had no idea it could be so easy. Thanks for posting your handy codelet above; into my area of snippets it goes!