[Unity]Roguelike random map generation

Keywords: C# Unity

Basic room generation

First, you need to determine which direction of the previous room is generated in each room generation. Therefore, you need to declare an enumeration value to record the up, down, left and right directions
Create a script RoomGenerator to declare the direction enumeration value

	/// <summary>
    ///Direction
    /// </summary>
    public enum Direction
    {
        LEFT = 0,
        RIGHT = 1,
        TOP = 2,
        BOTTOM = 3
    }

    //The direction of the next generated room
    private Direction direction;

In addition, we need to declare the following variables

  • GameObject variable for the base room
  • Maximum number of rooms
  • List of all rooms currently generated
  • Room generation point
  • 10. Position offset of Y axis
  • Start and end rooms
	//Prefabricated body of foundation room
    public GameObject baseRoom;
    //Maximum number of rooms
    public int maxCreateNum;
    //Current room list
    private List<Room> roomList = new List<Room>();
    //Generation point
    public Transform spawnPoint;
    //Generate point offset
    public float xOffset, yOffset;

	public GameObject startRoom;
    public GameObject endRoom;

Go to unity and use a Square to temporarily act as the base room

Drag the sprite as a prefab, create a Room script and drag it onto the prefab

Set a Room level Room

Next, a random number is used to determine the direction of the next generated room

	/// <summary>
    ///Randomly create room orientation
    /// </summary>
    void RandomDirection()
    {
    	direction = (Direction) Random.Range(0, 4);

        switch (direction)
        {
            case Direction.LEFT:
                spawnPoint.position += new Vector3(-xOffset, 0, 0);
                break;
            case Direction.RIGHT:
                spawnPoint.position += new Vector3(xOffset, 0, 0);
                break;
            case Direction.BOTTOM:
                spawnPoint.position += new Vector3(0, -yOffset, 0);
                break;
            case Direction.TOP:
                spawnPoint.position += new Vector3(0, yOffset, 0);
                break;
            }
    }

Create room

	void Start()
    {
        CreateRoom();
    }
	
	/// <summary>
    ///Create random map
    /// </summary>
    void CreateRoom()
    {
        for (int i = 0; i < maxCreateNum; i++)
        {
            CreateRoomObj(baseRoom, spawnPoint.position);
            RandomDirection();
        }
    }

    /// <summary>
    ///Create room
    /// </summary>
    ///< param name = "room" > Room preform object < / param >
    ///< param name = "POS" > location < / param >
    void CreateRoomObj(GameObject room, Vector3 pos)
    {
        GameObject obj = Instantiate(room, pos, Quaternion.identity);
        roomList.Add(obj.GetComponent<Room>());
    }

Go to unity, create a new Gameobject named RoomGenerator, and create a new sub object spanroompoint under this object

Mount the script RoomGenerator.cs on RoomGenerator, set the basic room and room generation point, and set the offset of X and Y axes according to your needs

At this point, the map can be generated randomly, but the generated rooms may overlap

Therefore, we need to judge when moving the room generation point to judge whether there is a room in the current position

First, a BoxCollider2D is added to the baseeroom preform for occupancy detection

Modify the random direction function and declare a detection radius variable

    //Detection radius (whether there is a room in the current position)
    public float roomColliderRadius;

Use the Physics2D.OverlapCircle function to detect whether there is a room in the current location. This method returns a detected Collider2D. If not, it returns null

void RandomDirection()
    {
        do
        {
            direction = (Direction) Random.Range(0, 4);

            switch (direction)
            {
                case Direction.LEFT:
                    spawnPoint.position += new Vector3(-xOffset, 0, 0);
                    break;
                case Direction.RIGHT:
                    spawnPoint.position += new Vector3(xOffset, 0, 0);
                    break;
                case Direction.BOTTOM:
                    spawnPoint.position += new Vector3(0, -yOffset, 0);
                    break;
                case Direction.TOP:
                    spawnPoint.position += new Vector3(0, yOffset, 0);
                    break;
            }

        } while (Physics2D.OverlapCircle(spawnPoint.position, roomColliderRadius, roomLayer));
    }

Run again and the rooms will no longer overlap

Next, we need to make the judgment of doors in four directions of the room

Judgment of room door

First, we need to declare the following variables

  • GameObject object for four direction doors
  • Four bool values are used to judge which directions of the room will have doors
  • Total number of doors
	//Four doors
    public GameObject doorLeft, doorRight, doorTop, doorBottom;
    
    public bool isLeft;
    public bool isRight;
    public bool isTop;
    public bool isBottom;

Create four sub objects under the baseeroom preform and rename them to the corresponding direction enumeration values. For example, the door on the right is 1 and the door on the left is 0

Note that the name of the upper door should be set according to the direction enumeration value, and should correspond to each other one by one

	public enum Direction
    {
        LEFT = 0,
        RIGHT = 1,
        TOP = 2,
        BOTTOM = 3
    }

Initialize the instance objects of the four doors in the Room script

	void Awake()
    {
        foreach (Transform door in transform)
        {
            switch ((RoomGenerator.Direction)Convert.ToInt32(door.gameObject.name))
            {
                case RoomGenerator.Direction.LEFT:
                    doorLeft = door.gameObject;
                    break;
                case RoomGenerator.Direction.RIGHT:
                    doorRight = door.gameObject;
                    break;
                case RoomGenerator.Direction.TOP:
                    doorTop = door.gameObject;
                    break;
                case RoomGenerator.Direction.BOTTOM:
                    doorBottom = door.gameObject;
                    break;
            }   
        }
    }

Determine which doors need to be opened according to the status of doors in four directions

	void Start()
    {
        doorLeft.SetActive(isLeft);
        doorRight.SetActive(isRight);
        doorBottom.SetActive(isBottom);
        doorTop.SetActive(isTop);
    }

Total number of updated doors

	public void UpdateRoomState()
    {
        if (isLeft) doorNumber++;
        if (isRight) doorNumber++;
        if (isTop) doorNumber++;
        if (isBottom) doorNumber++;
    }

Go to the RoomGenerator script and also use Physics2D.OverlapCircle to determine whether there are adjacent rooms around

	/// <summary>
    ///Detects the number of rooms around the current room
    /// </summary>
    /// <param name="room"></param>
    /// <param name="pos"></param>
    void CheckRoomDoor(Room room, Vector3 pos)
    {
        Collider2D left = Physics2D.OverlapCircle(pos + new Vector3(-xOffset, 0, 0), roomColliderRadius, roomLayer);
        Collider2D right = Physics2D.OverlapCircle(pos + new Vector3(xOffset, 0, 0), roomColliderRadius, roomLayer);
        Collider2D top = Physics2D.OverlapCircle(pos + new Vector3(0, yOffset, 0), roomColliderRadius, roomLayer);
        Collider2D bottom = Physics2D.OverlapCircle(pos + new Vector3(0, -yOffset, 0), roomColliderRadius, roomLayer);

        room.isLeft = left;
        room.isRight = right;
        room.isTop = top;
        room.isBottom = bottom;

        room.UpdateRoomState();
    }

After creating the room, call the above method to open the door corresponding to the direction of the room.

	void Start()
    {
        CreateRoom();

        foreach (var room in roomList)
        {
            CheckRoomDoor(room, room.transform.position);
        }
    }

Final effect

Current full script
RoomGenerator.cs

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

public class RoomGenerator : MonoBehaviour
{
    /// <summary>
    ///Direction
    /// </summary>
    public enum Direction
    {
        LEFT = 0,
        RIGHT = 1,
        TOP = 2,
        BOTTOM = 3
    }

    //Prefabricated body of foundation room
    public GameObject baseRoom;

    //The direction of the next generated room
    private Direction direction;

    //Maximum number of rooms
    public int maxCreateNum;

    //Room level
    public LayerMask roomLayer;
    
    //Current room list
    private List<Room> roomList = new List<Room>();

    public GameObject startRoom;
    public GameObject endRoom;

    //Generation point
    public Transform spawnPoint;
    //Generate point offset
    public float xOffset, yOffset;
    //Detection radius (whether there is a room in the current position)
    public float roomColliderRadius;

    //Start room color
    public Color startColor;
    //End room color
    public Color endColor;

    // Start is called before the first frame update
    void Start()
    {
        CreateRoom();

        foreach (var room in roomList)
        {
            CheckRoomDoor(room, room.transform.position);
        }

        startRoom = roomList[0].gameObject;
        endRoom = roomList[maxCreateNum - 1].gameObject;
        startRoom.GetComponent<SpriteRenderer>().color = startColor;
        endRoom.GetComponent<SpriteRenderer>().color = endColor;
    }

    // Update is called once per frame
    void Update()
    {
        
    }

    /// <summary>
    ///Randomly create room orientation
    /// </summary>
    void RandomDirection()
    {
        do
        {
            direction = (Direction) Random.Range(0, 4);

            switch (direction)
            {
                case Direction.LEFT:
                    spawnPoint.position += new Vector3(-xOffset, 0, 0);
                    break;
                case Direction.RIGHT:
                    spawnPoint.position += new Vector3(xOffset, 0, 0);
                    break;
                case Direction.BOTTOM:
                    spawnPoint.position += new Vector3(0, -yOffset, 0);
                    break;
                case Direction.TOP:
                    spawnPoint.position += new Vector3(0, yOffset, 0);
                    break;
            }

        } while (Physics2D.OverlapCircle(spawnPoint.position, roomColliderRadius, roomLayer));


    }

    /// <summary>
    ///Create random map
    /// </summary>
    void CreateRoom()
    {
        for (int i = 0; i < maxCreateNum; i++)
        {
            CreateRoomObj(baseRoom, spawnPoint.position);
            RandomDirection();
        }
    }

    /// <summary>
    ///Create room
    /// </summary>
    ///< param name = "room" > Room preform object < / param >
    ///< param name = "POS" > location < / param >
    void CreateRoomObj(GameObject room, Vector3 pos)
    {
        GameObject obj = Instantiate(room, pos, Quaternion.identity);
        // obj.transform.position = pos;
        roomList.Add(obj.GetComponent<Room>());
    }

    /// <summary>
    ///Detects the number of rooms around the current room
    /// </summary>
    /// <param name="room"></param>
    /// <param name="pos"></param>
    void CheckRoomDoor(Room room, Vector3 pos)
    {
        Collider2D left = Physics2D.OverlapCircle(pos + new Vector3(-xOffset, 0, 0), roomColliderRadius, roomLayer);
        Collider2D right = Physics2D.OverlapCircle(pos + new Vector3(xOffset, 0, 0), roomColliderRadius, roomLayer);
        Collider2D top = Physics2D.OverlapCircle(pos + new Vector3(0, yOffset, 0), roomColliderRadius, roomLayer);
        Collider2D bottom = Physics2D.OverlapCircle(pos + new Vector3(0, -yOffset, 0), roomColliderRadius, roomLayer);

        room.isLeft = left;
        room.isRight = right;
        room.isTop = top;
        room.isBottom = bottom;

        room.UpdateRoomState();
    }

    void OnDrawGizmos()
    {
        Gizmos.color = Color.red;
        Gizmos.DrawWireSphere(spawnPoint.position,roomColliderRadius);
    }
}

Room.cs

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

public class Room : MonoBehaviour
{
    //Four doors
    public GameObject doorLeft, doorRight, doorTop, doorBottom;

    public bool isLeft;
    public bool isRight;
    public bool isTop;
    public bool isBottom;

    //Number of doors
    public int doorNumber;



    void Awake()
    {
        foreach (Transform door in transform)
        {
            switch ((RoomGenerator.Direction)Convert.ToInt32(door.gameObject.name))
            {
                case RoomGenerator.Direction.LEFT:
                    doorLeft = door.gameObject;
                    break;
                case RoomGenerator.Direction.RIGHT:
                    doorRight = door.gameObject;
                    break;
                case RoomGenerator.Direction.TOP:
                    doorTop = door.gameObject;
                    break;
                case RoomGenerator.Direction.BOTTOM:
                    doorBottom = door.gameObject;
                    break;
            }   
        }
    }

    void Start()
    {
        doorLeft.SetActive(isLeft);
        doorRight.SetActive(isRight);
        doorBottom.SetActive(isBottom);
        doorTop.SetActive(isTop);


    }

    public void UpdateRoomState()
    {
        if (isLeft) doorNumber++;
        if (isRight) doorNumber++;
        if (isTop) doorNumber++;
        if (isBottom) doorNumber++;
    }
}

Posted by newzub on Sun, 10 Oct 2021 07:21:33 -0700