Path Generator

 avatar
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...