- Home /
How to find all objects in specific layer?
I know how to do that with tags, but in my case I would need to use layers since I'm already using tags for other purposes on those objects.
Is there a way to return the amount of objects in a specific layer?
Thanks for your time! Stephane
Answer by Eric5h5 · Oct 23, 2011 at 07:39 PM
There's no built-in function, so you have to make your own. You can get all gameobjects, and iterate through them to find the ones with the specified layer, like this:
function FindGameObjectsWithLayer (layer : int) : GameObject[] {
var goArray = FindObjectsOfType(GameObject);
var goList = new System.Collections.Generic.List.<GameObject>();
for (var i = 0; i < goArray.Length; i++) {
if (goArray[i].layer == layer) {
goList.Add(goArray[i]);
}
}
if (goList.Count == 0) {
return null;
}
return goList.ToArray();
}
Hi Eric5h5, thanks for your answer, it's really helpful. Is this pretty much what the "FindObjectsWithTag" function do? The reason I'm asking is because I wonder if the code you gave me above will be as fast as the "FindObjectsWithTag" function. In any case I would only do this on Awake or Start, so even if it's slower, I can live with it.
Again, thanks for your time! Stephane
I would assume so; or at least I can't think of a better way that it could be done offhand.
Thanks Eric, this was useful, converted it to C#:
GameObject[] FindGameObjectsWithLayer (int layer) { GameObject[] goArray = FindObjectsOfType(typeof(GameObject)) as GameObject[]; List goList = new List(); for (int i = 0; i < goArray.Length; i++) { if (goArray[i].layer == layer) { goList.Add(goArray[i]); } } if (goList.Count == 0) { return null; } return goList.ToArray(); }
@Eric5h5 I disagree, for sure that's not what FindObjectsWithTag
and it's slower, as you can find on GameObject.Find
documentation "for performance reasons it is recommended to not use this function every frame Ins$$anonymous$$d cache the result in a member variable at startup or use GameObject.FindWithTag" and on Object.FindObjectsOfType
"please note that this function is very slow. It is not recommended to use this function every frame".
FindWithTag isn't all that much faster than Find. If you're doing any kind of find, it's standard practice to cache the results if you're going to be re-using them.
Answer by scottmitting · Feb 03, 2013 at 03:53 AM
I needed a solution that would let me find all objects that were inactive, and I was doing it from an EditorWindow, so I had to take a different route, using GetComponentsInChildren() and supplying a root game object to check within:
private static List GetObjectsInLayer(GameObject root, int layer)
{
var ret = new List();
foreach (Transform t in root.transform.GetComponentsInChildren(typeof(GameObject), true))
{
if (t.gameObject.layer == layer)
{
ret.Add (t.gameObject);
}
}
return ret;
}
Nice, thx for the code snippet, it will come in handy :)
Answer by roberto_sc · Oct 24, 2013 at 01:21 AM
For using inside the Update call, I suggest making a class and inheriting all your scripts from it instead of MonoBehaviour
. In this class you can create caches specific to each layer:
public class LayeredMonoBehaviour : MonoBehaviour
{
private static IDictionary<int, IList<GameObject>> layersCache = new Dictionary<int, IList<GameObject>>();
/**
* Returns a list of all (active/inactive) GameObjects of the specified layer. Returns null if no GameObject was found.
*/
public static IList<GameObject> FindGameObjectsWithLayer(int layer)
{
return layersCache[layer];
}
/**
* Returns one GameObject with the specified layer. Returns null if no GameObject was found.
*/
public static GameObject FindWithLayer(int layer)
{
var cache = layersCache[layer];
if (cache != null && cache.Count > 0)
return cache[0];
else
return null;
}
protected void Awake()
{
// careful: this code assumes that your GameObjects won't change layer during gameplay.
IList<GameObject> cache;
if (!layersCache.TryGetValue(gameObject.layer, out cache))
{
cache = new List<GameObject>();
layersCache.Add(gameObject.layer, cache);
}
cache.Add(gameObject);
}
protected void OnDestroy()
{
layersCache[gameObject.layer].Remove(gameObject);
}
}
The disadvantage is that you'll have to override Awake
and OnDestroy
and call base's implementation if you intend to use these methods in your scripts; but in general this is a good strategy and during the development of your game you may find other stuff that you want to add to this parent class that you wouldn't be able to do with extension methods for the regular MonoBehaviour.
It's also important to note that it won't work for GameObjects that doesn't have any scripts attached to it!
Answer by twobob · May 19, 2015 at 07:18 PM
I wanted an editor solution that didn't make me specify root.
Layer 31 is hardcoded.
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
public class GetAllObjectsInLayer : MonoBehaviour
{
public static int layer = 31;
[MenuItem("Tools/Select Objects in Layer 31", false, 50)]
public static void SelectObjectsInLayer()
{
var objects = GetSceneObjects();
GetObjectsInLayer(objects, layer);
}
private static void GetObjectsInLayer(GameObject[] root, int layer)
{
List<GameObject> Selected = new List<GameObject>();
foreach (GameObject t in root)
{
if (t.layer == layer)
{
Selected.Add(t);
}
}
Selection.objects = Selected.ToArray();
}
private static GameObject[] GetSceneObjects()
{
return Resources.FindObjectsOfTypeAll<GameObject>()
.Where(go => go.hideFlags == HideFlags.None).ToArray();
}
}