2D程序地牢生成游戏

问题描述 投票:2回答:1

我目前正在统一使用程序化2D游戏。以下是我用来生成地牢的脚本,想知道是否有指定标准起始房间的脚本。我已经建好了预制房间,但想让一个预制房间的玩家始终加入其中。

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

public class DungeonGeneration : MonoBehaviour {

    [SerializeField]
    private int numberOfRooms;

    [SerializeField]
    private int numberOfObstacles;
    [SerializeField]
    private Vector2Int[] possibleObstacleSizes;

    [SerializeField]
    private int numberOfEnemies;
    [SerializeField]
    private GameObject[] possibleEnemies;

    [SerializeField]
    private GameObject goalPrefab;

    [SerializeField]
    private TileBase obstacleTile;

    private Room[,] rooms;

    private Room currentRoom;

    private static DungeonGeneration instance = null;

    void Awake () {
        if (instance == null) {
            DontDestroyOnLoad (this.gameObject);
            instance = this;
            this.currentRoom = GenerateDungeon ();
        } else {
            string roomPrefabName = instance.currentRoom.PrefabName ();
            GameObject roomObject = (GameObject) Instantiate (Resources.Load (roomPrefabName));
            Tilemap tilemap = roomObject.GetComponentInChildren<Tilemap> ();
            instance.currentRoom.AddPopulationToTilemap (tilemap, instance.obstacleTile);
            Destroy (this.gameObject);
        }
    }

    void Start () {
        string roomPrefabName = this.currentRoom.PrefabName ();
        GameObject roomObject = (GameObject) Instantiate (Resources.Load (roomPrefabName));
        Tilemap tilemap = roomObject.GetComponentInChildren<Tilemap> ();
        this.currentRoom.AddPopulationToTilemap (tilemap, this.obstacleTile);
    }

    private Room GenerateDungeon() {
        int gridSize = 3 * numberOfRooms;

        rooms = new Room[gridSize, gridSize];

        Vector2Int initialRoomCoordinate = new Vector2Int ((gridSize / 2) - 1, (gridSize / 2) - 1);

        Queue<Room> roomsToCreate = new Queue<Room> ();
        roomsToCreate.Enqueue (new Room(initialRoomCoordinate.x, initialRoomCoordinate.y));
        List<Room> createdRooms = new List<Room> ();
        while (roomsToCreate.Count > 0 && createdRooms.Count < numberOfRooms) {
            Room currentRoom = roomsToCreate.Dequeue ();
            this.rooms [currentRoom.roomCoordinate.x, currentRoom.roomCoordinate.y] = currentRoom;
            createdRooms.Add (currentRoom);
            AddNeighbors (currentRoom, roomsToCreate);
        }

        int maximumDistanceToInitialRoom = 0;
        Room finalRoom = null;
        foreach (Room room in createdRooms) {
            List<Vector2Int> neighborCoordinates = room.NeighborCoordinates ();
            foreach (Vector2Int coordinate in neighborCoordinates) {
                Room neighbor = this.rooms [coordinate.x, coordinate.y];
                if (neighbor != null) {
                    room.Connect (neighbor);
                }
            }
            room.PopulateObstacles (this.numberOfObstacles, this.possibleObstacleSizes);
            room.PopulatePrefabs (this.numberOfEnemies, this.possibleEnemies);

            int distanceToInitialRoom = Mathf.Abs (room.roomCoordinate.x - initialRoomCoordinate.x) + Mathf.Abs(room.roomCoordinate.y - initialRoomCoordinate.y);
            if (distanceToInitialRoom > maximumDistanceToInitialRoom) {
                maximumDistanceToInitialRoom = distanceToInitialRoom;
                finalRoom = room;
            }
        }

        GameObject[] goalPrefabs = { this.goalPrefab };
        finalRoom.PopulatePrefabs(1, goalPrefabs);

        return this.rooms [initialRoomCoordinate.x, initialRoomCoordinate.y];
    }

    private void AddNeighbors(Room currentRoom, Queue<Room> roomsToCreate) {
        List<Vector2Int> neighborCoordinates = currentRoom.NeighborCoordinates ();
        List<Vector2Int> availableNeighbors = new List<Vector2Int> ();
        foreach (Vector2Int coordinate in neighborCoordinates) {
            if (this.rooms[coordinate.x, coordinate.y] == null) {
                availableNeighbors.Add (coordinate);
            }
        }

        int numberOfNeighbors = (int)Random.Range (1, availableNeighbors.Count);

        for (int neighborIndex = 0; neighborIndex < numberOfNeighbors; neighborIndex++) {
            float randomNumber = Random.value;
            float roomFrac = 1f / (float)availableNeighbors.Count;
            Vector2Int chosenNeighbor = new Vector2Int(0, 0);
            foreach (Vector2Int coordinate in availableNeighbors) {
                if (randomNumber < roomFrac) {
                    chosenNeighbor = coordinate;
                    break;
                } else {
                    roomFrac += 1f / (float)availableNeighbors.Count;
                }
            }
            roomsToCreate.Enqueue (new Room(chosenNeighbor));
            availableNeighbors.Remove (chosenNeighbor);
        }
    }

    private void PrintGrid() {
        for (int rowIndex = 0; rowIndex < this.rooms.GetLength (1); rowIndex++) {
            string row = "";
            for (int columnIndex = 0; columnIndex < this.rooms.GetLength (0); columnIndex++) {
                if (this.rooms [columnIndex, rowIndex] == null) {
                    row += "X";
                } else {
                    row += "R";
                }
            }
            Debug.Log (row);
        }
    }

    public void MoveToRoom(Room room) {
        this.currentRoom = room;
    }

    public Room CurrentRoom() {
        return this.currentRoom;
    }

    public void ResetDungeon() {
        this.currentRoom = GenerateDungeon ();
    }

}

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

public class Room
{
    public Vector2Int roomCoordinate;
    public Dictionary<string, Room> neighbors;

    private string[,] population;

    private Dictionary<string, GameObject> name2Prefab;

    public Room (int xCoordinate, int yCoordinate)
    {
        this.roomCoordinate = new Vector2Int (xCoordinate, yCoordinate);
        this.neighbors = new Dictionary<string, Room> ();
        this.population = new string[18, 10];
        for (int xIndex = 0; xIndex < 18; xIndex += 1) {
            for (int yIndex = 0; yIndex < 10; yIndex += 1) {
                this.population [xIndex, yIndex] = "";
            }
        }
        this.population [8, 5] = "Player";
        this.name2Prefab = new Dictionary<string, GameObject> ();
    }

    public Room (Vector2Int roomCoordinate)
    {
        this.roomCoordinate = roomCoordinate;
        this.neighbors = new Dictionary<string, Room> ();
        this.population = new string[18, 10];
        for (int xIndex = 0; xIndex < 18; xIndex += 1) {
            for (int yIndex = 0; yIndex < 10; yIndex += 1) {
                this.population [xIndex, yIndex] = "";
            }
        }
        this.population [8, 5] = "Player";
        this.name2Prefab = new Dictionary<string, GameObject> ();
    }

    public List<Vector2Int> NeighborCoordinates () {
        List<Vector2Int> neighborCoordinates = new List<Vector2Int> ();
        neighborCoordinates.Add (new Vector2Int(this.roomCoordinate.x, this.roomCoordinate.y - 1));
        neighborCoordinates.Add (new Vector2Int(this.roomCoordinate.x + 1, this.roomCoordinate.y));
        neighborCoordinates.Add (new Vector2Int(this.roomCoordinate.x, this.roomCoordinate.y + 1));
        neighborCoordinates.Add (new Vector2Int(this.roomCoordinate.x - 1, this.roomCoordinate.y));

        return neighborCoordinates;
    }

    public void Connect (Room neighbor) {
        string direction = "";
        if (neighbor.roomCoordinate.y < this.roomCoordinate.y) {
            direction = "N";
        }
        if (neighbor.roomCoordinate.x > this.roomCoordinate.x) {
            direction = "E";
        }   
        if (neighbor.roomCoordinate.y > this.roomCoordinate.y) {
            direction = "S";
        }
        if (neighbor.roomCoordinate.x < this.roomCoordinate.x) {
            direction = "W";
        }
        this.neighbors.Add (direction, neighbor);
    }

    public string PrefabName () {
        string name = "Room_";
        foreach (KeyValuePair<string, Room> neighborPair in neighbors) {
            name += neighborPair.Key;
        }
        return name;
    }

    public Room Neighbor (string direction) {
        return this.neighbors [direction];
    }

    public void PopulateObstacles (int numberOfObstacles, Vector2Int[] possibleSizes) {
        for (int obstacleIndex = 0; obstacleIndex < numberOfObstacles; obstacleIndex += 1) {
            int sizeIndex = Random.Range (0, possibleSizes.Length);
            Vector2Int regionSize = possibleSizes [sizeIndex];
            List<Vector2Int> region = FindFreeRegion (regionSize);
            foreach (Vector2Int coordinate in region) {
                this.population [coordinate.x, coordinate.y] = "Obstacle";
            }
        }
    }

    public void PopulatePrefabs (int numberOfPrefabs, GameObject[] possiblePrefabs) {
        for (int prefabIndex = 0; prefabIndex < numberOfPrefabs; prefabIndex += 1) {
            int choiceIndex = Random.Range (0, possiblePrefabs.Length);
            GameObject prefab = possiblePrefabs [choiceIndex];
            List<Vector2Int> region = FindFreeRegion (new Vector2Int(1, 1));

            this.population [region[0].x, region[0].y] = prefab.name;
            this.name2Prefab [prefab.name] = prefab;
        }
    }

    private List<Vector2Int> FindFreeRegion (Vector2Int sizeInTiles) {
        List<Vector2Int> region = new List<Vector2Int>();
        do {
            region.Clear();

            Vector2Int centerTile = new Vector2Int(UnityEngine.Random.Range(2, 18 - 3), UnityEngine.Random.Range(2, 10 - 3));

            region.Add(centerTile);

            int initialXCoordinate = (centerTile.x - (int)Mathf.Floor(sizeInTiles.x / 2));
            int initialYCoordinate = (centerTile.y - (int)Mathf.Floor(sizeInTiles.y / 2));
            for (int xCoordinate = initialXCoordinate; xCoordinate < initialXCoordinate + sizeInTiles.x; xCoordinate += 1) {
                for (int yCoordinate = initialYCoordinate; yCoordinate < initialYCoordinate + sizeInTiles.y; yCoordinate += 1) {
                    region.Add(new Vector2Int(xCoordinate, yCoordinate));
                }
            }
        } while(!IsFree (region));
        return region;
    }

    private bool IsFree (List<Vector2Int> region) {
        foreach (Vector2Int tile in region) {
            if (this.population [tile.x, tile.y] != "") {
                return false;
            }
        }
        return true;
    }

    public void AddPopulationToTilemap (Tilemap tilemap, TileBase obstacleTile) {
        for (int xIndex = 0; xIndex < 18; xIndex += 1) {
            for (int yIndex = 0; yIndex < 10; yIndex += 1) {
                if (this.population [xIndex, yIndex] == "Obstacle") {
                    tilemap.SetTile (new Vector3Int (xIndex - 9, yIndex - 5, 0), obstacleTile);
                } else if (this.population [xIndex, yIndex] != "" && this.population [xIndex, yIndex] != "Player") {
                    GameObject prefab = GameObject.Instantiate (this.name2Prefab[this.population [xIndex, yIndex]]);
                    prefab.transform.position = new Vector2 (xIndex - 9 + 0.5f, yIndex - 5 + 0.5f);
                }
            }
        }
    }
}

即使您能向我指明如何使用的方向,任何帮助也都很棒。

unity3d game-development procedural-generation
1个回答
0
投票

漂亮的程序地牢生成器!就像一个建议一样,在生成地牢时可以在开始时缓存第一个房间吗?然后,您可以抓住initialRoomForPlayerSpawn坐标/位置作为字符放置的参考点。

Room initialRoomForPlayerSpawn = null;

while (roomsToCreate.Count > 0 && createdRooms.Count < numberOfRooms) {
            Room currentRoom = roomsToCreate.Dequeue ();
            this.rooms [currentRoom.roomCoordinate.x, currentRoom.roomCoordinate.y] = currentRoom;
            createdRooms.Add (currentRoom);
            AddNeighbors (currentRoom, roomsToCreate);

            /* Cache First Room */
            if(createdRooms != null && createdRooms.Count <= 1) {
                  initialRoomForPlayerSpawn = currentRoom;
            }
        }
© www.soinside.com 2019 - 2024. All rights reserved.