Unity 3d Level Generator

Hi there, its my first time posting so idk how to go abt this,

Im currently developing a 3d first person game where each level contains multiple rooms, and what i need is a way to generate levels with a random amount of rooms (which are chosen by criteria which are chosen by a give, not important criteria, so rn just random). This ammount cant surpass the max amount of rooms, for example 15 in the first level.

They need to generate door to door to each other and are supposed to generate on an imaginary 2d grid (birds view) and i havnt found any turtorial (and trust me ive been trying hard) that covers 3d room generation door to door on a 2d grid.

I would appreciate direct help, or if not, just a link to a turtorial / guide that i (almost certainly) havnt gotten reccomended. Thx in advance :slight_smile:

Welcome to procedural generation.

It’s a huge field with millions of approaches and ways to try and do stuff.

A good way to start is by the simplest way possible: spawn cubes in code, scale them, position them, spawn your player on them.

If any of that sounds unfamiliar, get busy with some tutorials immediately. If tutorials don’t work for you, nothing typed in this little text box will either.

Two steps to tutorials and / or example code:

  1. do them perfectly, to the letter (zero typos, including punctuation and capitalization)
  2. stop and understand each step to understand what is going on.

If you go past anything that you don’t understand, then you’re just mimicking what you saw without actually learning, essentially wasting your own time. It’s only two steps. Don’t skip either step.

Here’s my handy package of random procgen stuff:

MakeGeo is presently hosted at these locations:

https://bitbucket.org/kurtdekker/makegeo

Imphenzia: How Did I Learn To Make Games:

thx for ur reply, ive alr tried using grids and also spawning platforms to detremine the places for rooms, but they just generate, they dont get random generation, ive tried giving them factors to make it random, tried to do something with pathfinding, but nothing really worked out. Its not like im just coping stuff :slight_smile: thx for the reply again!


using UnityEngine;
using System.Collections;
using System.Collections.Generic;

[System.Serializable]
public class RoomType
{
    public string name;
    public int amount;
    public GameObject roomPrefab;
}

public class GridGeneration : MonoBehaviour
{
    public int gridWidth = 5;
    public int gridHeight = 5;
    public float cellSize = 1.0f;
    public float cellSeparation = 0.1f;
    public float gridYPosition = 0.0f;
    public GameObject emptyCellPrefab;
    public List<RoomType> roomTypes;

    private string[,] grid;
    private Dictionary<string, GameObject> placedRooms = new Dictionary<string, GameObject>();
    private Dictionary<string, Color> roomColors = new Dictionary<string, Color>();

    void Start()
    {
        grid = new string[gridWidth, gridHeight];
        GenerateGrid();

        StartCoroutine(PlaceRoomsFromListCoroutine());
        StartCoroutine(VisualizeGridCoroutine());
    }

    void GenerateGrid()
    {
        for (int row = 0; row < gridHeight; row++)
        {
            for (int col = 0; col < gridWidth; col++)
            {
                grid[col, row] = "empty";
            }
        }
    }

    IEnumerator PlaceRoomsFromListCoroutine()
    {
        foreach (RoomType room in roomTypes)
        {
            if (!roomColors.ContainsKey(room.name))
            {
                roomColors[room.name] = new Color(Random.value, Random.value, Random.value);
            }

            for (int i = 0; i < room.amount; i++)
            {
                bool placed = false;

                for (int attempt = 0; attempt < 100 && !placed; attempt++) // Limit retries
                {
                    int randomCol = Random.Range(0, gridWidth);
                    int randomRow = Random.Range(0, gridHeight);

                    if (grid[randomCol, randomRow] == "empty")
                    {
                        for (int rotation = 0; rotation < 4; rotation++) // Try all rotations
                        {
                            if (CanPlaceRoom(randomCol, randomRow, room.roomPrefab, rotation))
                            {
                                PlaceRoom(randomCol, randomRow, room.roomPrefab, rotation, room.name);
                                placed = true;
                                break;
                            }
                        }
                    }
                }

                if (!placed)
                {
                    Debug.LogWarning($"Skipped placing room {room.name} after multiple attempts.");
                }

                yield return null; // Wait for next frame
            }
        }
    }

    bool CanPlaceRoom(int col, int row, GameObject roomPrefab, int rotation)
    {
        GameObject tempRoom = Instantiate(roomPrefab, Vector3.zero, Quaternion.Euler(0, rotation * 90, 0));
        RoomValidator validator = tempRoom.GetComponent<RoomValidator>();

        if (validator == null)
        {
            Debug.LogError($"RoomPrefab '{roomPrefab.name}' is missing a RoomValidator component!");
            Destroy(tempRoom);
            return false;
        }

        bool valid = true;

        foreach (Transform opening in validator.openings)
        {
            Vector3 worldPos = tempRoom.transform.TransformPoint(opening.localPosition);
            Vector3Int gridPos = WorldToGrid(worldPos);

            if (!IsValidGridPosition(gridPos) || grid[gridPos.x, gridPos.z] != "empty")
            {
                valid = false;
                break;
            }
        }

        foreach (Transform wall in validator.walls)
        {
            Vector3 worldPos = tempRoom.transform.TransformPoint(wall.localPosition);
            Vector3Int gridPos = WorldToGrid(worldPos);

            if (IsValidGridPosition(gridPos) && grid[gridPos.x, gridPos.z] != "empty")
            {
                valid = false;
                break;
            }
        }

        Destroy(tempRoom);
        return valid;
    }

    void PlaceRoom(int col, int row, GameObject roomPrefab, int rotation, string roomName)
    {
    Vector3 position = GridToWorld(col, row);
    GameObject room = Instantiate(roomPrefab, position, Quaternion.Euler(0, rotation * 90, 0));

    // Scale the room based on the cell size (instead of using Renderer)
    room.transform.localScale = new Vector3(cellSize, cellSize, cellSize); // Adjust XYZ as needed

    grid[col, row] = roomName;
    placedRooms[$"{col},{row}"] = room;
    }



    Vector3Int WorldToGrid(Vector3 worldPosition)
{
    float startX = transform.position.x - (gridWidth * 0.5f * (cellSize + cellSeparation));
    float startZ = transform.position.z - (gridHeight * 0.5f * (cellSize + cellSeparation));

    int col = Mathf.RoundToInt((float)((worldPosition.x - startX) / (cellSize + cellSeparation))); // Explizites Casting auf float
    int row = Mathf.RoundToInt((float)((worldPosition.z - startZ) / (cellSize + cellSeparation))); // Explizites Casting auf float

    return new Vector3Int(col, 0, row);
}


    Vector3 GridToWorld(int col, int row)
    {
        float startX = transform.position.x - (gridWidth * 0.5f * (cellSize + cellSeparation));
        float startZ = transform.position.z - (gridHeight * 0.5f * (cellSize + cellSeparation));

        float x = startX + col * (cellSize + cellSeparation);
        float z = startZ + row * (cellSize + cellSeparation);

        return new Vector3(x, gridYPosition, z);
    }

    bool IsValidGridPosition(Vector3Int gridPos)
    {
        return gridPos.x >= 0 && gridPos.x < gridWidth && gridPos.z >= 0 && gridPos.z < gridHeight;
    }
IEnumerator VisualizeGridCoroutine()
{
    // Create a copy of the dictionary values or keys to avoid modifying the collection while iterating
    var rooms = new List<GameObject>(placedRooms.Values);

    foreach (var room in rooms)
    {
        // If you need any other visualization, you can directly modify the room transform here
        // For example, you could add a Debug.Log or change the position/scale dynamically
        yield return null;
    }
}


}


using UnityEngine;
using System.Collections.Generic;
public class RoomValidator : MonoBehaviour
{
    public List<Transform> openings; // Points for door connections
    public List<Transform> walls;    // Points for wall collision checks
}

this is my current script :smiley: rn its such a mess, it either says it cant place rooms or just places them facing wall to opening, looking forward to an reply :smiley: like fr im lost, pls help