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;
}
}
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.