How to count child gameobjects with tag

Hi, I am trying to create a function that spawns enemies. However, I want the script to count the number of children with the tag “enemy” and only spawn if that number of children is less than the spawn count variable.

I have written code to do this, but the update function fills the list immediately, because its counting and adding per frame.

How can I get it to only count the game objects each frame and update the total number, rather than adding to the list?

Here is the code I am using:

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class ScriptObjectMonsterSpawner : MonoBehaviour
{

    public GameObject monster;
    public GameObject[] spawnPoints;
    public GameObject particle;
    private GameObject currentPoint;
    public List<GameObject> Children;
    private int index;
    public int spawnAmount = 5;
    public float spawnRate = 5.0f;
    private bool spawning;
        
    // Use this for initialization
    void Start()
    {
        particle.SetActive(false);
    }

    // Update is called once per frame
    void Update()
    {
        
        if (transform.childCount < spawnAmount && spawning == false)
        {
            StartCoroutine(SpawnMonster());
            SpawnMonster();
        }

        foreach (Transform child in transform)
        {
            if (child.tag == "Enemy")
            {
                Children.Add(child.gameObject);
            }
           
        }


    }

    public IEnumerator SpawnMonster()
    {
        spawning = true;
        particle.SetActive(true);
        spawnPoints = GameObject.FindGameObjectsWithTag("SpawnPoint");
        index = Random.Range(0, spawnPoints.Length);
        currentPoint = spawnPoints[index];
        var newmonster = Instantiate(monster, currentPoint.transform.position, currentPoint.transform.rotation) as GameObject;
        newmonster.transform.parent = gameObject.transform;
        yield return new WaitForSeconds(spawnRate);
        particle.SetActive(false);
        spawning = false;
    }
}

Thanks in advance for any help :slight_smile:

Since you’re adding the enemy children to a list anyway, why don’t you avoid searching for tags and going through the children altogether, and let the spawning function handle it. That is, add the monsters to the list as they’re created. You can then use the Count of that list to determine if more should be spawned too.

So the full code might be something like this (I made a couple of other tweaks too):

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
 
public class ScriptObjectMonsterSpawner : MonoBehaviour
{
 
    public GameObject monster;
    public GameObject[] spawnPoints;
    public GameObject particle;
    public List<GameObject> Children;
    public int spawnAmount = 5;
    public float spawnRate = 5.0f;
    private bool spawning;
 
    // Use this for initialization
    void Start()
    {
        Children = new List<GameObject>();
        //^ Could initialise the size of this with the max number of enemies there would be too (if this is fixed)

        spawnPoints = GameObject.FindGameObjectsWithTag("SpawnPoint");
        //^ Moved this from the coroutine as you only need to find the spawn points once (and it's expensive)

        particle.SetActive(false);
    }
 
    // Update is called once per frame
    void Update()
    {
 
        if (Children.Count < spawnAmount && spawning == false)
        {
            StartCoroutine(SpawnMonster());
        }
    }
 
    public IEnumerator SpawnMonster()
    {
        spawning = true;
        particle.SetActive(true);

        var index = Random.Range(0, spawnPoints.Length);
        var currentPoint = spawnPoints[index];
        var newmonster = Instantiate(monster, currentPoint.transform.position, currentPoint.transform.rotation) as GameObject;

        //^Note that we could simplify the above since we don't use index or currentPoint for anything else, like so:
        //var newmonster = Instantiate(monster, spawnPoints[Random.Range(0, spawnPoints.Length].transform.position

        Children.Add(newmonster);
        newmonster.transform.parent = gameObject.transform;
        yield return new WaitForSeconds(spawnRate);
        particle.SetActive(false);
        spawning = false;
    }
}

That should do it for you =).

As a side note:

If you wanted to filter children by tag, you could do something like this:

taggedChildren = new List<Transform>();

foreach(Transform child in transform)
{
    if(child.tag == "DesiredTag") taggedChildren.Add(child);
}

taggedChildren is now a list of all the relevant children.

But you don’t need to do that in this case.

I think it’d be easier if you would run the coroutine and in there yield if no creation is needed.

void Start(){
    StartCoroutine(SpawnMonster());
}

IEnumerator SpawnMonster(){
   // if too many children do not do anything
   if(transform.childCount > spawnAmount) yield return null;
   // else create new enemy
   particle.SetActive(true);

   // here if you do not add new spawn points, 
   // you could find them in the Start once and for all
   spawnPoints = GameObject.FindGameObjectsWithTag("SpawnPoint");

   index = Random.Range(0, spawnPoints.Length);
   currentPoint = spawnPoints[index];
   var newmonster = Instantiate(monster, currentPoint.transform.position, currentPoint.transform.rotation) as GameObject;
   newmonster.transform.parent = gameObject.transform;
   yield return new WaitForSeconds(spawnRate);
   particle.SetActive(false);
}

I reckon your code is working, I just removed the spwaning boolean which is not useful anymore.

I think I have the definitive answer, people :smiley:

In my case I have some Prefabs with bricks inside. But some of these prefabs have also gameObjects with bricks inside them. So, I needed to recursively get inside these “sub-objects”, count the subobjects and return the total amount.

public int initialBricks;
	void Awake () {
		initialBricks = NumOfBricks (transform);
	}

	public int NumOfBricks(Transform mainT){
		int n = 0;
		n +=BrickCounter (mainT, 0);
		return n;
	}
	 int  BrickCounter(Transform trans, int num){
		foreach (Transform t in trans) {
			if (t.tag == "Brick") {
				num++;
			} else {
				num +=BrickCounter (t, 0);
			}
		}
		return num;
	}

If it’s not enough self-explainatory, please ask for explanations, I will edit the post to clarify it :slight_smile:

I knew I could do it better, and here is the abstract method to count children and sub-children of object with a target Tag.

It has an overload method that allows to start the count in a number different from 0.

public int ChildrenWithTag(Transform childrenOf, string targetTag){	
		int	currentCount = 0;
		currentCount +=TagCounter (childrenOf, targetTag, 0);
		return currentCount;
	}
	public int ChildrenWithTag(Transform childrenOf, string targetTag,  int currentCount){
		 currentCount +=TagCounter (childrenOf, targetTag, 0);
		return currentCount;
	}

	int  TagCounter(Transform childrenOf,string targetTag, int currentCount){
		foreach (Transform t in childrenOf) {
			if (t.tag == targetTag) {
				currentCount++;
			} else {
				currentCount +=TagCounter (t, targetTag, 0);
			}
		}
		return currentCount;
	}