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
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
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();
}
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;
}
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 GameObject
s that doesn’t have any scripts attached to it!
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();
}
}
Just to correct
static List<GameObject> GetObjectsInLayer(GameObject root, int layer)
{
var ret = new List<GameObject>();
foreach (Transform t in root.transform.GetComponentsInChildren(typeof(GameObject), true))
{
if (t.gameObject.layer == layer)
{
ret.Add(t.gameObject);
}
}
return ret;
}
Easiest way to do it is with Linq To GameObject package. It’s free. This is how I do it.
void Start()
{
_walkableGameObjects = GetGameObjectsInLayer("Ground");
}
public List<GameObject> GetGameObjectsInLayer(string layerName)
{
List<GameObject> allObjectsInLayer = new List<GameObject>();
//works
allObjectsInLayer = GameObject.Find("SCENE ROOT").Descendants().Where(t=>t.layer == LayerMask.NameToLayer(layerName)).ToList();
Debug.Log($"<color=blue>there are {allObjectsInLayer.Count} items on the ground layer</color>");
return allObjectsInLayer;
}
I needed a C# version, displayed in the Editor menu (Tools), to simply create an inventory of game objects by layer. Here’s the code (it should be part of a class that extends Editor
class and is inside the "Editor"
folder):
[MenuItem("Tools/Show Objects in each Physics Layer")]
public static void FindGameObjectsWithLayer() {
Dictionary<int, List<string>> gameObjects = new();
Debug.Log($"Scanning objects...");
foreach (var gameObject in GameObject.FindObjectsOfType<GameObject>(true))
{
int layer = gameObject.layer;
if (!gameObjects.TryGetValue(layer, out List<string> objects))
{
objects = new();
gameObjects.Add(layer, objects);
}
objects.Add(gameObject.name);
}
Debug.Log($"Done.");
foreach (var layer in gameObjects.Keys)
{
Debug.Log(
$"Layer ({layer}): {LayerMask.LayerToName(layer)}: {gameObjects[layer].Count} objects\n" +
$"{string.Join("\n- ", gameObjects[layer])}");
}
}