Passing a value from one object to another

Can anyone tell me how I can pass a value from one object to another?

In my game I instantiate projectile objects in front of the player and set these moving at a good speed. Whenever the projectile objects hit something they destroy themselves and instantiate an explosion object. In the script attached to the projectiles I have the following variable:

var projectileDamage = 1;

I’d like to be able to pass this value to the instantiated explosion object, so that the script attached to it can use the value. In pseudo-code what I need in the explosion script is:

var damage = projectileObject.projectileDamage;

I’ve looked in the documentation, but without examples it makes my head spin :frowning:

Have you looked in the new online docs? There’s a load of examples there.

The docs are for 1.1, but I very little (if anything) has been added in that part of scripting

I’ve been using the online docs. I’m afraid it isn’t helping me at the moment. I only have time to use Unity now and then, so I’m still quite rusty.

Trying to access properties in other game objects is causing me a lot of problems. For example, when my explosion objects are instantiated they check to see what other objects fall within their overlap sphere. I need to check if any of these objects have the public variable ‘strength’. If they do I need to decrease this to simulate damage caused by the explosion.

My first thoughts were to do something like:

if (targetObject.damage) {
   targetObject.damage -= 10
   }

but it looks like I need to something more complicated. Working out what is giving me a headache! I’m sure it’s painfully obvious to the scripting experts on the forum, but I’m stuck.

Well what you need is basically outlined in the Accessing Other Components and the Accessing Other Game Objects sections in the overview.

When you have instantiated the explosion, you can fetch the script component attached to it.

// Lets assume the script you have attached to the prefab is called
// "ExplosionScript", and the variable inside it "damageValue"
var explosion : ExplosionScript = Instantiate(....);
explosion.damageValue=projectileDamage;

Edit:
… i.e. the trick is that you have to access the correct component of the game object, as the game object themselves don’t contain the variables.
In general if you have a game object, you access your script variables by first calling GetComponent on the other object, like this:

var otherInfo : MyScript = otherObject.GetComponent(MyScript);
if (otherInfo) {
   // the otherObject did have a MyScript attached
   otherInfo.someVariable=something // or anything...
}
else {
  // the otherObject did not have a MyScript attached, so no values to manipulate
}

I’ve been reading this section of the documentation, but don’t fully understand it. Sorry. If it makes you feel better I get exactly the same problems when I teach at university. :wink: Students need lots of examples before things ‘click’.

My projectile object has this script attached to it:

/*Publicly declared variables.*/

/*Reference to the explosion used if this projectile hits anything.*/
var explosion : GameObject;

var projectileMass = 0.01;
var projectileDamage = 1;

function Start() {
/*Assign mass.*/
this.rigidbody.mass = projectileMass;
}

function OnCollisionEnter() {
/*Instantiate the explosion object assigned in the inspector.*/
instantiatedExplosion = Instantiate(explosion, transform.position, transform.rotation);

/*Destroy the explosion object after 2 seconds.*/
Destroy(instantiatedExplosion, 2);
	
/*Destroy this projectile object.*/
Destroy(gameObject, 0.0);
}

The ‘explosion’ var at the top is a reference to the explosion object which will be instantiated.

The explosion object has this script attached to it:

/*When the projectile collides with an object it destroys itself then replaces itself with an explosion. When the explosion game object is activated, this script is executed.*/

/*This script applies a force to all nearby colliders.*/

/*Public variables.*/

/*Note that these can't be zero!*/
var explosionRadius = 2;
var explosionPower = 4;

var damage = 1;

/*The duration of the explosion, supposedly in seconds.*/
var duration = 0.1;

/*Find all nearby colliders.*/
colliders = Physics.OverlapSphere(transform.position, explosionRadius);
/*Apply a force to all surrounding rigid bodies.*/
for (var hit in colliders) {
	if(hit.rigidbody) {
		StartCoroutine("ApplyForce", hit.rigidbody);
		}
}

/*Start emitting particles.*/
particleEmitter.emit = true;

/*Wait for half a moment.*/	
yield new WaitForSeconds (duration);


/*Stop Emitting.*/
/*The particle system is setup to autodestroy so when no particles are left the entire game object will be killed*/
particleEmitter.emit = false;

function ApplyForce(body : Rigidbody) {
time = 0.1;
/*Apply the force smoothly over time.*/
/*Check if the rigid body we apply force to is still alive.*/
while (duration > 0  body) {
	explosionPosition = transform.position;
	bodyPosition = body.transform.position;
		
	offset = bodyPosition - explosionPosition;
	direction = offset.normalized;
		
	/*We apply the force based on the distance.*/
	/*Percentage will be in the range 0...1.*/
	/*Where 0 means no force applied. And 1 means apply full force.*/
	rangePercentage = (explosionRadius - offset.magnitude) / explosionRadius;
	rangePercentage = Mathf.Clamp01 (rangePercentage);
		
	power = rangePercentage * explosionPower * time;
	force = direction * power;
	body.AddForceAtPosition (force, explosionPosition);

	/*Yields until the next fixed update frame.*/
	/*Forces always need to be applied at fixed frame rate*/				
	yield new WaitForFixedUpdate ();
	time -= Time.deltaTime;
	}
}

Breaking it down into two problems, how would I do:

1 - Pass the value of the ‘projectileDamage’ variable in the projectile object to the newly instantiated explosion object, assigning it to the ‘damage’ variable there?

2 - In the explosion object when the script loops through all the colliders which fell within the overlap sphere, how would I check whether these objects have a variable ‘damage’. If they do I need to decrement it.

Thanks for your patience and help.

Yes, you pass the value in the first script. As you have a handy reference to the newly created explosion object:

function OnCollisionEnter() { 
   /*Instantiate the explosion object assigned in the inspector.*/ 
   var instantiatedExplosion : GameObject = Instantiate(explosion, transform.position, transform.rotation); 
   var explosionComponent : MyExplosionScript = instantiatedExplosion.GetComponent(MyExplosionScript);
   explosionComponent.damage=projectileDamage;

   /*Destroy the explosion object after 2 seconds.*/ 
   Destroy(instantiatedExplosion, 2); 
    
   /*Destroy this projectile object.*/ 
   Destroy(gameObject, 0.0); 
}

And regarding the damage value of the other objects, see the example I just added to my previous reply.

Thanks for that. I’ll try it out as soon as possible.

Not quite working yet. I’ve added your suggested code so that the OnCollisionEnter function in my projectile script now looks like:

function OnCollisionEnter() {
/*Instantiate the explosion object assigned in the inspector.*/
instantiatedExplosion = Instantiate(explosion, transform.position, transform.rotation);

var explosionComponent : explosion = instantiatedExplosion.GetComponent(explosion); 
explosionComponent.damage=projectileDamage;

/*Destroy the explosion object after 2 seconds.*/
Destroy(instantiatedExplosion, 2);
	
/*Destroy this projectile object.*/
Destroy(gameObject, 0.0);
}

The lines you provided were:

var explosionComponent : MyExplosionScript = instantiatedExplosion.GetComponent(MyExplosionScript); 
   explosionComponent.damage=projectileDamage;

I guessed that ‘MyExplosionScript’ should be replaced with the name of the script I’m using, in this case ‘explosion’. Unfortunately this doesn’t seem to work. I get the error:

Cannot convert ‘error’ to explosion.

Am I doing something wrong? I’m a bit puzzled by the ‘var explosionComponent : MyExplosionScript’ code as I thought what came after the ‘:’ was the type of variable. The short examples in the documentation all seem to use different things here. No doubt I’m missing something obvious.

This is because you have both a variable called explosion and a class called explosion.

It’s a good idea to use some sort of a naming convention to avoid this.
At OTEE we always have variables start with a lower case letter and classes and functions with upper case letters.

In our C# and C++ code we even make sure that member variables start with m_.

Aah. I’ll do as you suggest and see how it works. Thanks.

Sorry, I must be having a thick day. I still can’t get it to work. The problem is I don’t fully understand the code you suggested.

I’ve changed my public variable ‘explosion’ to ‘theExplosion’ and recreated the link in the Unity inspector. However I’m still getting the ‘Cannot convert ‘error’ to ‘explosion’’ error message.

var explosionComponent : explosion = instantiatedExplosion.GetComponent(explosion); 
explosionComponent.damage=projectileDamage;

What’s confusing me about the above code is the first line where the ‘explosionComponent’ variable is declared. My understanding is that the ‘var explosionComponent : explosion’ part means ‘create the variable explosionComponent of the type explosion’. I don’t understand why the type is ‘explosion’ and not something like ‘ScriptObject’. Is it because it has to match the name of the script being referenced? I’m afraid I’m lost.

If you could clear this up for me it would save a lot of head scratching and give me a chance to enter something into the widget contest.

I’ve attached a stripped down version of the project because it’s probably easier to look at it than make sense of my ramblings.

I haven’t had time to look at your code yet, but I will follow up on that one soon.

But yes, you declare a variable of type ‘explosion’ because you are going to access the member variables of your script which defines a class called explosion. I.e. the variables and methods in your script are members of a class of the same name as the name of the script.

I’ll take a look later on and come with a proper answer.

Thanks. I’m glad I sort of understand the declaration part, but your feedback is appreciated. I’d be stumped without your help.

Okay… your problem was not that hard after all… I took a quick look at your project and noticed that you did not declare the type of the return value form Instantiate().

Instantiate can clone any object and therefore by default returns a value of type Object, which does not have a GetComponent method.

Here’s the fixed verision:

function OnCollisionEnter() {
/*Instantiate the explosion object assigned in the inspector.*/
var instantiatedExplosion : GameObject = Instantiate(theExplosion, transform.position, transform.rotation); /// <---- CHANGE

/*Access the script attached to the explosion object so we can set the 'damage' variable using the value of the 'projectileDamage' variable in this script.*/
var explosionComponent : explosion = instantiatedExplosion.GetComponent(explosion); 
explosionComponent.damage=projectileDamage;


/*Destroy the explosion object after 2 seconds.*/
Destroy(instantiatedExplosion, 2);
	
/*Destroy this projectile object.*/
Destroy(gameObject, 0.0);
}

… so the solution is to declare the variable you are going to store the result in to have type GameObject.

If you want to know when you have to declare the type of a return variable, you can look up the definition of the function you are calling (either by starting with MonoBehaviour and walk up the class hierarchy until you find the method definition or by searching through the Alphabetical reference.) If it returns a base class, like a Component or an Object, you must tell the compiler what the actual type should be using the var variable : class-name construct.

Thanks for that. No more errors. If you’re ever in Lancashire the beer’s on me!

Hopefully I’ll be able to get something working soon. I’ll put it online so other people can learn from my fumbling.

Last hurdle hopefully. If you can show me how to do this I’ll be a very happy chappy!

My explosion objects which get instantiated when the projectiles hit anything have the ‘explosion.js’ script attached to them. In this script the ‘damage’ variable gets set by the parent projectile. What I need to do is check whether the objects falling within the overlap sphere of the explosion have the variable ‘strength’. (If they do it’s because they have the script ‘generalObject.js’ attached to them.) I want to decrement this variable by the value of the ‘damage’ var in the explosion object.

In the explosion script we have the following:

/*Find all nearby colliders.*/
colliders = Physics.OverlapSphere(transform.position, explosionRadius);
/*Apply a force to all surrounding rigid bodies.*/
for (var hit in colliders) {
	if(hit.rigidbody) {
		StartCoroutine("ApplyForce", hit.rigidbody);
		}
       /*Does the hit object have a 'strength' property. If so, decrement it using 'damage'.*/
	var objectRef : generalObject = hit.GetComponent(generalObject);
	if (objectRef) {
		objectRef.strength -= damage;
		}
}

I guess this isn’t working because the ‘hit’ variable inside the for loop refers to a collider, not a game object. How would I get this to work? Is there a way to go up the component tree - collider.component.game object?

Woah. You found a bug in our javascript implementation.

Workaround for now is just placing the code you posted inside function Start () instead of on the module level (outside of any functions)

You don’t have to. All components can fetch any other component attached to the same game object using the GetComponent method (All components are attached to GameObjects – there is no hierarchy of components). So yes, you are calling it correctly.

The javascript bug is that right now whenever it sees a var xxx:ttt on the module level, it thinks you are defining a member variable (visible in the inspector) and actually moves that line into the initialisation of the object, and out of any loops it might have found it in…

so just put a

function Start() {
   ....
}

around the code, like Joe suggested in order to work around it.

Blimey. I’ve put it in Start() and everything works. Should I move the code back to where it was once the bug is fixed? When do you think the bug will be fixed? Version 1.1?

Thanks for your help.

Thanks to all your help I have a working example. It’s very basic but you can see what I have in mind.

http://www.pdrummond.btinternet.co.uk/games/shooter1.html

When the large objects are destroyed a single debris object is instantiated. I’d like to generate a random number of debris objects centred around the target, but haven’t been able to. I tried adding a for loop to my script and immediately got strange errors about missing brackets, even though there shouldn’t have been a problem. Because of this I didn’t get any further. That and the fact that it’s late!

Here’s the function which is used to destroy the large blocks and create a debris object. Any suggestions on how to improve it are welcome:

function destroyObject() {
/*Instantiate the explosion object assigned in the inspector.*/
var instantiatedExplosion : GameObject = Instantiate(theExplosion, transform.position, transform.rotation);

/*Destroy this object.*/
Destroy(gameObject, 0.0);

/*If leavesDebris = true we instantiate some debris objects to leave wreckage behind. By instantiating the debris BEFORE we destroy the explosion, the explosion force imparts velocity to it.*/
if (leavesDebris) {
	/*Instantiate the debris object assigned in the inspector.*/
	var instantiatedDebris : GameObject = Instantiate(theDebris, transform.position, transform.rotation);
	instantiatedDebris.rigidbody.velocity = this.rigidbody.velocity;
	/*Generate a random number between -0.5 and 0.5 and use this to scale the instantiated debris object on its x and z axes.*/
	instantiatedDebris.transform.localScale.x += Random.RandomRange(-0.5, 0.5);
	instantiatedDebris.transform.localScale.z += Random.RandomRange(-0.5, 0.5);
	}
	
/*Destroy the explosion object after 2 seconds.*/
Destroy(instantiatedExplosion, 2);
}