- Home /
Find if any script on a GameObject has a specific variable?
I wish to clarify and amend this question.
If I am expecting to have a collision on a set layer,
there are many different types of object on that layer.
I know all of the objects will have a common variable on their script
that variables' value is unique to them
but it is the same variable name across all different types of script
Can (and if so how) do I access that variable to read and write to ?
Can I also find out that type (the name of the script), store it as a string, then use it as a reference
e.g.
go = hit.collider.gameObject;
goScript : String = [name of go script].ToString();
goScript.health -= 1;
SendMessage is fine for one-way operations, and creating functions of variable names, to deal with those variables, but there is no way to use something like return[info]; in the script that is the target of the SendMessage.
Thanks =]
Original Question
I am playing around making an RTS game, and it is surprising how well it is going with just giving each type of object (fighter, archer, building, ect) a behaviour, and let them run off and be happy.
However I have just hit a bump where adding different types of enemy building etc i have a steadily increasing switch-case for finding what type of enemy I am around and GetComponent of that enemy script to deduct health from.
it didn't click when I was doing some enemies, but when I added buildings I realized this is a pain, and I have to implement a universal health system.
my first idea is to have a health script that attached to everything, that simply SendMessage to its associated gameObject to deal damage, become dead, etc.
but I would like to know If i can do it without an extra script.
Can I Find if any script on a GameObject has a specific variable name?
to put it another way :
is there a way to search for a variable on a script (or all scripts as components of a GameObject) without knowing its typecast (script name)
and possibly from that can I find the type that script is, and use it later (like currentEnemyScript.health -= 1; ) ?
Just for explanation, this is what I'm starting to notice and wish to avoid :
each enemy has a different script for their behaviour of course (a building isn't a troop ... yet!) therefore a different script name, therefore a different TypeCast :
// check for opponents
// spherecast every 0.5 secs
// then ....
for (var object : Collider in objectsInRange)
{
switch( object.gameObject.name )
{
// Troops
case "Troops_0" :
// ... Do Stuff ... then
troopTarget = object.gameObject.transform;
break;
case "Miner_0" :
// ...
minerTarget = object.gameObject.transform;
break;
case "Builder" :
// ...
builderTarget = object.gameObject.transform;
break;
// Buildings
case "Barracks" :
// ...
barracksTarget = object.gameObject.transform;
break;
case "Refinery" :
// ...
refineryTarget = object.gameObject.transform;
break;
case "0" :
break;
}
}
// store reference to targeted troop transform and script
// then follow
if ( troopTarget != null )
{
// switch case for troop name
troop0script = troopTarget.GetComponent( "CharTroops0" ) as CharTroops0;
// then follow
targetFollowing = "Troops_0";
enemyCurrentState = enemy0state.Following;
currentTarget = troopTarget;
}
else if ( minerTarget != null )
{
// switch case for troop name
miner0script = minerTarget.GetComponent( "CharMiner" ) as CharMiner;
// then follow
targetFollowing = "Miner_0";
enemyCurrentState = enemy0state.Following;
currentTarget = minerTarget;
}
else if ( builderTarget != null )
{
etc
Edit :
My idea of a health class that is applied to all Objects that can be damaged
the health script :
// the main script finds that var and changes it at start
// e.g. if a fighter then health = 5; if an archer then health = 3; etc
var health : float = 5.0;
function JustBitTheDust()
{
// disable the troop gameObject
// add the troop back to the pool
}
on the troop :
// when finding a target
targetHealthScript = hit.collider.gameobject.GetComponent("GenericHealthScript") as GenericHealthScript;
// every cycle that the troop does damage
targetHealthScript.health -= 1;
if (health <= 0) targetHealthScript.JustBitTheDust();
You could do that using Reflection, but it would be super slow.
Thanks Fattie, I'm slowly getting this stuff via osmosis. I'm trying to make everything modular and universal. As I said, it would be quite easy with a universal health script (only one type there) and Send$$anonymous$$essage can do its thing.
I'd like to know the answer to this question to, as I was at that point not long ago and used a globalVars script to hold ALL the enemy health variables, and ray cast to check what enemy I was facing then call the relevant health variable from the globalVars script.
GRIFFO - there's nothing at all wrong with using a singleton (or some careful approach to essentially globals) in game engineering.
But the "send$$anonymous$$essage" concept is incredibly useful, once your program$$anonymous$$g comes along! Enjoy!
BTW you could make that code a little less verbose and a bit faster by doing:
someScript = someTarget.GetComponent(Char$$anonymous$$inerForExample);
The non-quoted version is faster and does not need to be cast with an as afterwards.
Answer by whydoidoit · Oct 06, 2012 at 08:51 AM
So there are three ways you could directly get variables and methods on a script without knowing its script name:
Reflection
Interfaces
Inheritance
So just to say it again - Reflection is slow. Reflection here would be very slow because you would need to interrogate all of the components on the target object looking for a particular method. In this case you might as well use SendMessage except for the fact you could get a return value - which obviously you want.
What you might be looking for is an Interface I think. Interfaces allow multiple objects to offer a well known behaviour but implement it in different ways. An interface definition is like a class definition except that there is no implementation. Then you actually implement the interface on each of your behaviours and you can treat them all the same. So, for example, things supporting an IAmAlive interface would all have health and be able to take damage, or heal. IRangedWeapon behaviours would all know how to aim and fire, IPickup behaviours would all be able to be displayed in an inventory and picked up etc. The benefit of interfaces is that a magic bow could for instance be alive, a ranged weapon and a pickup. This post talks about interfaces in UnityScript - it does mean you have to explicitly define your classes rather than let Unity do it all behind the scenes.
The other method is to use inheritance - in this case you are classifying objects - for example all living things inherit from LivingThing which has the variables and methods to affect health. Inheritance is more limited but you do not have to keep writing health methods. Often you will end up using both interfaces and inheritance! Make an interface to do something, then make a base class that implements that interface which most of your objects will be able to inherit from - special cases get their own version. Inheritance also requires that you explicitly define your classes and the base classes you write will need to inherit from MonoBehaviour.
Getting the interface on a component can be done with GetComponent.
Ok here's an example:
TestInterfaces.js
#pragma strict
var items : GameObject[];
function Start () {
}
function Update () {
for(var o in items)
{
Debug.Log(o.GetComponent(IAmAlive).Damage(4).ToString());
}
}
IAmAlive.js
#pragma strict
interface IAmAlive
{
function GetCurrentHealth() : float;
function Damage(amount : float) : float;
}
ThingWithHealth.js
#pragma strict
class ThingWithHealth extends $$anonymous$$onoBehaviour implements IAmAlive
{
var health : int;
function GetCurrentHealth() : float
{
return health;
}
function Damage(amount : float) : float
{
health -= amount;
return health;
}
}
OtherThingWithHealth.js
#pragma strict
class OtherThingWithHealth extends $$anonymous$$onoBehaviour implements IAmAlive
{
var health : float;
function GetCurrentHealth() : float
{
return health;
}
function Damage(amount : float) : float
{
health -= amount/2;
return health;
}
function Update()
{
health += Time.deltaTime;
}
}
In that example you can see how each of the two different classes that implement IAmAlive can be found in exactly the same way without knowing their script name, just the fact that they implement IAmAlive.
To complete the set here is an example of using inheritance and interfaces that works with the above test example:
LivingThings.js
#pragma strict
class LivingThing extends $$anonymous$$onoBehaviour implements IAmAlive
{
var health : int;
function GetCurrentHealth() : float
{
return health;
}
function Damage(amount : float) : float
{
health -= amount;
return health;
}
}
ThingWithHealth.js
#pragma strict
class ThingWithHealth extends LivingThing
{
var iHaveSomeOtherVariables : String;
function And$$anonymous$$ethods()
{
}
}
OtherThingWithHealth.js
class OtherThingWithHealth extends LivingThing
{
function Update()
{
health += Time.deltaTime * 5;
}
}
YetAnotherThingWithHealth.js
#pragma strict
class YetAnotherThingWithHealth extends $$anonymous$$onoBehaviour implements IAmAlive
{
var health : float;
function GetCurrentHealth() : float
{
return health;
}
function Damage(amount : float) : float
{
health -= health > 50 ? amount/2 : amount;
return health;
}
}
As you can see you can use both interfaces and inheritance to have objects provide their own implementations or just use a standard base class implementation.