- Home /
GO duplication vs. custom class instances
I add an empty GameObject then my custom component. In the component several instances of other custom classes can be stored, created, deleted. Everything is serialized, properly saved and loaded with the scene.
However when I duplicate the Game Object manually in the editor there is a problem: the GO is new but the component inside is referencing to stuff from the original GO. What I'd like to have is unique instances of everything inside the GO.
What is the proper way of handling this?
The actual system is somewhat complicated but can be boiled down to the following example:
I have GameObject "Pot" and I add component "Plant" to it. That component can create and store instances of the custom class "Berry" (public, serialized, extends C#'s root class). Berry creation is not possible through the inspector.
If I duplicate "Pot" using the editor context menu then I end up with "Pot(2)" containing "Plant(2)" while "Berry" is shared between the two plants. My goal is to have "Berry(2)", a unique copy of the original.
EDIT: Added example. EDIT2: Made it more apparent that duplication is done by the Unity Editor, not from script.
Sorry I couldn't help, but voted +1 for a good question. I just haven't done anything w/ custom editor for so long :/
Answer by FL · May 25, 2013 at 11:42 PM
Use the C#'s interface ICloneable and use Clone()
. Reference.
You mean Clone() should be called by the Unity Editor when it duplicates a GO, component and other contents? $$anonymous$$y tests show that even if Berry implement ICloneable, Clone() is not actually called, but I might be missing something.
No. You need to call Clone()
.
First, try to implement this interface in both Plant and Berry (in your example) and manually add the script to second Pot using plantClone = Plant.Clone();
and, after call secondPot.AddComponent(plantClone);
If this didn't works, you can also implement the interface only in the Berry, and after duplicate the object, use secondPlant = firstPlant.Clone();
.
Just to be clear, I'm talking about duplication of a GO manually in the editor (CTRL+D, Edit/Duplicate). I don't see where and when I'm supposed to call Clone().
Oh! Sorry! I misunderstand this part. You probably need to edit/make some Editor scripts to use the Clone()
method but I have no experience in this part.
As far as I know, things work pretty much the same way as regular scripting if you just use ExecuteInEdit$$anonymous$$ode... I could very well be wrong, but check it out, it may at least point you in the right direction :)
Answer by ZoltanErdokovy · Jun 01, 2013 at 09:12 AM
My example was wrong above, here is how things seem to be working:
If 'Berry' extends c#'s root class (i.e. extends nothing) then it does get duplicated properly.
If 'Berry' extends ScriptableObject then the instance is shared. In other words the references are duplicated not the instance they are pointing to. This behavior is actually a feature as explained in this excellent post. (Although I'm still somewhat confused how serialization and ctrl+d relate exactly.)
- For other reasons I needed 'Berry' to be a ScriptableObject so I had to detect when a GO got duplicated from the editor UI and do deep copies and reference fixes throughout the cloned component:
The component in question has [ExecuteInEditMode] so it's Update() is executed every time the scene changes, such as on duplication.
I store the result of GetInstanceID() in a separate variable, initialized on creation of the component and on Awake() to handle scene loads. In Update() the cached value is compared to the current GetInstanceID() and if they differ then we are a duplicate.
The UNITY_EDITOR define ensures that this comparison is only done while in the editor.
While performing the deep copies on the contents of the component I build a remapping dictionary containing oldInstance - newInstance pairs. I use that to restore inter-component referencing. (So nothing keeps pointing to the original component's contents but rather to the similar part of the newly created component with the now unique instances.)
It's not the most elegant solution but works.