- Home /
How to convert System.Type into an instance or access the class variables
First question ever! And as always there might be a very easy solution/trick I don't know about...
The details:
One weapon script A that handles releasing the projectiles
One weapon script B that handles the current avaible weapons
Numerous control scripts C1-C8 that regulate the behaviour of the projectiles (1 per weapon)
The issue:
In script A I'm in need of accessing the static variables before I instantiate the projectile (like rate of fire)
Script B delivers the currently used weapon prefab and its script TYPE C1-C8 (System.Type)
I don't know what script will be delived by the script B, so I have to keep script A independent
"missile" is the prefab and has a "MissileControl" script as component.
"gun" is another prefab and has a "GunControl" script as component.
// depending on weaponID script B returns either "GunControl" or "MissileControl" System.Type var projectileType : System.Type = ScriptB.getType(WeaponID); // same does script B for the prefab resource path var weaponPath : String = ScriptB.getPath(WeaponID);
// First attemp to get the rate of fire var instance : Transform = Instantiate(Resources.Load(weaponPath, Transform));
var rof : float = instance.GetComponent(projectileType).rof; // Error: rof is not a member of UnityEngine.Component // but rof is member of ALL C scripts, I promise. Therefore I have to convert this projectileType in a real component name (e.g. GunControl) to be accepted
// Second attemp, trying to create a script instance var instanceScript : projectileType = new projectileType(); // well, I know this had to fail. But I tried at least...
// Third attemp, rof = projectileType.rof; // both GunControl.rof and MissileControl.rof are static, so this shouldn't be a problem
The third solution would be my alltime favourite, but I don't know to create/access a class by it's type... simple problem it seems :(
I bet the solution can be found here, but I have no idea how to take advantage of it: http://msdn.microsoft.com/library/42892f65
He could, but it's not terribly scalable I$$anonymous$$HO, you can end up with multiple places you need a switch statement: everywhere you need to deal with a weapon (or its projectile).
By leveraging an OOP design, you can more easily create types that have common functionality but with specific implementations. Your base class type becomes you means of switching.
If I consider having a switch statement for my object clicking scenario that we have in our game (see below), then I would have functionality for many different kinds of object in one place. It would be very messy.
The above problem is one that has been solved with polymorphism for some time name.
I can also re-use the constructs from project to project.
Unfortunately, the usage of Invoke()
is not something I would rank highly in my book of things to use given its high processing requirement. If you have any speed concerns, I would definitely give that a miss. I just felt like placing that out there, but there is definitely an easier way.
The mentioned switch statement is already part of script B to turn my weapon enum into my weapon System.Type
Would be a nasty thing to call that block evertime I need a single variable :O
if each script will have the same variable types you can use Polymorphism as I've described, if not, you will need the switch statement on the type anyway to know what methods and properties you can call.
If you do this without polymorphism, you're going to struggle - creating an arbitrary type is one thing, being able to use it is a whole new ballgame.
Answer by Bovine · Jul 25, 2012 at 08:36 AM
I haven't tested it, but you might do this like so I imagine:
Type type = scriptB.GetType();
ConstructorInfo constructor = type.GetConstructor(Type.EmptyTypes);
dynamic new_weapon = constructor.Invoke(null);
However, I don't think dynamic will be supported in the current .Net version for Unity and I suspect you'll need it given that you don't know the type of the script, then you cannot reliably call methods on it.
What I think you will need is to have a base class for weapons support, i.e.:
public class MyWeaponBase : MonoBehaviour
{
void FireWeapon(GameObject someContext);
}
And then you would be able to do:
Type type = scriptB.GetType();
ConstructorInfo constructor = type.GetConstructor(Type.EmptyTypes);
MonoBehaviour new_weapon = constructor.Invoke(null);
new_weapon.FireWeapon(this.gameObject);
However, what I suggest you might want to do is have weapon types in a similar hierarchy and when you spawn a weapon, you do so from a list of weapon types available, creating an instance of a prefab with a weapon script on it that supports MonoBehaviour.
Then you should be able to do:
MyWeaponBase weapon = MyPrefab.GetComponent<MyWeaponBase>();
weapon.FireWeapon(this.gameObject);
I use this model for items in the world we can click and hence we have the following code:
void RayHit(POINTER_INFO ptrInfo)
{ // we only care about change!
if(ptrInfo.evt == POINTER_INFO.INPUT_EVENT.NO_CHANGE) return;
if(ptrInfo.evt == POINTER_INFO.INPUT_EVENT.TAP)
{
GameObject object_hit = ptrInfo.hitInfo.collider.gameObject;
Assert.IsNotNull(object_hit);
BVInteractiveObject interactive = object_hit.GetComponent<BVInteractiveObject>();
if(interactive != null && interactive.ClickDistance >= ptrInfo.hitInfo.distance) interactive.Clicked();
}
}
We use EZGUI hence the hook onto its ray casting.
Hope that helps.
This looks a little hard to understand even though I notice that it actually looks like what I was thinking of (besides the switch statement).
You're basically creating a class, $$anonymous$$yWeaponBase
, that all weapons will inherit from, then you put an abstract method into them (`FireWeapon()`) and use that to decide between the types? Or am I reading this wrong?
Yes, so ins$$anonymous$$d of a switch statement anywhere you need to DoThing() with a weapon, you would:
$$anonymous$$yWeaponBase weapon = GetComponent<$$anonymous$$yWeaponBase>();
weapon.DoThing();
weapon.DoSomeOtherThing();
int damage = weapon.CalculateDamage();
And then the implementation for that weapon type lives within the script for that weapon and not scattered around your codebase.
This doesn't even help with static variables. They don't belong to the instance, they belong to the class itself. You would have to use reflection to get the value of a static variable from a System.Type reference.
However i wouldn't recommend reflection for such a cases. A base class is the way to go and avoid static variables, they are the evil part of OOP ;)
True, sorry, implicit suggestion - I wouldn't recommend static variables for this sort of thing :o)
You suggest the same "base" script for all weapons to have fix reference for all of them. Good approach if all objects should behave the same (in your example it is the object interactive state).
But sooner or later I have to deter$$anonymous$$ate which stats to load, where they are stored and how the weapon should behave. So it can't be avoided to preload the specific weapon details.
Your contructor solution is very much in the direction I was looking to. I wish there'd be the following function: Type.getValue(Type) that returns the type and not the type holding variable. Or does this work with Type.getType()? Will test this afternoon and report then!
Answer by Jaimi · Jul 25, 2012 at 12:33 PM
Normally, You use Activator.CreateInstance:
System.Activator.CreateInstance(type);
http://msdn.microsoft.com/en-us/library/system.activator.createinstance.aspx
Already tried that earlier D:
var instance = System.Activator.CreateInstance(projectileType);
var rof : float = instance.RateOfFire;
-> Error: 'RateOfFire' is not a member of 'Object'.
That's because you don't know the type you would need dynamic, which won't be supported
And if your weapons are all different, you need to $$anonymous$$NOW that RateOfFire is a member of your type... switch statement or polymorphism am afraid
I guess interfaces are new into Unity Script - obviously work find in C# - so there might have been a problem with implementing properties I guess - you should definitely log it as a bug if it is reproduceable though.
Reflection is SLOW, GetValue causes a JIT on iOS that kills it (though there are workarounds). If your game design requires reflection like this then there's a red flag up about something being wrong.
It strikes me that you want external scripts to know things like rate of fire etc - the object oriented way is to hide that detail in the object and just call CanFire or even just Fire and let it work itself out. The moment you start pulling that stuff out elsewhere you are breaking encapsulation and you will find that everything is fighting against you.
Answer by Nanity · Jul 26, 2012 at 09:39 AM
Yeha, polymorphism works like a charm!
Here's the important code! Thanks to all and maybe it will save some others much trouble:
// player component class weaponHandler { function createWeapon() { var weapon : Transfrom = Instantiate(Resources.Load(prefabPath, Transform)); var script : WeaponControl = weapon.GetComponent(WeaponControl); script.setOwner = transform.root; } }
// the basic script, not attached to any weapon class WeaponControl extends MonoBehaviour { var owner : Transform; var RateOfFire : float;
function Start () {
setStats();
}
function setStats() {}
}
// one missile script, attached to the missile prefab class MissileControl extends WeaponControl { function setStats() { RateOfFire = 0.5; }
function Fire() {
var missileShot : Transform = Instantiate(Resources.Load("MissileShot", Transform));
var mSS : MissileShotScript = weapon.GetComponent(MissileShotScript);
mSS.setOwner = owner;
}
}
// the missileShot script, attached to the missileShot prefab class MissileShotControl extends MonoBehaviour { var owner : Transform; private var speed : float; private var _body : Rigidbody;
function Start() {
_body = GetComponent(Rigidbody);
_body.velocity = transform.forward * speed * Time.deltaTime;
}
}