- Home /
Find objects by one tag or another.
Hi, I need to find all objects in a scene that have either the tag "1", "2" or "3". The query should return an array of all the objects. I can only find references to finding all objects by ONE tag.
The reason for this is I am trying to code a food chain, and animals that are tagged with "3" can find and eat all animals tagged "2" or "1".
If there is a better way to do this than using tags please let me know, I have been googling for a full day.
Thanks
Answer by aldonaletto · Feb 20, 2013 at 12:20 PM
You could just use GameObject.FindGameObjectsWithTag 3 times at Start, and check the appropriate arrays - animals 3 look for food in arrays 1 and 2, for instance. If you really want the animals in a single array, however, add some specific script to all animals, and get them all at once with GameObject.FindObjectsOfType(ScriptName), again at Start. Both alternatives have two drawbacks: newly created animals aren't included in the arrays, and destroyed ones aren't removed. The last problem is easy to work around: any references to a destroyed game object magically become null in Unity, thus you should skip null array elements - they were already eaten. But the first issue is more complicated: Find functions are too slow to be used in Update. If new animals may be added during the level, it would be better to use another approach: create an empty object - say, "Animals" - reset its position and rotation and child all animals to it when they are created. You could thus iterate through all animals with a simple for..in like this:
for (var animal: Transform in animals.transform){
switch (animal.tag){
case "1":
case "2":
// this is food!
break;
case "3":
// it's a colleague, don't eat it!
break;
}
}
Since all animals are childed to "Animals" when created, its children list is always up to date - and destroyed animals are automatically removed from the list, thus you don't have to worry about null references.
EDITED:
In order to use the "animals group" approach, you should create and empty game object named "Animals" and reset its position and rotation. All animals should have code to child themselves to Animals when they are created. The same script could be used in all animals, if it had fields indicating which itens the animal can eat - like this:
var food1 = "2"; // this animal can eat types 2 or 3
var food2 = "3"; // the eatable types can be defined in the Inspector
private var animals: GameObject;
function Start(){
animals = GameObject.Find("Animals"); // find the animals group
transform.parent = animals.transform; // child itself to it
}
function FindClosestTarget () : GameObject {
var closest: GameObject;
var distance = Mathf.Infinity;
var position = transform.position;
for (var trFood: Transform in animals.transform){ // for each animal...
if (trFood == transform) continue; // avoid eating yourself!
var food = trFood.gameObject; // get its GameObject reference
// check if it's comestible:
if (food.CompareTag(food1) || food.CompareTag(food2)){
// if so, check if it's the nearest one:
var curDistance = (trFood.position - position).sqrMagnitude;
if (curDistance < distance){ // it's the nearest up to now:
closest = food; // save its game object reference...
distance = curDistance; // and its squared distance
}
}
}
return closest; // return the nearest one, or null if there's nothing to eat
}
If the animal can only eat one type, set food1 and food2 to the same type (CompareTag only accepts existing tags - invalid values throw a runtime exception). You can of course have more eatable types: just declare food3, food4 etc. and include them in the if clause.
I was wondering if you could expand on this a little? Case "1" will be food for animals tagged as 2. Would I have to change the script for each tier of animal or would there be a way of doing ti within one script?
Also, I need the animal to chase the nearest food. So don't I need some way of iterating through them to find the closest one?
I think I have confused myself...
This is what I had initially to find the closest object with the tag "1".
function FindClosestTarget () : GameObject {
var gos : GameObject[] = GameObject.FindGameObjectsWithTag("1");
var closest : GameObject;
var distance = $$anonymous$$athf.Infinity;
var position = transform.position;
for (var go : GameObject in gos) {
var diff = (go.transform.position - position);
var curDistance = diff.sqr$$anonymous$$agnitude;
if (curDistance < distance) {
closest = go;
distance = curDistance;
}
}
targetPosition = closest.transform.position;
return closest;
}
It depends on how often you call this function, how many animals you have and how complex is your scene. If there are 10 or 20 animals, and this function is called only when some one was eaten, this approach may be ok. But if you have 100 animals or more, maybe the function will be called too often, causing hiccups in the performance - specially if your scene is too complex: FindGameObjectsWithTag must check all game objects to find the ones of interest, and allocate arrays in memory, what causes more frequent expensive garbage collection internal operations.
Actually, the most efficient approach would be the "animals" group, since you don't have to use any Find functions - just search for the closest food when needed. I'll edit my answer and include an example of this alternative.
Answer by gheeler · Feb 20, 2013 at 12:52 PM
why not just call GameObject.FindGameObjectsWithTag 3 times? and have 3 arrays or gameobjects.
to add them all to the same array
GameObject[] threeTags = new GameObject[arr1.length + arr2.length + arr3.length];
arr1.CopyTo (threeTags, 0);
arr2.CopyTo (threeTags, arr1.length);
arr2.CopyTo (threeTags, arr2.length);
Answer by save · Feb 20, 2013 at 12:34 PM
Aldo has a great point and that is the method you should use. Although for people who enter this domain searching for treasure regarding the title, here's another take which will sum all objects with tag(s) into one single array.
private var gameObjectsWithMyTags : GameObject[]; //Cached objects
function Start () {
// Define the tags
var tags : String[] = ["Tag 1", "Tag 2", "Tag 3"];
// Collect the objects with tags
gameObjectsWithMyTags = FindGameObjectsWithTags(tags);
// Do something with gameObjectsWithMyTags
for (var go : GameObject in gameObjectsWithMyTags)
Debug.Log("GameObject: "+go.name+" has tag: "+go.tag);
}
function FindGameObjectsWithTags (tags : String[]) : GameObject[] {
var allFoundObjects = new Array();
for (var tag : String in tags) {
allFoundObjects = allFoundObjects.Concat(GameObject.FindGameObjectsWithTag(tag));
}
return allFoundObjects.ToBuiltin(GameObject) as GameObject[];
}
Your answer
Follow this Question
Related Questions
How can I detecting whether certain characters appear in any strings in my array of strings? 1 Answer
How do I make an array and then access an Object from the array 2 Answers
Best method to manage multiple tags 2 Answers
(JS) Calling a member function of an object in an array 2 Answers
Need a Custom array Index to look up gameobject tags? 2 Answers