Align Spawnpoints in Procedural generation - c#

I've created some boxes to recreate a 2D level where the dungeon is being created in a procedural generation. Altho when I run my script it creates the right levels, but it does not align the Spawnpoints to each other.
This causes an unlimited level generation (since the spawn points don't touch) but it also makes sure that there is no way for the character to go from 1 way to the other. I've made sure that every level is 10 x 10 and the spawn points are 10 pixels away from the middle of the rooms. I wanted to ensure collision between the spawn points but unfortunately, this is not the case.
I have created multiple rooms where each has their own exit (Spawn Point) depending on where the spawn point is, it can connect with another room that has the same spawn point (Room with a left exit is connected by a room with at least a right exit).
I put all of this in an int, declaring the number of rooms that are available.
Roomspawner code
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RoomSpawner : MonoBehaviour
{
public int openingDirection;
// 1 --> need bottom door
// 2 --> need top door
// 3 --> need left door
// 4 --> need right door
private RoomTemplates templates;
private int rand;
private bool spawned = false;
void Start(){
templates = GameObject.FindGameObjectWithTag("Rooms").GetComponent<RoomTemplates>();
Invoke("Spawn", 0.5f);
}
void Spawn(){
if(spawned == false){
if(openingDirection == 1){
// Need to spawn a room with a BOTTOM door.
rand = Random.Range(0, templates.bottomRooms.Length);
Instantiate(templates.bottomRooms[rand], transform.position, templates.bottomRooms[rand].transform.rotation);
} else if(openingDirection == 2){
// Need to spawn a room with a TOP door.
rand = Random.Range(0, templates.topRooms.Length);
Instantiate(templates.topRooms[rand], transform.position, templates.topRooms[rand].transform.rotation);
} else if(openingDirection == 3){
// Need to spawn a room with a LEFT door.
rand = Random.Range(0, templates.leftRooms.Length);
Instantiate(templates.leftRooms[rand], transform.position, templates.leftRooms[rand].transform.rotation);
} else if(openingDirection == 4){
// Need to spawn a room with a RIGHT door.
rand = Random.Range(0, templates.rightRooms.Length);
Instantiate(templates.rightRooms[rand], transform.position, templates.rightRooms[rand].transform.rotation);
}
spawned = true;
}
void OnTriggerEnter2D(Collider2D other){
if(other.CompareTag("SpawnPoint")){
if(other.GetComponent<RoomSpawner>().spawned == false && spawned == false){
// spawns walls blocking off any opening !
Instantiate(templates.closedRoom, transform.position, Quaternion.identity);
Destroy(gameObject);
}
spawned = true;
}
}
}
}
Room templates
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RoomTemplates : MonoBehaviour
{
public GameObject[] bottomRooms;
public GameObject[] topRooms;
public GameObject[] leftRooms;
public GameObject[] rightRooms;
public GameObject closedRoom;
}
I did a new test with a room where it has only 1 spawn point. I Paused the game the second the 2nd spawn point was created so you can see that the 2nd level is created a with a different height than the starting one.
Underneath you can see the connecting room spawning just above the starting one. By doing this it messes up all the rest of the rooms.
Instead I want it to be like this

It seems to me like the problem arises due to the way you instantiate the copies of the rooms.
They are all instantiated at transform.position but given that they appear to spawn at all sorts of places, I'd suggest that you make sure that the pivot point/center point of the prefabs/meshes of your Room Templates are aligned correctly.

Related

Spawn objects in unity

I'm writting a small 2D game in Unity with C#. I have built two obstaclespawner which spawn vertical lines. After the lines have been spawned, they move down. One of the spawner is located at the upper left edge and the other one at the upper right edge. Currently, new objects are spawned after a certain time. But my goal is, for example, to spawn a new object on the upper left edge when the object which is spawned on the top right has traveled a certain distance.
Could this possibly be done via the coordinates of the objects?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ObstacleSpawner : MonoBehaviour
{
public GameObject[] obstacles;
public List<GameObject> obstaclesToSpawn = new List <GameObject>();
int index;
void Awake()
{
InitObstacles();
}
// Start is called before the first frame update
void Start()
{
StartCoroutine (SpawnRandomObstacle ());
}
// Initialize obstacles
void InitObstacles()
{
index=0;
for(int i =0; i<obstacles.Length*3;i++){
GameObject obj = Instantiate(obstacles[index], transform.position, Quaternion.identity);
obstaclesToSpawn.Add(obj);
obstaclesToSpawn [i].SetActive (false);
index++;
if (index == obstacles.Length)
{
index= 0;
}
}
}
IEnumerator SpawnRandomObstacle()
{
//Wait a certain time
yield return new WaitForSeconds(3f);
}
//I want something like this
(if gameObject.x == -0.99){
//activate obstacles
int index = Random.Range(0, obstaclesToSpawn.Count);
while(true){
if (!obstaclesToSpawn[index].activeInHierarchy){
obstaclesToSpawn[index].SetActive(true);
obstaclesToSpawn [index].transform.position = transform.position;
break;
}else{
index = Random.Range (0, obstaclesToSpawn.Count);
}
}
StartCoroutine (SpawnRandomObstacle ());
}
}
As far as I understand, you need to save in each spawner the reference to other spawner.
public class ObstacleSpawner : MonoBehaviour
{
public ObstacleSpawner otherSpawner;
...
Then in spawner check the position of obstacle in the second spawner. Something like this:
...
if (otherSpawner.obstaclesToSpawn[someIndex].transform.position.x <= -0.99)
{
// Spawn new obstacle in this spawner...
...
}
Comparing position of an object to a certain position in the world is something that might work for you right now but may cause problems in the future if you ever try to change anything in the way your scene is set up.
You're looking for distance travelled by an object and you have everything necessary to calculate said distance.
The starting point for all your obstacles spawned by an ObstacleSpawner is the position of the ObstacleSpawner object so you don't need to cache the spawn position which makes things a lot easier.
You need a variable defining the distance after which you want to spawn another obstacle something like public float distBeforeNextObstacle = 1f, then you can compare this distance against the obstacle's distance from its spawn position (use Vector3 or Vector2, both have a Distance method, you should choose whatever fits your game best):
if(Vector3.Distance(obstaclesToSpawn[index].transform.position, transform.position)>=distBeforeNextObstacle)
{
//Spawn next obstacle
}

Spawning 2d objects randomly, but not overlapping with other objects

I'm working on a small game project for school in UNITY, specifically a clone of a snake game called Rattler Race. I'm pretty much a complete beginner with the engine, hence why I'm struggling a bit. The game we're suppose to make has to have 30 unique levels that have ascending complexity, something like this: Final level
My main problem at the moment is spawning food for the snake so it doesn't overlap with any inner walls or the borders.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SpawnFood : MonoBehaviour
{
public GameObject FoodPrefab;
public Transform BorderTop;
public Transform BorderBottom;
public Transform BorderLeft;
public Transform BorderRight;
public Transform Obstacle;
Vector2 pos;
// Use this for initialization
void Start ()
{
for (int i = 0; i < 10; i++) {
Spawn();
}
}
void Spawn()
{
pos.x = Random.Range(BorderLeft.position.x + 5,BorderRight.position.x - 5);
pos.y = Random.Range(BorderBottom.position.y + 5, BorderTop.position.y - 5);
Instantiate(FoodPrefab, pos, Quaternion.identity);
}
// Update is called once per frame
void Update ()
{
}
}
The code is very simple ofcourse because at the moment the game field is empty with no obstacles:
Current state
However my problem is if I was to scale up that tiny red obstacle, like so:
Big Obstacle
The food would spawn behind it(every level has 10 food objects) and be impossible to get. My idea was to build levels off of one object(the "Obstacle") and its copies, I don't really know if that idea is sound yet but I wanted to try.
If theres any way or any method to spawn food objects in locations so they don't overlap with the obstacle, like to check if the space is already occupied or a method that checks if a would be food object intersects with an existing obstacle, I would be very grateful if someone teaches me, because I'm really lost at the moment.
Assuming your food is exactly one unit long, you could have a coroutine which would check if there is something around that food gameObject, if there isn't anything, you can spawn it.
public LayerMask layerMask; //Additional variable so you can control which layer you don't want to be detected in raycast
void Spawn(){
StartCoroutine(GameObjectExists());
}
IEnumerator GameObjectExists(){
do{
yield return null;
pos.x = Random.Range(BorderLeft.position.x + 5,BorderRight.position.x - 5);
pos.y = Random.Range(BorderBottom.position.y + 5, BorderTop.position.y - 5);
}while(Physics2D.OverlapCircle(new Vector2(pos), 1f, layerMask); //1f is the radius of your food gameObject.
Instantiate(FoodPrefab, pos, Quaternion.identity);
yield return null;
}

Logic issue spawning a set number of game objects when game object is destroyed if collides with something?

I'm having a dilemma with a basic game in Unity with C# - I need to spawn 10 cubes in random locations, however once a cube is spawned I destroy it if it detects a collision with another game object in this script ATTACHED to the cube object:
void OnCollisionStay(Collision col){
if (col.gameObject.tag == "Maze" || col.gameObject.tag == "Player") {
//Destroy cube
Destroy(this.gameObject);
spawnCubes.numCubesExist--;
Debug.Log ("touching!!" + spawnCubes.numCubesExist);
spawnCubes.touchedMaze = true;
}
}
In another script in my scene I spawn the cubes here. I am using public static int numCubesExist to enumerate the number of cubes in the scene (although this doesn't work) and NUMTOBESPAWNED to set the number of cubes that SHOULD end up in the scene.
In the cube script, numCubesExist is decreased by 1 if collision is detected and when a cube is spawned by cubeSpawner (); I increment. My logic is that my while statement would keep spawning UNTIL numCubesExist == NUMTOBESPAWNED but this does not happen.
void Start () {
while(numCubesExist <= NUMTOBESPAWNED)
{
cubeSpawner ();
}
}
void cubeSpawner()
{
//Random textures
switch (Random.Range (0, 3)) {
case 0:
cube.GetComponent<Renderer> ().material = color1;
break;
case 1:
cube.GetComponent<Renderer> ().material = color2;
break;
case 2:
cube.GetComponent<Renderer> ().material = color3;
break;
case 3:
cube.GetComponent<Renderer> ().material = color4;
break;
}
//Random positions
if (UIManagerScript.sceneNum == 0) {
position = new Vector3 (Random.Range (7.0F, 26.0F), (float)1.4, Random.Range (-9.0F, 10.0F));
Debug.Log (position);
}
else if(UIManagerScript.sceneNum == 1) {
position = new Vector3 (Random.Range (7.0F, 47.0F), (float)1.4, Random.Range (-9.0F, 10.0F));
Debug.Log (position);
}
else if(UIManagerScript.sceneNum == 2) {
position = new Vector3 (Random.Range (-4.7F, 44.0F), (float)1.4, Random.Range (-23.0F, 25.0F));
Debug.Log (position);
}
//spawn
Instantiate (cube, position, Quaternion.identity);
Debug.Log ("spawned it!"+numCubesExist);
numCubesExist++;
}
My Debug.Log statements within the collision on the cube script are printed AFTER all the spawn statements are printed, so what I'm seeing is the cubes spawn the correct number, then half of them are destroyed and NOT REPLACED by the OnCollisionStay. So I'm always left with less cubes than NUMTOBESPAWNED.
I'm new to Unity- what is the best way to achieve this? Where should I call my while loop (In update just results in a million cubes)?
EDIT to enumerate cubes:
GameObject[] allObjects = GameObject.FindGameObjectsWithTag("blueCube");
foreach (GameObject go in allObjects) {
cubesInScene++;
}
Here's what I see, I hope this helps!
Your spawner does not guarantee the cube is not spawning right next (or into) a "Maze" or "Player" object, so most probably it happens every now and then and your cubes get destroyed right after they spawned. As "Nick Otten" highlights, your spawn-loop is already over when this happens so no new cubes are spawned in place of the destroyed ones.To solve this, you can do several things. Here's a few examples:
Create spawner GOs in the Scene and guarantee both "Maze" and "Player" are outside of it's pre-defined range (let's say a 10 units globe), as well as the cubes are guaranteed spawned inside; you can make it simple -as of coding- and add a collider to your spawners' "globe", and with IgnoreLayerCollision make unwanted items to repel ("stay outside") and others to fall through ("stay inside"). E.g. cubes repel and player(s) stay inside or vice versa.
You can apply a different collision mechanism in "spawning phase". I.e. on OnCollisionEnter, you make the objects repel / bounce-off / etc each other, instead of cubes getting destroyed. As soon as spawning phase is over, it is guaranteed nothing is touching and you can switch to "destroy mechanism" / "play mode"
Slow and an overhead so I don't recommend it, but you can keep spawning and spawning until all cubes stay (i.e. none gets destroyed after it was spawned)
Instead of "numCubesExist" and such, why don't you create a cube prefab queue (and hide/requeue them in script instead of destroy)? Here's a nice tutorial on how to do this: Object Pooling Tutorial (I also recommend Sebastian's other tutorials, he is doing a great job in teaching Unity)
In your cube spawner method, instead of a switch, why don't you do something like this?//class variable
private Material[] colorMats; //fill up in Start() with color mats.
//then in cubeSpawner:
cube.GetComponent<Renderer>().material = colorMats[Random.Range(0, colorMats.Length)];So whenever you make change to your materials (e.g. add a new one), you have to alter your initialization code only, at one place, in Start()

How to ensure objects are not spawned on top of one another (random spawn location)

I am creating an endless jumping game. I have created a lot of obstacles to spawn randomly for time and for spaces, but now I want to make the obstacles spawn not in the same place as the previous ones, because they are spawning but sometimes they can spawn on the top of the other or near the others or even inside them, so please help me!
code:
using UnityEngine;
using System.Collections;
public class SpawnObstacles : MonoBehaviour {
public GameObject[] Obstacle;
public float MINTObstacle;
public float MAXTObstacle;
public bool spawning = false;
public Transform pos;
void Update()
{
if (!spawning)
{
StartCoroutine("SpawnObstacle");
}
}
IEnumerator SpawnObstacle()
{
spawning = true;
yield return new WaitForSeconds(Random.Range(MINTObstacle, MAXTObstacle));
Vector2 finalposition = new Vector2(Random.Range(3,7), Random.Range(pos.position.y - 6f, pos.position.y - 6f));
Instantiate(Obstacle[Random.Range(0, Obstacle.Length)], finalposition, Quaternion.identity);
spawning = false;
}
}
I think what you want is some kind of bounding box around each obstacle that represents the "padding" that you want around that obstacle, where no other can be placed. You might use a primitive collider for this purpose.
When you create a new obstacle at a random position, you could check to see if its "padding" collides with another obstacle's "padding", and if so, re-calculate the random position.
One issue I foresee if if you have a very crowded space, this check might have to be re-done many many times before a valid location is found. You may want to limit the number of checks it can do, and if it doesn't find a valid spot it just doesn't spawn the object after 20 tries or so.

Unity: Enemy Spawn / Health System

I'm working at an enemy spawn system. This is my code:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class EnemyManager : MonoBehaviour
{
public GameObject shark; // prefab von Shark
public GameObject instanceShark; // globale Instanzvariable von Shark
public Transform[] spawnPoints; // An array of the spawn points this enemy can spawn from.
public float spawnTime = 3f; // How long between each spawn.
public int maximumSharks = 2;
private int currentSharks;
public int healthShark; // current Health von Shark
public int startinghealthShark = 200;
public float sinkSpeed = 2.5f;
bool isDead;
void Start ()
{
healthShark = startinghealthShark;
currentSharks = 0;
}
void Update ()
{
if (currentSharks <= maximumSharks) {
InvokeRepeating ("Spawn", spawnTime, spawnTime);
}
Debug.Log (currentSharks);
}
void Spawn ()
{
// Find a random index between zero and one less than the number of spawn points.
int spawnPointIndex = Random.Range (0, spawnPoints.Length);
// Create an instance of the enemy prefab at the randomly selected spawn point's position and rotation.
instanceShark = Instantiate (shark, spawnPoints[spawnPointIndex].position, spawnPoints[spawnPointIndex].rotation) as GameObject;
currentSharks++;
if (currentSharks >= maximumSharks) {
CancelInvoke("Spawn");
}
}
public void AddDamageToShark (int neuDamageWert) // Addiere zu damage. Public function, können andre scripts auch aufrufen
{
// If the enemy is dead...
if(isDead)
// ... no need to take damage so exit the function.
return;
healthShark -= neuDamageWert;
if (healthShark <= 0) { //tot
Death ();
}
Debug.Log (healthShark);
}
void Death ()
{
// The enemy is dead.
isDead = true;
currentSharks--;
Destroy (instanceShark);
Debug.Log ("dead?");
return;
}
What I want: Spawn enemies as long as maximum amount isn't reached (this part works so far) and destroy the enemy that has been shot and respawn another one (doesn't work).
This codes creates 2 sharks as enemies at the moment. The problem is when I damage one, only the last created instance gets destroyed even though I shot at the first shark. Also the health of the other instance and new spawning instance isn't affected at all.
I'd appreciate any advices, I spent ages with this code and it seems like I need some help regarding the instances - health logic.
Thank you very much
Split your spawn manager behaviour and your enemy behaviour and use interfaces to organize your code in a more SOLID approach. Turn your objects responsible for just one scope (Now your SpawnManager is current responsible for Enemy behaviours/responsabilities)
SpawnManager should be a singleton object with just one responsibility "to manage enemy spawn" with a maximunEnemyCount and a currentEnemyCount. You can spawn always when your currentEnemyCount < maximunEnemyCount.
OnTakeDamage() and OnDeath() should be interfaces of your enemy behaviour (in a separated script from SpawnManager, lets assume EnemyBehaviour script) and your destroy should target just the instance of itself Destroy(this.gameObject)
Remember to notify your SpawnManager when an enemy is dead on your OnDeath method to adjust enemyCurrentCount.
I think this is a more elegant way to do this task and less susceptible to bugs on managing enemy instances.
edited:
linked singleton references that I forgot before
you'll need a unique gameObject for manager that should know how much enemies can live and how many are living, nothing more about the enemies (enemy's health is a responsibility of enemy object in this case).
each enemy need to know when he/she dies and so notify the manager to decrement its counter.

Categories