- Home /
Radio button like toggle behaviour on gameobjects
Hi. Heres what I would like to achieve. I have at least three gameobjects. If I click on one object(out of three) it would have arbitrary boolean value wasClicked set true. If I click on any other gameobject then the boolean for the previously clicked object gets set to false and gets set true for the last object that was clicked. So all the three gameobjects would use the wasClicked boolean but it would be true for only one gameobject at a time.
So far my idea was to
collect the gameobjects in to array
Create a extension method for the wasClicked boolean
Somehow do the radio button like behaviour???
Heres some cut outs from my code
Game object array
private GameObject[] players; // Create array for gameobjects
void Start ()
{
players = GameObject.FindGameObjectsWithTag("Player");//Collect all the right gameobjects
}
void OnMouseDown()
{
RaycastHit hit ;//Set up new raycasthit
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);//Set up new ray shooting out of the camera from where the mouse is
if (Physics.Raycast(ray, out hit))
{
int i = 0;
players[i] = hit.transform.gameObject;//The Parent object that was hit by ray
GameObject myObj = players[i];//Convert array object to parentObject for nicer syntax
if (myObj.wasClicked)//if == true
{
//do something with the gameobject
}
else if (/*Another object was clicked */)
{
//Set wasClicked false for the object that was clicked first and do something
}
}
}
Extension method code
using UnityEngine;
using System.Collections;
public static class toggleObject {
public static void wasClicked(this GameObject go, bool clickState ){
clickState=false;
}
}
If anybody could at least tell me if I'm on the right way with using array's that would be awesome.
And for the boolean... I think my gameobjects (myObj) will end up having child objects added to them when they are clicked... so i think i could use something like
bool hasChild = myObj.transform.hasChildren();//Pseudo code
But I would like to avoid that if possible.
Answer by Jamora · Jul 31, 2013 at 06:30 PM
You seem like the kind of person who likes to think about these things, so I won't give you any working code. Instead, I will tell you where you're going wrong with this approach.
The way you explain using one boolean to mark the state for multiple objects cannot work. A boolean is just true of false, you cannot infer the object it is pointing to. What you could do is use an int to mark which object it is pointing to in the array. So when changing the boolean you would also change the int. But on further thought the boolean is then irrelevant, as it's always true. Following this trail of thought, why not keep a reference to the currently selected GameObject itself instead of an int. This way, the radio button effect will have been achieved, because only one GameObject can be pointed to at any given time.
The current way you're using your array overrides some references to players, namely to the player #0. So you will not be able to reference that player again unless you regain the reference. It puzzles me why you modify the array at all, because you already have the reference to the clicked object from hit.transform.gameObject
.
Finally, your extension method doesn't actually do anything. clickState will be a local variable within the method, so changing it will not have any effect globally. If you feel the need to use this approach (I think it's overcomplicating things) you need to do some research on how to pass variables by reference.
To check for children, you'll need to use transform.childCount
. I'd imagine there isn't a better way.
Thanks for the answer. About using int's. I was previously fiddling with gameobject id-s. I guess I could(maybe should?) also use array id-s. I had something like this int hitID = players[i].GetInstanceID(); This gave me nice numbers, but I ran out of ideas how to check the numbers against each other. Lets say I have objects A B and C. All of those objects are red by default. When I click on any of them the one clicked turns green. But how could i let object C know that object A is already green?
$$anonymous$$y point is that you shouldn't be using ints. Ins$$anonymous$$d store a reference to the GameObject that is currently selected. Then anyone who wants to know which object is selected simply checks if they equal the current selection.
I don't know your use case, but I would solve this problem like this (I know I said no code, but it's easier to show than explain):
public class SelectableCube{
private static GameObject selectedObject = null;
void Update(){
if(selectedObject == gameObject){
renderer.materal.color = Color.Red;
}else{
renderer.material.color = Color.Green;
}
}
void On$$anonymous$$ouseDown(){
selectedObject = gameObject;
}
}
It's not very performant, because the material is changed every frame. A more efficient version would be to only do changes when a new cube has been selected.
Super. You definitely got me in the right $$anonymous$$d set... I think :) This is what I made now
public GameObject[] players;
void Start ()
{
players = GameObject.FindGameObjectsWithTag("Player");
}
void On$$anonymous$$ouseDown()
{
RaycastHit hit ;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
foreach(GameObject player in players)
{
player.renderer.material.color = Color.green;//Set all the objects to green
}
if (Physics.Raycast(ray, out hit))
{
renderer.material.color = Color.red;//Set the object that was clicked red
}
}
And this basically does what I want but it seems a little bit wasteful.
The only way to make this less wasteful is to use a static variable like in my example. So get rid of the foreach, and before you set the current selection's color to red, you set the old selection's color to green and update the reference.
Based on your example I made another version.(It's amazing what 8 hours of sleep can do) Here's the code
public GameObject[] players;
private GameObject selectedPlayer = null;
void Start ()
{
players = GameObject.FindGameObjectsWithTag("Player");
}
void On$$anonymous$$ouseDown()
{
RaycastHit hit ;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
selectedPlayer = hit.transform.gameObject;//Set selected player equal to clicked gameobject
foreach(GameObject player in players)
{
if(player == selectedPlayer)
{
selectedPlayer.renderer.material.color = Color.red;//Set selected player color red
}
else
{
player.renderer.material.color = Color.green;//Set color green for all the other elements
}
}
}
But what I'm wondering about is that is it a good Idea to run through all the other objects in my array? Because I would actually need to set color = green for only one object. I guess i could add more conditions to the else statement. something like this
else if (player == ! selectedPlayer && player.renderer.material.color == Color.red)
{
//Do my thing
}
Is it something I should do? I'm just asking because I want to get in to "good habits" and learn and understand correct program$$anonymous$$g concepts.