- Home /
Need help understanding scripted prefab behavior - when I click one, the script runs on ALL prefab instances, not just the one I clicked.
Sorry for the lengthy question - I think part of why this is stumping me is that I'm having difficult articulating (and therefore searching for an answer) my problem.
Essentially - I've built a simple street prefab. I can tile these prefabs to create a length of road. The prefab includes a script that, when clicked, instantiates another prefab - a cross walk which sits on top of the street chunk. I also need to store the newly-instantiated crosswalk locally, so that it can be removed later.
However - when I click any of my street prefabs, the script runs on all of them. The result is 18 new crosswalks - there are 18 street prefabs in the scene - all stacked on top of the street the player clicked. Can I somehow get these to behave more locally?
I suspect that I'm just failing to understand a coding fundamental here, so hopefully the answer is something straightforward!
Here's the script in question:
var prefab : GameObject;
// if I declare myCrosswalk here, it's global to ALL instances of this script. :( // var myCrosswalk : GameObject;
function Update () {
// if I declare myCrosswalk here, every click stacks a new crosswalk.
if(Input.GetMouseButtonDown(0))
{
var hit : RaycastHit;
if(Physics.Raycast(Camera.main.ScreenPointToRay (Input.mousePosition), hit))
{
var myCrosswalk : GameObject;
var hitTrans : Transform = hit.transform;
if(hitTrans.gameObject.tag == "Tg_Rd_1way")
{
if (!myCrosswalk)
{
// No current crosswalk on this chunk of road. Make one!
myCrosswalk = Instantiate(prefab, hitTrans.position, hitTrans.rotation);
Debug.Log("Created new instance: "+myCrosswalk);
}
else {
// remove the crosswalk if one is in place.
Destroy(myCrosswalk);
}
}
}
}
}
@nongenre, you should use On$$anonymous$$ouseDown ins$$anonymous$$d of Raycast - take a look at my answer below
Answer by Steven-1 · Apr 28, 2012 at 08:39 PM
well yeah, you're not checking wheter the ray hits the current object, but wheter it hits anything. So ofcourse as soon as it hits anything, all instances of this script will say there's a hit. If you only want to check collision for the current object, there an other raycast function for that, I forgot what it was, but you'll find it in the docs. Edit: tada! http://unity3d.com/support/documentation/ScriptReference/Collider.Raycast.html
Answer by GuyTidhar · Apr 28, 2012 at 06:22 AM
I am not sure what you need to do, but when you need to control the instances of a script, you have the option to use the "static" keyword.
static tells the computer to keep only one copy in memory of the variable you have defined whether it is a value type such as a number or if its an object reference such as a GameObject.
e.g.
1)
Lets say I want to count the number of times, I have instanced a certain script.
I would do: static var scriptCount : int =0;
function Start()
{
scriptCount++;
Debug.Log("Scripts instantiated count: " + scriptCount);
}
Note this: a static variable can not be shown in the editor, even when it is public.
2)
You could also do something like make sure you never get more then one instance of a script, by destroying new instances of it.
private var myScriptInstance : ThisScriptName;
function Start()
{
if ( !myScriptInstance )
myScriptInstance = this;
else // Already instantiated this script - ditch the copy
Destroy(this);
}
3)
The static keyword also enable you to access a variable, if it is also a public one, from all other scripts that know your script (by "know" I mean they were compiled after the script having the static variable you wish to access). In example 2, if the "myScriptInstance" would be public and the file was named "HighLander", you could check from another script the status of the instance:
// Check if there is already one
if ( HighLander.myScriptInstance )
Debug.Log("There can be only one!");
else
Debug.Log("There is none - the fools all decapitated each other's heads.");
I'm not sure if I need to kill all but one instance of the script - I want each section of road to have its own, independent status.
I think I could get around this by not using prefabs, but that seems like the wrong thing to do.
Answer by aldonaletto · Apr 28, 2012 at 09:25 PM
You should declare myCrosswalk outside any function to make it a member variable - variables declared inside functions are temporary, and respawn each time you run the function. Member variables exist independently in each instance, and changing one of them doesn't modify the others.
Your problem has a different cause: when you click, all instances of this script do exactly the same raycast from the camera to the street section you're clicking, and each instance creates its own crosswalk, all of them over the clicked street section!
There's an easier way to do this: use OnMouseDown to detect the click - since this event is only reported to the object clicked, only it will instantiate the crosswalk:
var prefab : GameObject; var myCrosswalk : GameObject;
function OnMouseDown () {
if (!myCrosswalk){
// No current crosswalk on this chunk of road. Make one!
myCrosswalk = Instantiate(prefab, transform.position, transform.rotation);
Debug.Log("Created new instance: "+myCrosswalk);
}
else {
// remove the crosswalk if one is in place.
Destroy(myCrosswalk);
}
}