Im trying to make a first version of a game with unity, but the trees wont keep spawning after i have destroyed the tree amount to less than 5. In unity when i run the game it displays the tree amount and when it reaches 5 it stops spawning them but if i destroy one it wont spawn a new one even tho the tree amount is less than 5.
Also if i keep destroying them so there is always just two trees then it keeps spawning. But whenever it reaches 5 then it stops.
I have two scripts:
This is the one where if i click the tree it destroys it.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Harvest : MonoBehaviour
{
public GameObject tree;
public int treeScore;
public void AddTreeScore()
{
treeScore++;
}
private Spawner spawnerScript;
void Start()
{
spawnerScript = FindObjectOfType<Spawner>();
}
public void OnMouseDown()
{
GameObject.Destroy(tree);
spawnerScript.treeAmount--;
AddTreeScore();
Debug.Log(treeScore);
}
}
And this is everything else.
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
public class Spawner : MonoBehaviour
{
public GameObject tree;
public int treeAmount;
public float spawnRate = 1f;
public bool canSpawn = true;
public int maxAmount = 5;
private IEnumerator Spawning()
{
WaitForSeconds wait = new WaitForSeconds(spawnRate);
while (canSpawn == true)
{
yield return wait;
Vector2 randomSpawnPos = new Vector2(Random.Range(-10, 11), Random.Range(-4, 5));
Instantiate(tree, randomSpawnPos, Quaternion.identity);
AddTreeAmount();
if (treeAmount >= maxAmount)
{
canSpawn = false;
}
if( treeAmount < maxAmount)
{
canSpawn = true;
}
}
}
void AddTreeAmount()
{
treeAmount++;
}
void Start()
{
StartCoroutine(Spawning());
}
void Update()
{
}
}
I also tried setting the spawnRate to 0f; and then back to 1f;, but that did not work. It showed in unity that the spawnRate went to 0 but it kept still spawning them.
I think i have to set the canSpawn back to true some other way. Or is there more errors or mistakes?
>Solution :
Your coroutine Spawning runs exactly once – at the beginning of the game.
void Start()
{
StartCoroutine(Spawning());
}
Once canSpawn = false; is reached it will never run again.
Instead you could use a field for tracking if an instance of that routine is already running and if not run a new one like e.g.
public class Spawner : MonoBehaviour
{
// in general for things you only set up via the Inspector
// you should use private o avoid other classes to modify them (Encapsulation)
[SerializeField] private GameObject tree;
[SerializeField] private float spawnRate = 1f;
[SerializeField] private int maxAmount = 5;
// backing field for treeAmount
private int _treeAmount;
// keeps track whether a Spawning routine is already running
private bool spawning;
public int treeAmount
{
get => _treeAmount;
set
{
// this is executed whenever a value is assigned
_treeAmount = value;
RunSpawningRoutine();
}
}
private void Start()
{
RunSpawningRoutine();
}
private void RunSpawningRoutine()
{
// if already spawning ignore
if(spawning) return;
StartCoroutine(Spawning());
}
private IEnumerator Spawning()
{
// just in case double safety for blocking concurrent routines
if(spawning) yield break;
// block other routines from running (see above)
spawning = true;
// simpler and cleaner loop condition
while (treeAmount < maxAmount)
{
yield return new WaitForSeconds(spawnRate);
// generally note that this does not prevent two trees spawning in the same spot
var randomSpawnPos = new Vector2(Random.Range(-10, 11), Random.Range(-4, 5));
Instantiate(tree, randomSpawnPos, Quaternion.identity);
treeAmount++;
}
// allow a next routine to start
spawning = false;
}
}
This way whenever a value is assigned to treeAmount it will kickoff RunSpawningRoutine. Here you will check whether a routine is already running in which case you don’t have to do anything, but if not it will start a new routine.