Path Generator
unknown
plain_text
6 months ago
14 kB
7
Indexable
using System.Collections.Generic; using UnityEngine; public class PathGenerator : MonoBehaviour { public int baseWidth = 10; public int baseHeight = 10; public GameObject wallPrefab; public GameObject startPointPrefab; public GameObject endPointPrefab; public float complexity = 1f; public GameObject secondaryPathPrefab; public GameObject secondaryStartPointPrefab; public GameObject secondaryEndPointPrefab; public GameObject pathPrefab; public GameObject waterPrefab; public GameObject[] randomDecorations; public int decorationRadiusFromEndPoints = 3; public int pathSeparation = 4; public int decorRatio = 10; public int Prev = 25; public int NumberOfSpawnPoints; public int AddedGC; public int currentWaterLayers; private int level = 1; private int width; private int height; private int[,] map; private int visitedCells = 0; private int secondaryVisitedCells = 0; public Vector2Int startPoint; public Vector2Int endPoint; private Vector2Int secondaryStartPoint; private Vector2Int secondaryEndPoint; private const int MaxStackSize = 10000; private HashSet<Vector2Int> instantiatedPathPositions; private HashSet<Vector2Int> wallPositions; private HashSet<Vector2Int> decoratedWallPositions; private List<Vector2Int> secondaryPaths = new List<Vector2Int>(); public Vector2Int preStartPoint; public Material[] sky; public GameObject rain; void Start() { width = baseWidth; height = baseHeight; GeneratePath(); DrawPath(); int ran = Random.Range(0, sky.Length); if (ran == 8) { rain.SetActive(true); } else { rain.SetActive(false); } RenderSettings.skybox = sky[ran]; } void Update() { if (Input.GetKeyDown(KeyCode.Space)) { LevelUp(); } } public void LevelUp() { level++; width = baseWidth + level - 1; height = baseHeight + level - 1; CleanupScene(); GeneratePath(); DrawPath(); if (level % 15 == 0 && secondaryPaths.Count < 4) { GenerateSecondaryPath(); } GameObject[] spawnPoints = GameObject.FindGameObjectsWithTag("Spawns"); NumberOfSpawnPoints = spawnPoints.Length; int ran = Random.Range(0, sky.Length); if (ran == 8) { rain.SetActive(true); } else { rain.SetActive(false); } RenderSettings.skybox = sky[ran]; } void GeneratePath() { InitializePath(); int totalBlocks = width * height; int decorationBlocks = Mathf.FloorToInt(totalBlocks / decorRatio); int minVertices = Mathf.FloorToInt((totalBlocks - decorationBlocks) / 2f); if (level > 1) { minVertices = Prev; } else { minVertices = Mathf.FloorToInt((totalBlocks - decorationBlocks) / 2f); } int maxVertices = Mathf.FloorToInt(totalBlocks / 2f); Prev = maxVertices; GeneratePathStructure(minVertices, maxVertices); } void InitializePath() { map = new int[width * 2 + 1, height * 2 + 1]; visitedCells = 0; secondaryVisitedCells = 0; instantiatedPathPositions = new HashSet<Vector2Int>(); wallPositions = new HashSet<Vector2Int>(); decoratedWallPositions = new HashSet<Vector2Int>(); } void GeneratePathStructure(int minVertices, int maxVertices) { Stack<Vector2Int> stack = new Stack<Vector2Int>(); List<Vector2Int> path = new List<Vector2Int>(); Vector2Int currentCell = new Vector2Int(1, 1); map[currentCell.x, currentCell.y] = 1; path.Add(currentCell); visitedCells++; int targetVertices = Random.Range(minVertices, maxVertices + 1); while (visitedCells < targetVertices) { List<Vector2Int> neighbors = GetUnvisitedNeighbors(currentCell); if (neighbors.Count > 0) { Vector2Int chosenNeighbor = neighbors[Random.Range(0, neighbors.Count)]; if (Random.value < complexity && stack.Count < MaxStackSize) { stack.Push(currentCell); } RemoveWall(currentCell, chosenNeighbor); currentCell = chosenNeighbor; map[currentCell.x, currentCell.y] = 1; path.Add(currentCell); visitedCells++; instantiatedPathPositions.Add(currentCell); } else if (stack.Count > 0) { currentCell = stack.Pop(); } else { break; } } while (visitedCells < targetVertices) { List<Vector2Int> additionalNeighbors = GetUnvisitedNeighbors(currentCell); if (additionalNeighbors.Count > 0) { Vector2Int chosenNeighbor = additionalNeighbors[Random.Range(0, additionalNeighbors.Count)]; RemoveWall(currentCell, chosenNeighbor); currentCell = chosenNeighbor; map[currentCell.x, currentCell.y] = 1; path.Add(currentCell); visitedCells++; instantiatedPathPositions.Add(currentCell); } else { break; } } if (path.Count >= 2) { preStartPoint = path[path.Count - 2]; startPoint = path[path.Count - 1]; } else { preStartPoint = Vector2Int.zero; startPoint = path[path.Count - 1]; } endPoint = path[0]; } void GenerateSecondaryPath() { Vector2Int newSecondaryStart = FindSuitableStartPoint(); if (newSecondaryStart != Vector2Int.zero) { CreateSecondaryPath(newSecondaryStart); secondaryPaths.Add(newSecondaryStart); } } Vector2Int FindSuitableStartPoint() { List<Vector2Int> possibleStartPoints = new List<Vector2Int>(); Vector2Int startPoint = new Vector2Int(Random.Range(2, width * 2 - 1), Random.Range(2, height * 2 - 1)); if (!secondaryPaths.Contains(startPoint) && map[startPoint.x, startPoint.y] == 0) { return startPoint; } return Vector2Int.zero; } void CreateSecondaryPath(Vector2Int start, int depth = 0) { const int MaxDepth = 500; if (depth > MaxDepth) { return; } Vector2Int currentSecondaryCell = start; List<Vector2Int> secondaryPath = new List<Vector2Int>(); secondaryPath.Add(currentSecondaryCell); secondaryVisitedCells++; while (true) { List<Vector2Int> secondaryNeighbors = GetUnvisitedNeighbors(currentSecondaryCell); if (secondaryNeighbors.Count > 0) { Vector2Int chosenSecondaryNeighbor = secondaryNeighbors[Random.Range(0, secondaryNeighbors.Count)]; RemoveWall(currentSecondaryCell, chosenSecondaryNeighbor); currentSecondaryCell = chosenSecondaryNeighbor; map[currentSecondaryCell.x, currentSecondaryCell.y] = 1; secondaryPath.Add(currentSecondaryCell); secondaryVisitedCells++; instantiatedPathPositions.Add(currentSecondaryCell); if (secondaryPath.Count > 1 && !secondaryPath.Contains(endPoint)) { secondaryEndPoint = secondaryPath[secondaryPath.Count - 1]; break; } } else { break; } } if (secondaryPath.Count <= 1) { CreateSecondaryPath(start, depth + 1); } else { secondaryStartPoint = secondaryPath[0]; } } List<Vector2Int> GetUnvisitedNeighbors(Vector2Int cell) { List<Vector2Int> neighbors = new List<Vector2Int>(); if (cell.x > 1 && map[cell.x - 2, cell.y] == 0) neighbors.Add(new Vector2Int(cell.x - 2, cell.y)); if (cell.x < width * 2 - 1 && map[cell.x + 2, cell.y] == 0) neighbors.Add(new Vector2Int(cell.x + 2, cell.y)); if (cell.y > 1 && map[cell.x, cell.y - 2] == 0) neighbors.Add(new Vector2Int(cell.x, cell.y - 2)); if (cell.y < height * 2 - 1 && map[cell.x, cell.y + 2] == 0) neighbors.Add(new Vector2Int(cell.x, cell.y + 2)); return neighbors; } void RemoveWall(Vector2Int current, Vector2Int neighbor) { Vector2Int wallPosition = current + (neighbor - current) / 2; map[wallPosition.x, wallPosition.y] = 1; instantiatedPathPositions.Add(wallPosition); } void DrawPath() { HashSet<Vector2Int> instantiatedPositions = new HashSet<Vector2Int>(); for (int x = 0; x < width * 2 + 1; x++) { for (int y = 0; y < height * 2 + 1; y++) { Vector2Int position = new Vector2Int(x, y); if (map[x, y] == 0) { Instantiate(wallPrefab, new Vector3(x, 0, y), Quaternion.identity, transform); wallPositions.Add(position); } else if (instantiatedPositions.Add(position)) { Instantiate(pathPrefab, new Vector3(x, 0, y), Quaternion.identity, transform); } } } List<Vector2Int> decorationCandidates = new List<Vector2Int>(wallPositions); decorationCandidates.RemoveAll(pos => Vector2Int.Distance(pos, startPoint) <= decorationRadiusFromEndPoints || Vector2Int.Distance(pos, endPoint) <= decorationRadiusFromEndPoints || Vector2Int.Distance(pos, secondaryStartPoint) <= decorationRadiusFromEndPoints || Vector2Int.Distance(pos, secondaryEndPoint) <= decorationRadiusFromEndPoints); int numDecorations = Mathf.Min(decorationCandidates.Count, Mathf.FloorToInt((width * height) / decorRatio)); for (int i = 0; i < numDecorations; i++) { Vector2Int decorationPosition = decorationCandidates[Random.Range(0, decorationCandidates.Count)]; Instantiate(randomDecorations[Random.Range(0, randomDecorations.Length)], new Vector3(decorationPosition.x, 0, decorationPosition.y), Quaternion.identity, transform); decorationCandidates.Remove(decorationPosition); } Instantiate(startPointPrefab, new Vector3(startPoint.x, 0, startPoint.y), Quaternion.identity, transform); Vector2Int[] directions = { Vector2Int.up, Vector2Int.down, Vector2Int.left, Vector2Int.right }; Vector2Int? adjacentPathBlock = null; foreach (Vector2Int direction in directions) { Vector2Int adjacentPosition = endPoint + direction; if (map[adjacentPosition.x, adjacentPosition.y] == 1) { adjacentPathBlock = adjacentPosition; break; } } if (adjacentPathBlock.HasValue) { Vector3 directionToPathBlock = new Vector3(adjacentPathBlock.Value.x - endPoint.x, 0, adjacentPathBlock.Value.y - endPoint.y); Quaternion rotationToFacePathBlock = Quaternion.LookRotation(directionToPathBlock); float yRotation = rotationToFacePathBlock.eulerAngles.y; float snappedYRotation = Mathf.Round(yRotation / 90f) * 90f; GameObject endPointObject = Instantiate(endPointPrefab, new Vector3(endPoint.x, 0, endPoint.y), Quaternion.identity, transform); endPointObject.transform.rotation = Quaternion.Euler(0, snappedYRotation, 0); } else { Instantiate(endPointPrefab, new Vector3(endPoint.x, 0, endPoint.y), Quaternion.identity, transform); } if (secondaryStartPoint != Vector2Int.zero && secondaryEndPoint != Vector2Int.zero) { Instantiate(secondaryEndPointPrefab, new Vector3(secondaryEndPoint.x, 0, secondaryEndPoint.y), Quaternion.identity, transform); } currentWaterLayers = 1 + (level - 1) / 10; DrawWaterLayers(currentWaterLayers); } void DrawWaterLayers(int layers) { int mapWidth = width * 2 + 1; int mapHeight = height * 2 + 1; for (int layer = 0; layer < layers; layer++) { for (int x = -1 - layer; x <= mapWidth + layer; x++) { for (int y = -1 - layer; y <= mapHeight + layer; y++) { if (x == -1 - layer || x == mapWidth + layer || y == -1 - layer || y == mapHeight + layer) { Vector3 waterPosition = new Vector3(x, 0, y); Instantiate(waterPrefab, waterPosition, Quaternion.identity, transform); } } } } } void CleanupScene() { foreach (Transform child in transform) { Destroy(child.gameObject); } instantiatedPathPositions.Clear(); wallPositions.Clear(); decoratedWallPositions.Clear(); } }
Editor is loading...