Hi and welcome!
You will need some code to keep track of the current number of coins, as well as some knowledge (for example lists) about which locations are occupied or free. You also need to have a number for how many coins should be alive at the same time, in your case 2.
Now you can check if the number of coins is smaller than the desired amount of coins, and if so, pick a random free location, spawn a coin there and mark it as occupied now.
When a coin gets destroyed we do the same in reverse, effectively removing an occupied location and adding a free one. However, since we dont want to respawn a coin on the same location (which happened quite often in my tests), we need to keep track of the just collected coin locations as well, and only make them free again after we spawned the next coin.
The coin manager would look like this:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CoinCountManager : MonoBehaviour
{
private const int DESIRED_COIN_COUNT = 2;
public int currentCoinCount = 0;
[SerializeField] private List<Vector3> spawnLocations; // fill this with spawn locations through the editor
[SerializeField] private CoinScript coinPrefab; // put your coin-prefab in here
public List<Vector3> freeLocations;
public List<Vector3> occupiedLocations;
public List<Vector3> justCollectedLocations;
// Makes this single instance globally accessable everywhere by calling CoinCountManager.GetInstance()
private static CoinCountManager singletonInstance;
public static CoinCountManager GetInstance()
{
return singletonInstance;
}
private void Start()
{
singletonInstance = this;
freeLocations = spawnLocations;
occupiedLocations = new List<Vector3>();
justCollectedLocations = new List<Vector3>();
}
public void Update()
{
if (currentCoinCount < DESIRED_COIN_COUNT)
{
int randIndex = (int)Random.Range(0, freeLocations.Count); // get number between 0 and size of free location list
Vector3 randSpawn = freeLocations[randIndex];
freeLocations.Remove(randSpawn); // remove spawn location from free locations
occupiedLocations.Add(randSpawn); // and add to it to occupied locations
GameObject newCoin = Instantiate(coinPrefab.gameObject, randSpawn, Quaternion.identity); // instantiate new coin at that location
currentCoinCount++; // increase current coin count
}
// Prevents that we can spawn at locations we just collected from
if (justCollectedLocations.Count > 0)
{
foreach (Vector3 vec in justCollectedLocations)
{
occupiedLocations.Remove(vec);
freeLocations.Add(vec);
}
justCollectedLocations.Clear();
}
}
public static void RemoveCoin(CoinScript coin)
{
CoinCountManager instance = CoinCountManager.GetInstance();
instance.justCollectedLocations.Add(coin.transform.position);
instance.currentCoinCount--;
}
}
Dont forget to fill in the spawn locations and coinPrefab through the editor.
For your coin prefabs you only need to attach a script with an onDestroy function like this:
public class CoinScript : MonoBehaviour
{
private void OnDestroy()
{
CoinCountManager.RemoveCoin(this);
}
}
So effectively, first all locations are free. Since we dont want that, the script spawns coins until the desired number of coins is reached, and adds those locations to the occupiedLocations, on which we cannot spawn coins anymore. When a coin gets destroyed (collected) it automatically calls RemoveCoin, which adds its location to the list of just collected coins, to prevent directly freeing its location, which allows a new coin to spawn there.
After the next update loop (new coin was spawned), this list is then cleared and the locations are removed from the list of occupied locations, and added to the list of free locations again.
Using some cubes and an empty game object for testing purposes, the above example does exactly what it should, assuming you manually delete the cubes. Hope the explanations were understandable.