- Home /
Finding all Game Objects with multiple Tags
Ok, so heres my problem:
Say I have 2 Arrays setup, one array of strings and the other for gameobjects.
Just like this:
var townsPeople : int;
var bandits : int;
var allTags : String[];
var allTownsPleopleTags : String[];
var allMaffiaTags : String[];
var allPlayers : GameObject[];
var allTownsPleople : GameObject[];
var allMaffia : GameObject[];
function Start(){
}
function Update () {
for(apt = 0; apt < allTags.Length; apt++){
allPlayers = GameObject.FindGameObjectsWithTag(allTags[apt]);
}
for(atp = 0; atp < allTownsPleopleTags.Length; atp++){
allTownsPleople = GameObject.FindGameObjectsWithTag(allTownsPleopleTags[atp]);
}
for(am = 0; am < allMaffiaTags.Length; am++){
allMaffia = GameObject.FindGameObjectsWithTag(allMaffiaTags[am]);
}
}
You can probably tell what i'm trying to do. I'm trying to enter all of my tags into the string array and get all the game objects with the correct tags. The problem is that I'm only getting one random object with a random tag. I think that its because I have my code in the Update function, but it has to be there because my tags are going to change will in game. Any ideas?
Answer by Moonlight · Jun 22, 2011 at 05:10 AM
OK Guys, I figured it out!!!
Here is my final script, I used a for loop inside another for loop:
import System.Collections.Generic;
var townsPeople : int;
var mafia : int;
var allTags : String[];
var allTownsPleopleTags : String[];
var allMaffiaTags : String[];
var allPlayersList = new List.<GameObject>();
var townspeopleList = new List.<GameObject>();
var mafiaList = new List.<GameObject>();
function Update(){
UpdateTags();
}
function UpdateTags() {
allPlayersList.Clear();
townspeopleList.Clear();
mafiaList.Clear();
for(apt = 0; apt < allTags.Length; apt++){
var APT1Test : GameObject[] = GameObject.FindGameObjectsWithTag(allTags[apt]);
for(i = 0; i < APT1Test.length; i++){
allPlayersList.Add(APT1Test[i]);
}
}
for(atp = 0; atp < allTownsPleopleTags.Length; atp++){
var ATP2Test : GameObject[] = GameObject.FindGameObjectsWithTag(allTownsPleopleTags[atp]);
for(o = 0; o < ATP2Test.length; o++){
townspeopleList.Add(ATP2Test[o]);
}
}
for(am = 0; am < allMaffiaTags.Length; am++){
var AM3Test : GameObject[] = GameObject.FindGameObjectsWithTag(allMaffiaTags[am]);
for(p = 0; p < AM3Test.length; p++){
mafiaList.Add(AM3Test[p]);
}
}
}
It might be a very roundabout way to do it, but it works very well for my needs. I can not tell you how helpful you have all been.
PS: I don't really need to worry about it updating every frame because this is for a chat based game, no powerful graphics are going to be used. I did add a new function in the script so that if I really need to, I can get rid of the update function and just call the UpdateTags function from another script when I'm ready. Everyone was so worried about the performance hit by running Update, but I guess it would have helped if I mentioned there are no other heavy graphics or any other intensive scripts to bog down the performance. In my case, this works flawlessly.
Again, thank you to everyone for being so helpful, I think this is a great addition to the unity tagging system and will help in find Game Objects faster and easier. Feel free to use this in your own projects :-)
Glad the script works for you.
Though I would still suggest moving this out of the Update loop. It's unnecessary overhead. I bet it is easy to insert some kind of OnPlayerConnect/OnPlayerDisconnect functions and you just run the UpdateTags() in these functions.
Your solution might blow up on you once more people connect at the same time since the performance load of UpdateTags() will increase exponentially with each new user.
On the other hand, it's perfectly fine for you to say "works for me now, I'll deal with it once it becomes problematic", just sth for you to keep in $$anonymous$$d :)
Happy coding.
Answer by Eric5h5 · Jun 18, 2011 at 08:33 PM
You can use a List of GameObjects, and loop though the array of tags, adding each result to the List using AddRange. You really don't want to do that in Update though, regardless of whether the tags change in the game. Just do it when the tags actually change, not every frame. It would probably be best to make a FindGameObjectsWithTags function, so you can use it easily for different Lists of GameObjects.
import System.Collections.Generic;
var allPlayers : List.<GameObject>;
var allTags : String[];
function Start () {
allPlayers = FindGameObjectsWithTags (allTags);
}
function FindGameObjectsWithTags (tags : String[]) : List.<GameObject> {
var combinedList = new List.<GameObject>();
for (i = 0; i < tags.Length; i++) {
var taggedObjects = GameObject.FindGameObjectsWithTag(tags[i]);
combinedList.AddRange(taggedObjects);
}
return combinedList;
}
You could convert the List to a GameObject[] array, but you might as well save the conversion and leave it as a List, since it's more flexible than GameObject[] and is barely any slower.
I'm sorry, I don't know that much about lists, so I have no idea what this is doing. Could you please explain.
PS: Yes, I need it in the update function because this is for a multiplayer game where people are going to be joining all the time, and I need these arrays to tell if there is a change anyway. :-(
@$$anonymous$$oonlight: No, you really don't need it in the Update function. It's a very bad idea and will hurt performance, so don't do it. As I said, just do it when the tags actually change. As for List: http://msdn.microsoft.com/en-us/library/6sh2ey19.aspx It's like Array, except better and much faster.
I'm not trying to be harsh, but doing these 3 loops and using Find or Find Tags in an Update function is a horrible solution and I promise you there should be a better way to do this.
Could I ask why are you changing the tags, or what are you trying to do by changing them?
The tags were going to by my way to define "jobs" or "roles" of each person in game. Also when a player dies, there tag will change from whatever it was to "dead" because players don't re-spawn, they wait for the next game to start.
It seems you are using tags as variables. Tags should be constant I$$anonymous$$HO, unless you need some sort of a hack in a situation when doing anything else will cost you to lose your ability to finish a project.
I'd define roles using enums, e.g.:
enum PlayerRole { CleanHouse, DoTheDishes, TakeDogOut, Eat, Sleep }
Then, you can use the enum type above to check each player status and change it according to the status it is currently at.
Answer by Oliver Eberlei · Jun 18, 2011 at 09:57 AM
In your first for loop you use allTownsPleopleTags to define the length but eventually call FindGameObjectsWithTag() with your allTags array
Also, you will only get the results from the last tag in each tag-array since you always overwrite the content of the gameObject arrays.
A quick google search for "site:unity3d.com javascript array merge" gave me this
http://answers.unity3d.com/questions/59153/how-to-combine-vector3-arrays.html
Sorry about the allTags error, I didn't copy and past my exact code. The combining method seems like a good idea, but how would I combine ALL the results into one array?
Just copy all three arrays into one as described in the link?
I'm not trying to combine the 3 arrays, i'm trying to get 3 arrays of game objects form the 3 arrays of strings that will hold all the tags I want. I'm doing this for a multiplayer game where everyone will have different roles and each set of roles will be in its own group.
If that makes any sense, lol.
(I reposted this commant as an answer, since the formating here is messed up)
Answer by Oliver Eberlei · Jun 21, 2011 at 06:40 AM
You could define roles and jobs by different components. For example, you have a Warrior and a Cook component assigned to a character game object, by checking
if( characterObject.GetComponent<WarriorClass>() != null )
{
//this is a warrior
}
if( characterObject.GetComponent<CookJob>() != null )
{
//this is also a cook
}
so for your example, you have a townspeople and a mafia component. (These components really don't have to do anything, but I suggest you rethink your component structure so each action is done by its respective component)
to seperate them you then use sth like this
var mafiaList = new List.<GameObject>();
var townspeopleList = new List.<GameObject>();
for( i = 0; i < allCharacters.Length; ++i )
{
if( allCharacters[ i ].GetComponent<Mafia>() != null )
{
mafiaList.Add( allCharacters[ i ] );
}
if( allCharacters[ i ].GetComponent<TownsPeople>() != null )
{
townspeopleList .Add( allCharacters[ i ] );
}
}
(I tried to do the example in JavaScript, but no guarantees since I am using C#)
That dose seem like a good idea, but I tried something very similar to this and had trouble converting the lists back to arrays.