Search code examples
c#unity-game-enginespawn

Prevent Spawning Prefabs At The Same Position In A Row


i am trying to develop a unity game which uses prefabs spawn at random locations from an array. The problem i am having is prefabs are spawning on the top of each other too many times. I have tried to prevent this from happening with the help of other topics not only here but also from google but i couldnt apply some methods to my code. So my goal is keep tracking last spawned object position and spawn next object at diffrent position from the array i have created within the obstacle script. Is there anyone who can help me?

This is my obstacle scripts which attached to prefabs.

public class obstacle : MonoBehaviour

{
    private Rigidbody targetRb;
    // Start is called before the first frame update
    private float minSpeed = 12;
    private float maxSpeed = 16;
    private float ySpawnPos = 6;
    private float NewPosition = -1.87f;
    private List<Vector3> spawnPositions = new List<Vector3>();
    private int index;
    public int offset = 1;
    


    void Start()
    {
      
        index = Random.Range(0, spawns.Length);
       
        transform.position = spawns[index];
        
        
       

    }

   

    private void OnTriggerEnter(Collider other)
    {
        Destroy(gameObject);
    }
    Vector3 RandomForce()
    {
        return Vector3.down * Random.Range(minSpeed, maxSpeed);
    }

     Vector3[] spawns = new[]
    //spawns = new Vector3[]
    { 
        new Vector3(-2.16f,7,0), 
        new Vector3(-0.67f,7,0), 
        new Vector3(0.75f,7,0), 
        new Vector3(2.22f,7,0) 
    };
}

This is my game manager script which spawn the prefabs.

using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

public class GameManager : MonoBehaviour

//spawnera alternatif daha güzel
{
    public List<GameObject> targets;
   
    // Start is called before the first frame update
    public int score;
    public TextMeshProUGUI scoreText;
    public TextMeshProUGUI gameOverText;
    public Button restartButton;
    public bool isGameActive;
    public float spawnRate = 3.0f;
    public Text highScore;
    public Text highestScore;
    
    

   

    void Start()
    {
        isGameActive = true;
        score = 0;
        UpdateScore(0);
        StartCoroutine(SpawnTarget());
        highScore.text = PlayerPrefs.GetInt("HighScore", 0).ToString();
       
        

    }
    IEnumerator  SpawnTarget()
    {
        while (isGameActive)
        {
            yield return new WaitForSeconds(spawnRate);
            int index = Random.Range(0, targets.Count);
            Instantiate(targets[index]);


            UpdateScore(5); 

            if(score > PlayerPrefs.GetInt("HighScore",0))
            {
                PlayerPrefs.SetInt("HighScore", score);
                highScore.text = score.ToString();
            }
            
           
        }
    }
    // Update is called once per frame
    public void UpdateScore(int scoreToAdd)
    {

        score += scoreToAdd;
        scoreText.text = "Score: " + score;

    }

    public void GameOver()
    {
        restartButton.gameObject.SetActive(true);
        gameOverText.gameObject.SetActive(true);
        isGameActive = false;
        Time.timeScale = 0;
        highScore.gameObject.SetActive(true);
        highestScore.gameObject.SetActive(true);


    }
    public void RestartGame()
    {
        SceneManager.LoadScene(SceneManager.GetActiveScene().name);
        Time.timeScale = 1;
        
        
    }
     
  }
  


Solution

  • A stack or queue might be a bit overkill, so instead, I'll just keep an iterator and check if we are exceeding the bounds of your array. I am also going to move the initial placement to the GameManager so it can manage which positions are left in the spawn list locations.

        // move the array of locations to the GameManager
        Vector3[] spawns = new[]
        { 
            new Vector3(-2.16f,7,0), 
            new Vector3(-0.67f,7,0), 
            new Vector3(0.75f,7,0), 
            new Vector3(2.22f,7,0) 
        };
        
        private int currentSpawnItr = 0;
        private int maxSpawnItr = 0;
    
        private System.Random rndm;
    
        private void Start()
        {
             rndm = new System.Random();
             maxSpawnItr = spawns.Length;
             ShufflePositions();
        }
    
        IEnumerator  SpawnTarget()
        {
            while (isGameActive)
            {
                yield return new WaitForSeconds(spawnRate);
                int index = Random.Range(0, targets.Count);
                GameObject tmpObstacle = Instantiate(targets[index]);
    
                tmpObstacle.transform.position = GetNextPosition();
    
                UpdateScore(5); 
    
                if(score > PlayerPrefs.GetInt("HighScore",0))
                {
                    PlayerPrefs.SetInt("HighScore", score);
                    highScore.text = score.ToString();
                }
            }
        }
    
        private Vector3 GetNextPosition()
        {
             // when we reach the bounds of our spawn array length, shuffle it again
             // and reset our iterator
             if(currentSpawnItr == maxSpawnItr)
             {
                 currentSpawnItr = 0;
                 ShufflePositions();
             }
    
             ++currentSpawnItr;
             return spawns[currentSpawnItr-1];
        }
    
        private void ShufflePositions()
        {
             Vector3 prevLast = spawns[maxSpawnItr-1];
             spawns = spawns.OrderBy(spawn => rndm.Next()).ToArray();
             
             // when our new first spawn was our old last spawn
             if(spawns[0] == prevLast)
             {
                  // randomly pick an index to swap our first element to
                  int randomIdx = rndm.Next(1, maxSpawnItr);
                  spawns[0] = spawns[randomIdx];
                  spawns[randomIdx] = prevLast;
             }
        }
    

    I have not tested the above snippet. It is more the direction you could take this. If you have issues implementing this let me know.