Pages

Sunday, August 25, 2013

Unity3D + C#: Copy all the values from an existing component to another (derived) component, using Reflection

Happens to me all time. I have a prefab with a component such as "Player.cs" which contained over 30 variables of different types, including Transforms and GameObjects too. That it become such a hassle to configure all of them when I wanna make a new class which derived from "Player.cs"... say "Warrior.cs" with a structure like this:

public class Warrior : Player
{
}


For sure, the Warrior class will inherits everything from Player class, but that also means you have to reconfigure everything again (without a proper tricks), which is such a tedious work (though it can be done in a short time, if you don't have as many prefabs using the same script as me).
A good way of inheriting the variables from one component to another, is by using the Reset() method:

protected void Reset()
{
}


Similar to the OnEnable method which is called when an object is enabled, the OnReset method will be called either when you added the component containing the method, or hit the reset button on the component, which would usually empty all the variables in your script:


You can then alter the derived class's variables by referring it to the origin, for instance:

protected void Reset()
{
Player _player = GetComponent();
if (_player != null)
{

health = _player.health;
}
}


You can choose to do that and inherits all the values set in the original component's variables, one by one, and make your life a living hell (it's for me), or you can use Reflection.

System.Reflection

I've put together a little package for testing the Reflection method, you can download it here:

After exporting the package, try adding the component "Warrior" to the Player prefab, you'll see that all the variables in Warrior component is automatically filled with the one in the Player component:


To use Reflection, you need to first add "using System.Reflection;" at the top of the script, probably under "using UnityEngine;".

Then, inside a Reset method, try checking for a parent class component of the derived class, which will soon be added to a prefab (which has the parent class component):

protected void Reset()
{
// checking for Player class
Player _player = GetComponent();
if (_player != null) // if have Player class, do something...
{

}
}


Next, add the following lines within the "if (_player != null)" condition:

foreach (FieldInfo f in _player.GetType().GetFields())
{
f.SetValue(this, f.GetValue(_player));
}

What it does is, using the foreach loop, it check for each of the fields in the original Player component (acquired from the prefab), and set it to the Warrior component (this).

When all the fields (variables) in Warrior component is filled, you can now say bye bye to the Player component by removing it.

No comments: