Oh boy have I spent a lot of time on this problem...
What I'm trying to do is clone an object of type GameObject
that also contains a list of Component
. Cloning GameObject
types isn't any problem, but it seems this list is a problem as it is cloned but not its content; The content isn't copied but just stored.
As much as I would like to clone these components the same way as I did with the gameobjects, that's not possible because of my system.
Components are attached to a GameObject
and basically defines the GameObject
. For example, if I wanted to create a way for the use to control a GameObject
I would create a Component called something like PlayerController
that would derive from base type Component
, looking something like this:
class Component
{
//The gameobject this component is attached to
public GameObject gameObject { get; set;}
public virtual void Update()
{
//..
}
}
class PlayerController : Component
{
override Update()
{
Move();
//..
}
public void Move()
{
//...
}
}
The way I'm cloning GameObject is in a method like this:
public GameObject Clone(Vector2 position, float scale)
{
GameObject source = this;
GameObject clone = new GameObject();
//Get all properties of a GameObject
var srcProperties = TypeDescriptor.GetProperties(typeof(GameObject)).Cast<PropertyDescriptor>();
//Assign all properties from the source to the clone
foreach (var srcProperty in srcProperties)
{
srcProperty.SetValue(clone, srcProperty.GetValue(source));
}
//Clone all components from source and add them to the clone
if (source.components.Count > 0)
{
for (int i = source.components.Count - 1; i >= 0; i--)
{
var srcComp = source.components[i];
var compClone = srcComp.CloneComponent();
clone.components.Add(compClone);
}
}
clone.position = position;
clone.scale = scale;
AllGameObjects.Add(clone);
if (clone.components.Count > 0)
{
//Set the owner gameobjects and start the components
for (int i = clone.components.Count - 1; i >= 0; i--)
{
var comp = clone.components[i];
comp.gameObject = clone;
comp.Start();
}
}
return clone;
}
The CloneComponent()
method from the 17th line var compClone = srcComp.CloneComponent();
is a generic extension method that looks like this:
public static TComponent CloneComponent<TComponent>(this TComponent source) where TComponent : Component, new()
{
TComponent clone = new TComponent();
var srcProperties = TypeDescriptor.GetProperties(typeof(TComponent)).Cast<PropertyDescriptor>();
foreach (var srcProperty in srcProperties)
{
srcProperty.SetValue(clone, srcProperty.GetValue(source));
}
return clone;
}
This method works fine, if the source wasn't taken from a list of Component
as is seems to convert whatever type it was to the root type which it only does when being used as an argument. This obviously results in TComponent
being of type Component
and instead of cloning the source it just converts it to the root type and only clones properties from that.
What I'm asking for is for a way to bypass this, or for it to not convert to the root type when used as an argument?
Edit:
The type of the parameter source
will be of correct type, however TComponent
will always be of type Component
when source argument is given as an argument from a list. You might think I'm explaining this incorrect but I think it's just as weird as you do. The only thing I can think of is that it's a bug, which I think is unlikely.
Final edit:
Thank you so much @Philipp for the suggestion to use Activator.CreateInstance
. This is how I changed my CloneComponent()
method:
public static Component CloneComponent(this Component source)
{
var clone = Activator.CreateInstance(source.GetType());
var srcProperties = TypeDescriptor.GetProperties(source.GetType()).Cast<PropertyDescriptor>();
foreach (var srcProperty in srcProperties)
{
srcProperty.SetValue(clone, srcProperty.GetValue(source));
}
return (Component)clone;
}
source.GetType()
instead of typeof(TComponent)
should return the actual (inherited) type and you could create an Instance using Activator.CreateInstance()
. Also, you could always binary-serialize the entire tree and deserialize again, giving you a complete deep clone of the object graph.