- Home /
Several objects sharing a script
This is a fundamental question that I don't understand: How do I make several objects to share a script? I need to create several instruments in industrial process, each with a script handling temperature, pressure, etc. But one must be able to use several copies of the same instrument such as a valve, but at different places, with different input and output.
The first thing I do is a user's interface. The First Person moves and when the mouse is over an interactive part of an instrument (such as a switch or button) it is highlighted and accept a mouse input to turn it on, off, etc.
The basic codes are:
function Update () {
var ray : Ray = Camera.main.ScreenPointToRay (Input.mousePosition);
var hit : RaycastHit;
if (collider.Raycast (ray, hit, 2.0)) { // if hit!
hit.collider.gameObject.renderer.material.color = Color.red; //highlight it red.
}
}
Well, it works for the first object but not for a duplicated one. I am sure I am doing a fundamental mistake about Object Oriented Programming but I don't know what. Thanks in advance,
Michel
Thank you Eric and Aldo. Using On$$anonymous$$ouse was my first attempt. But then, I was told on this forum that I should rather us RayCast.
$$anonymous$$y problem is this: my colleagues say that I must learn Object Oriented Program$$anonymous$$g. I think I can but I can't grasp it in Unity. Say, I would like to make a class of object that is the user interface. I design it to be a RayCast that shows a red overlay to indicate that the UI is active.
Each time I need a UI, I make an instance of it and if e.g. in one case I want it to highlight to yellow, I modify it. But the day I decide that UI is On$$anonymous$$ouse and not RayCast, I can modify the UI class and ... lo and behold, everything is modified!
Now, how do I do that with Unity3D and JavaScripting?
Thanks in advance. Your help is much appreciated and a wonderful Season Holiday from Norway where Santa lives! ;-)
$$anonymous$$ichel
Answer by aldonaletto · Dec 23, 2012 at 09:31 PM
I would use OnMouse events in the object script: this is an easier solution in your case, because these events can modify the variables in the selected object without altering the other instances. But nothing is perfect: only the left mouse button is recognized in OnMouseDown/Up events, and you can't limit the range in OnMouseEnter/Exit events. Anyway, the code in each object could be something like this:
var buttonOn = false; // current button state
private var selected = false; // tells whether the button is under the mouse pointer
private var oldColor: Color; // saves the old color for deselection
function OnMouseEnter(){ // mouse pointer entered object:
oldColor = renderer.material.color; // save old color...
renderer.material.color = Color.red; // set selected color...
selected = true; // and set variable selected to true
}
function OnMouseExit(){ // mouse pointer has left the object:
renderer.material.color = oldColor; // restore original color
selected = false; // clear variable selected
}
function OnMouseDown(){ // object clicked:
buttonOn = !buttonOn; // toggle button state
}
The boolean variable selected isn't being used in this example, but may be useful in some other cases. If you want to detect right mouse button clicks, for instance, read the right button inside Update - but only when the object is selected:
function Update(){
if (selected && Input.GetMouseButtonDown(1)){
// right mouse button pressed over this object
}
}
EDITED: As @Eric5h5 mentioned, OnMouse events don't have a range limiting parameter, thus far objects may be unintentionally selected. But combining your original code with the logic above is somewhat easy, and may solve this problem (attach this script to the object prefab):
var buttonOn = false; // current button state
private var selected = false; // tells whether the button is under the mouse pointer
private var oldColor: Color; // saves the old color for deselection
function Update () {
var ray : Ray = Camera.main.ScreenPointToRay (Input.mousePosition);
var hit : RaycastHit;
if (collider.Raycast(ray, hit, 2.0)) { // mouse is over this object and inside range:
if (!selected){ // this is the equivalent of OnMouseEnter
oldColor = renderer.material.color; // save old color...
renderer.material.color = Color.red; // set selected color...
selected = true; // and set variable selected to true
}
} else { // mouse not over this object or out of range:
if (selected){ // this is the equivalent of OnMouseExit
renderer.material.color = oldColor; // restore original color
selected = false; // clear variable selected
}
}
if (selected){ // if this object is selected, check buttons here:
if (Input.GetMouseButtonDown(0)){
// left mouse button pressed over this object
buttonOn = !buttonOn; // toggle button state
}
if (Input.GetMouseButtonDown(1)){
// right mouse button pressed over this object
}
// even key events "over" this object may be detected:
if (Input.GetKeyDown("space")){
// space pressed "over" this object
}
}
}
Notice that the variables buttonOn, selected and oldColor are member variables, thus they exist and retain their values while the script exists. On the other hand, the variables ray and hit are temporary: they're created when entering the function Update and destroyed upon return.
That's how you may have several instances of the same script running independently of each other: each one has its own variables and stack internally allocated in a private space - if you click one of these objects, for instance, its buttonOn variable is toggled, but the buttonOn variables in the other instances aren't modified.
The problem is that there is no depth parameter when using On$$anonymous$$ouse events, and presumably the point of using 2.0 as the distance in Raycast is to prevent selection of more distant objects.
Yes, that's the limitation of On$$anonymous$$ouse events. I edited my answer and combined the collider.Raycast code with the logic above.
Answer by Eric5h5 · Dec 23, 2012 at 08:42 PM
It's correct and will work. If it doesn't, then the object either doesn't have a collider or is too far away. You can shorten the code a bit:
hit.collider.renderer.material.color = Color.red;
If you look at the docs for Collider, you can see that renderer is an inherited variable.
Thank you for your answer, Eric, indeed it works but only when I have one object attached to that script. If I have two, the behaviour is ... erratic.
In any case, what I really don't understand is how I make a function that can be called from many different places. I have no experience in OOP, only a long experience in Flash ActionScript 2 where I use routines at the root to handle often repeated tasks.
What would be the way, with Unity3D to make a user interface that can be called from various scripts?
I don't know what you mean. The correct practice is to put the code you posted into a script, and then attach that script to any object that you want to be affected.
In Unity, each object in scene has its own script instance - this way, a variable in some object keeps its value independently of the others in other objects. I don't know how exactly this is implemented, but suppose that each script instance stores its variables and stack in a private memory area allocated when the instance is created.
A variable declared outside any function is a member variable: it exists during the script life, and is independent of its twin sisters in other script instances. If you need an equivalent to the old style "global" variable, however, declare it as static: it's allocated and initialized only once when the program starts, and exists during the whole program life. Like old style global variables, they are unique: their values are the same for all script instances.
Your answer