I use Two scripts for generation, but, sometimes it gets stuck and doesn't generate more rooms even when being way under the minimum rooms demanded. (I implemented a seed system to replicate the problem and try to solve it, just grab a seed with a problem and introduce it into the variable)
And example of stuck situations is:(T is start room and O are rooms)
OO..
OTO
..OO
or
OOO...
O.....TO
OOOO
I don't know if it's a timing issue with function calls or some variable not working alright.
Roombinger.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
public class Roombinger : MonoBehaviour
{
public Camera MapCam;
public GameObject StartRoom;
public List<GameObject> Roomi;
public GameObject ShopRoom;
public GameObject BossRoom;
private int currroomdooramount;
public Roomathon currRoom;
public Vector2Int startpos = new Vector2Int(3, 5);
public int minrooms;
public bool updated;
public bool specials = false;
public List<bool> childrengeneration = new List<bool>();
public int seed;
public static Roombinger instance;
bool IsLoadingRoom = false;
public string currentWorldName = "Basement";//Get name from dictionary
public Vector3 StartRoomPos;
public bool BossSpawned;
public bool ShopSpawned;
[SerializeField]
public List<Roomathon> allChildren = new List<Roomathon>();
[SerializeField]
public List<Roomathon> Deadends = new List<Roomathon>();
void Awake()
{
instance = this;
}
public int GetchildrenCount()
{
return allChildren.Count;
}
public bool DoesRoomExist(float x, float y)
{
return allChildren.Find(item => item.X == x && item.Y == y) != null;
}
public Roomathon FindRoom(float x, float y)
{
return allChildren.Find(item => item.X == x && item.Y == y);
}
public void OnPlayerEnterRoomo(Roomathon room)
{
CameraController1.instance.currRoom = room;
currRoom = room;
}
// Start is called before the first frame update
void Start()
{
var temp = Instantiate(StartRoom, new Vector3(0, 0, 0), Quaternion.identity, transform).GetComponent<Roomathon>();
temp.X = 3;
temp.Y = 5;
temp.name = currentWorldName + "-" + "Start" + " " + temp.GetComponent<Roomathon>().X + ", " + temp.GetComponent<Roomathon>().Y;
StartRoomPos = new Vector3(temp.X * temp.Width, temp.Y * temp.Height, 0);
// comment this initialization out if setting up a specific seed(only numbers) manually
seed = Random.Range(0, int.MaxValue);
Random.InitState(seed);
}
// Update is called once per frame
void Update()
{
Random.InitState(seed);
allChildren = transform.Cast<Transform>().Select(t => t.gameObject.GetComponent<Roomathon>()).ToList();
childrengeneration = new List<bool>();
foreach (Roomathon child in allChildren)
{
childrengeneration.Add(child.isgenerating);
}
if (!childrengeneration.Contains(true) && allChildren.Count < minrooms)
{
// Only set the generating flag for rooms that are not generating
foreach (Roomathon child in allChildren)
{
if (!child.isgenerating)
{
child.isgenerating = true;
}
}
}
if (allChildren.Count < minrooms)
{
foreach (Roomathon child in allChildren)
{
if (!child.canGenerateMore)
{
//if (Random.value < 0.75f)
//{
child.canGenerateMore = true;
//}
}
}
}
if (allChildren.Count > minrooms)
{
foreach (Roomathon child in allChildren)
{
child.isgenerating = false;
}
}
if (!specials && !childrengeneration.Contains(true) && allChildren.Count >= minrooms && updated)
{
Debug.Log("splecia");
/*foreach (Roomathon child in allChildren)
{
if(child.ds.Count == 4)
{
}
}*/
StartCoroutine(SpecialsSpawn());
if (BossSpawned && ShopSpawned)
{
specials = true;
}
}
if ((allChildren.Count >= minrooms&& !updated && !childrengeneration.Contains(true)))
{
//Debug.Log("Childrends??"+ (allChildren.Count == minrooms - 1));
//allChildren.OrderBy(v => (v.X)).ToList();
foreach (Roomathon child in allChildren)
{
//Debug.Log("toremove");
child.RemoveUnconnectedDoors();
if (child.deadend)
{
/*if (Deadends.Contains(child))
{
Deadends.Remove(child);
}*/
//Debug.Log("if room has deads " + child);
Deadends.Add(child);
}
if (Deadends.Count >= 1) {
updated = true;
}
if (!child.canGenerateMore)
{
child.isgenerating = false;
}
}
}
if (allChildren.Count >= minrooms)
{
foreach (Roomathon child in allChildren)
{
child.isgenerating = false;
}
}
/*if(allChildren.Count == minrooms - 1 && !childrengeneration.Contains(false))
{
Debug.Log("Childrends??" + (allChildren.Count == minrooms - 1));
foreach (Roomathon child in allChildren)
{
Debug.Log("toremove");
child.RemoveUnconnectedDoors();
if (child.deadend)
{
if (Deadends.Contains(child))
{
Deadends.Remove(child);
}
Debug.Log("if room has deads " + child);
Deadends.Add(child);
}
if (Deadends.Count >= 1)
{
updated = true;
}
}
}*/
//Debug.Log(allChildren.Count);
}
public GameObject GetaRoom()
{
return Roomi[Random.Range(0, Roomi.Count)].gameObject;
}
IEnumerator SpecialsSpawn()
{
Roomathon temp = null;
//Debug.Log("Specialsss");
var Deadendsposs = new List<Transform>();
var Deadendsdiff = new List<Vector3>();
foreach (Roomathon child in Deadends)
{
Deadendsposs.Add(child.transform);
Deadendsdiff.Add(child.transform.position - StartRoomPos);
}
Deadendsdiff = Deadendsdiff.OrderBy(v => (Mathf.Abs(v.x) + Mathf.Abs(v.y) + Mathf.Abs(v.z))).ToList();
Deadends = Deadends.OrderBy(v => (Mathf.Abs(v.transform.position.x) + Mathf.Abs(v.transform.position.y) + Mathf.Abs(v.transform.position.z))).ToList();
yield return new WaitForSeconds(0.07f);
Debug.Log("dd");
//Debug.Log("trying" + (!BossSpawned && Deadends.Count != 0));
//Debug.Log("Boss?" + !BossSpawned);
//Debug.Log("No counts?" + (Deadends.Count != 0));
if (!BossSpawned && Deadends.Count != 0 && allChildren.Count >= minrooms)
{
BossSpawned = true;
Debug.Log("Boss?");
Deadends = Deadends.OrderBy(v => (StartRoomPos - new Vector3(Mathf.Abs(v.transform.position.x), Mathf.Abs(v.transform.position.y), Mathf.Abs(v.transform.position.z))).magnitude).ToList();
//Deadendsdiff = Deadendsdiff.OrderBy(v => (Mathf.Abs(v.x) + Mathf.Abs(v.y) + Mathf.Abs(v.z))).ToList();
yield return new WaitForSeconds(0.1f);
var nn = Deadends[0];
Debug.Log(nn);
yield return new WaitForSeconds(0.2f);
temp = Instantiate(BossRoom, new Vector3(0, 0, 0), Quaternion.identity, transform).GetComponent<Roomathon>();
temp.X = nn.X;
temp.Y = nn.Y;
temp.name = currentWorldName + "-" + "Boss" + " " + nn.X + ", " + nn.Y;
Deadends.Remove(nn);
//Deadends.Add(temp);
Destroy(nn.gameObject);
temp.RemoveUnconnectedDoors();
Debug.Log("Removed");
}
/*if (temp != null)
{
Deadendsdiff.Remove(Deadendsdiff[Deadendsdiff.Count]);
}*/
yield return new WaitForSeconds(0.5f);
if (!ShopSpawned && Deadends.Count != 0 && allChildren.Count >= minrooms && BossSpawned)
{
ShopSpawned = true;
Debug.Log("Shop?");
Deadends = Deadends.OrderBy(v => (StartRoomPos - new Vector3(Mathf.Abs(v.transform.position.x), Mathf.Abs(v.transform.position.y), Mathf.Abs(v.transform.position.z))).magnitude).ToList();
//Deadendsdiff = Deadendsdiff.OrderBy(v => (Mathf.Abs(v.x) + Mathf.Abs(v.y) + Mathf.Abs(v.z))).ToList();
yield return new WaitForSeconds(0.1f);
var nn = Deadends[0];
Debug.Log(nn);
yield return new WaitForSeconds(0.2f);
temp = Instantiate(ShopRoom, new Vector3(0, 0, 0), Quaternion.identity, transform).GetComponent<Roomathon>();
temp.X = nn.X;
temp.Y = nn.Y;
temp.name = currentWorldName + "-" + "Shop" + " " + nn.X + ", " + nn.Y;
Deadends.Remove(nn);
//Deadends.Add(temp);
Destroy(nn.gameObject);
temp.RemoveUnconnectedDoors();
Debug.Log("Removed2");
yield return new WaitForSeconds(0.2f);
}
/*if (temp != null)
{
Deadendsdiff.Remove(Deadendsdiff[Deadendsdiff.Count]);
}*/
}
void mapcampos()
{
var temp = new Vector3(0,0,-154.7f);
allChildren.OrderBy(v => (v.X)).ToList();
Debug.Log(((allChildren[0].transform.position) + ((allChildren[0].transform.position - allChildren[allChildren.Count - 1].transform.position) / 2)).x);
temp.x = ((allChildren[0].transform.position) + ((allChildren[0].transform.position - allChildren[allChildren.Count - 1].transform.position) / 2)).x;
allChildren.OrderBy(v => (v.Y)).ToList();
Debug.Log(((allChildren[0].transform.position) + ((allChildren[0].transform.position - allChildren[allChildren.Count - 1].transform.position) / 2)).y);
temp.y= ((allChildren[0].transform.position) + ((allChildren[0].transform.position - allChildren[allChildren.Count - 1].transform.position) / 2)).y;
Debug.Log(temp);
MapCam.transform.position = temp;
}
}
Roomathon.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
public class Roomathon : MonoBehaviour
{
[Header("Ingame Properties")]
public Vector2 SwlextedNeighbor2 = Vector2.zero;
public Vector2 SwlextedNeighbor = Vector2.zero;
public List<Vector2> availableNeighbors2;
public List<Vector2> availableNeighbors;
private bool isCoroutineRunning = false;
public bool canGenerateMore = true;
public List<GameObject> Neighbors2;
public List<GameObject> Neighbors;
public bool isgenerating = true;
public bool deadend = false;
public List<Door> ds;
public int Height;
public int Width;
public int X;
public int Y;
[Header("Outer Properties")]
public List<Door> doors = new List<Door>();
public SpriteRenderer MapSprite;
public Door bottomDoor;
public Door rightDoor;
public Door leftDoor;
public Door topDoor;
// Start is called before the first frame update
void Start()
{
transform.position = new Vector3(X * Width, Y * Height, 0);
availableNeighbors2 = new List<Vector2>();
availableNeighbors = new List<Vector2>();
Neighbors = new List<GameObject>();
if (Roombinger.instance == null)
{
Debug.Log("No controller instance found");
return;
}
// add Doors to ds
Door[] ds = GetComponentsInChildren<Door>();
foreach (Door d in ds)
{
doors.Add(d);
switch (d.doorType)
{
case Door.DoorType.right:
rightDoor = d;
break;
case Door.DoorType.left:
leftDoor = d;
break;
case Door.DoorType.top:
topDoor = d;
break;
case Door.DoorType.bottom:
bottomDoor = d;
break;
}
}
StartCoroutine(RoomGenerationCoroutine());
}
public int SearchNeighbors(int x, int y)
{
// Clear relevant lists
availableNeighbors.Clear();
SwlextedNeighbor = Vector2.zero;
Neighbors.Clear();
// Each Findroom removes and adds the item to avoid dupes
if (Roombinger.instance.FindRoom(x + 1, y) != null)
{
//Debug.Log(Roombinger.instance.FindRoom(x + 1, y).gameObject+"roomj");
Neighbors.RemoveAll(item => item == Roombinger.instance.FindRoom(x + 1, y).gameObject);
Neighbors.Add(Roombinger.instance.FindRoom(x + 1, y).gameObject);
}
else
// If there isn't a room add to availables
{
availableNeighbors.Add(new Vector2(x + 1, y));
}
if (Roombinger.instance.FindRoom(x - 1, y) != null)
{
Neighbors.RemoveAll(item => item == Roombinger.instance.FindRoom(x - 1, y).gameObject);
Neighbors.Add(Roombinger.instance.FindRoom(x - 1, y).gameObject);
}
else
{
availableNeighbors.Add(new Vector2(x - 1, y));
}
if (Roombinger.instance.FindRoom(x, y + 1) != null)
{
Neighbors.RemoveAll(item => item == Roombinger.instance.FindRoom(x, y+1).gameObject);
Neighbors.Add(Roombinger.instance.FindRoom(x, y + 1).gameObject);
}
else
{
availableNeighbors.Add(new Vector2(x, y + 1));
}
if (Roombinger.instance.FindRoom(x, y - 1) != null)
{
Neighbors.RemoveAll(item => item == Roombinger.instance.FindRoom(x, y-1).gameObject);
Neighbors.Add(Roombinger.instance.FindRoom(x, y - 1).gameObject);
}
else
{
availableNeighbors.Add(new Vector2(x, y - 1));
}
if (availableNeighbors.Count > 0)
{
SwlextedNeighbor = availableNeighbors[Random.Range(0, availableNeighbors.Count)];
if (Roombinger.instance.DoesRoomExist(SwlextedNeighbor.x, SwlextedNeighbor.y))
{
availableNeighbors.Remove(SwlextedNeighbor);
}
else
{
SwlextedNeighbor = availableNeighbors[Random.Range(0, availableNeighbors.Count)];
}
}
return Neighbors.Count;
}
public int SearchNeighbors2(int x, int y)
{
availableNeighbors2.Clear();
SwlextedNeighbor2 = Vector2.zero;
Neighbors2.Clear();
if (Roombinger.instance.FindRoom(x + 1, y) != null)
{
//Debug.Log(Roombinger.instance.FindRoom(x + 1, y).gameObject+"roomj");
Neighbors2.RemoveAll(item => item == Roombinger.instance.FindRoom(x + 1, y).gameObject);
Neighbors2.Add(Roombinger.instance.FindRoom(x + 1, y).gameObject);
}
else
{
availableNeighbors2.Add(new Vector2(x + 1, y));
}
if (Roombinger.instance.FindRoom(x - 1, y) != null)
{
Neighbors2.RemoveAll(item => item == Roombinger.instance.FindRoom(x - 1, y).gameObject);
Neighbors2.Add(Roombinger.instance.FindRoom(x - 1, y).gameObject);
}
else
{
availableNeighbors2.Add(new Vector2(x - 1, y));
}
if (Roombinger.instance.FindRoom(x, y + 1) != null)
{
Neighbors2.RemoveAll(item => item == Roombinger.instance.FindRoom(x, y + 1).gameObject);
Neighbors2.Add(Roombinger.instance.FindRoom(x, y + 1).gameObject);
}
else
{
availableNeighbors2.Add(new Vector2(x, y + 1));
}
if (Roombinger.instance.FindRoom(x, y - 1) != null)
{
Neighbors2.RemoveAll(item => item == Roombinger.instance.FindRoom(x, y - 1).gameObject);
Neighbors2.Add(Roombinger.instance.FindRoom(x, y - 1).gameObject);
}
else
{
availableNeighbors2.Add(new Vector2(x, y - 1));
}
if (availableNeighbors2.Count > 0)
{
SwlextedNeighbor2 = availableNeighbors2[Random.Range(0, availableNeighbors2.Count)];
if (Roombinger.instance.DoesRoomExist(SwlextedNeighbor2.x, SwlextedNeighbor2.y))
{
availableNeighbors2.Remove(SwlextedNeighbor2);
}
else
{
SwlextedNeighbor2 = availableNeighbors2[Random.Range(0, availableNeighbors2.Count)];
}
}
return Neighbors2.Count;
}
private IEnumerator RoomGenerationCoroutine()
{
isCoroutineRunning = true;
while (isgenerating && canGenerateMore)
{
Debug.Log("InLoop");
bool isDeadEnd = SearchNeighbors(X, Y) == 1;
if (isDeadEnd && Random.value < 0.25f)
{
canGenerateMore = false; // The room cannot generate more
}
// 50% chance to stop
if ((Random.value > 0.5f))
{
isgenerating = false;
}
// If total room number is greater or equal to minrooms stop generating
if (Roombinger.instance.allChildren.Count >= Roombinger.instance.minrooms)
{
isgenerating = false;
}
var maxNeighbor1 = 1;
// If start room allow up to 3 neighbors
if (X == 3 && Y == 5 && SearchNeighbors(X, Y) < 4)
{
maxNeighbor1 = 3;
}
// if not start room only allow one neighbor (past room)
else if (X != 3 && Y != 5)
{
maxNeighbor1 = 1;
}
//check neighbors
if (SearchNeighbors(X, Y) <= maxNeighbor1)
{
// Check if there isn't any existing room at selected place
yield return new WaitForSeconds(0.2f);
if (!Roombinger.instance.DoesRoomExist(SwlextedNeighbor.x, SwlextedNeighbor.y))
{
// If the isn't any existing room there total rooms are minor or equal to minrooms
yield return new WaitForSeconds(0.2f);
if (!Roombinger.instance.DoesRoomExist(SwlextedNeighbor.x, SwlextedNeighbor.y) && Roombinger.instance.GetchildrenCount() <= Roombinger.instance.minrooms)
{
// Search if neighbors of selected place is minor or equal to 1 and total rooms are minor than minrooms (task takes times needs WaitForseconds or wont work correctly)
var tmmm = ((SearchNeighbors2((int)SwlextedNeighbor.x, (int)SwlextedNeighbor.y) <= 1) && (Roombinger.instance.allChildren.Count<Roombinger.instance.minrooms));
yield return new WaitForSeconds(0.2f);
if (tmmm)
{
// Get a random room from list of variants and instantiate on selected place with corresponding name as child of RoomManager, end with adding the room to allchildren list
var ChosenRoom = Roombinger.instance.GetaRoom();
var temp = Instantiate(ChosenRoom, new Vector3(SwlextedNeighbor.x, SwlextedNeighbor.y, 0), Quaternion.identity, transform.parent);
temp.GetComponent<Roomathon>().X = (int)SwlextedNeighbor.x;
temp.GetComponent<Roomathon>().Y = (int)SwlextedNeighbor.y;
temp.name = Roombinger.instance.currentWorldName + "-" + temp.name + " " + temp.GetComponent<Roomathon>().X + ", " + temp.GetComponent<Roomathon>().Y;
//Debug.Log(Roombinger.instance.currentWorldName + "-" + temp.name + " " + temp.GetComponent<Roomathon>().X + ", " + temp.GetComponent<Roomathon>().Y + " spawned");
temp.transform.parent = transform.parent;
Roombinger.instance.allChildren.Add(temp.GetComponent<Roomathon>());
}
}
}
}
}
isCoroutineRunning = false;
}
// Update is called once per frame
void Update()
{
/*if (!isCoroutineRunning && Roombinger.instance.GetchildrenCount() < Roombinger.instance.minrooms)
{
isgenerating = true;
StartCoroutine(RoomGenerationCoroutine());
}*/
}
// Draw Room bounds in editor
void OnDrawGizmos()
{
Gizmos.color = Color.green;
Gizmos.DrawWireCube(transform.position, new Vector3(Width, Height, 0));
}
public Vector3 GetRoomCenter()
{
return new Vector2(X * Width, Y * Height);
}
// Change Mapsprite color to selected and change currRoom
void OnTriggerEnter2D(Collider2D other)
{
if (other.tag == "Player")
{
Roombinger.instance.OnPlayerEnterRoomo(gameObject.GetComponent<Roomathon>());
var teuump = MapSprite.color;
teuump.a =1f;
MapSprite.color = teuump;
}
}
// Change Mapsprite color to not selected
void OnTriggerExit2D(Collider2D other)
{
if (other.tag == "Player")
{
var teuump = MapSprite.color;
teuump.a = 0.35f;
MapSprite.color = teuump;
}
}
// Add all Door items to ds and for each one if a room isn't found in its direction setactive false and remove them from temp list that gets pushed into ds later and if ds doors is 1 then mark room as deadend
public void RemoveUnconnectedDoors()
{
ds = GetComponentsInChildren<Door>().ToList();
List<Door> doorsToRemove = new List<Door>(); // Create a list to hold doors to be removed
foreach (Door door in ds)
{
switch (door.doorType)
{
case Door.DoorType.right:
if (GetRight() == null)
{
door.gameObject.SetActive(false);
doorsToRemove.Add(door); // Add the door to the removal list
}
break;
case Door.DoorType.left:
if (GetLeft() == null)
{
door.gameObject.SetActive(false);
doorsToRemove.Add(door); // Add the door to the removal list
}
break;
case Door.DoorType.top:
if (GetTop() == null)
{
door.gameObject.SetActive(false);
doorsToRemove.Add(door); // Add the door to the removal list
}
break;
case Door.DoorType.bottom:
if (GetBottom() == null)
{
door.gameObject.SetActive(false);
doorsToRemove.Add(door); // Add the door to the removal list
}
break;
}
}
// Now remove the doors that were marked for removal
foreach (Door doorToRemove in doorsToRemove)
{
ds.Remove(doorToRemove);
}
if (ds.Count == 1)
{
deadend = true;
}
}
public Roomathon GetLeft()
{
if (Roombinger.instance.DoesRoomExist(X - 1, Y))
{
return Roombinger.instance.FindRoom(X - 1, Y);
}
return null;
}
public Roomathon GetTop()
{
if (Roombinger.instance.DoesRoomExist(X, Y + 1))
{
return Roombinger.instance.FindRoom(X, Y + 1);
}
return null;
}
public Roomathon GetBottom()
{
if (Roombinger.instance.DoesRoomExist(X, Y - 1))
{
return Roombinger.instance.FindRoom(X, Y - 1);
}
return null;
}
public Roomathon GetRight()
{
if (Roombinger.instance.DoesRoomExist(X + 1, Y))
{
return Roombinger.instance.FindRoom(X + 1, Y);
}
return null;
}
}
I've tried messing with max neighbor variables and waitforseconds delays and changing variables conditions but nothing is working.