Files
3DBlobici-WorkingTitle/3D blobici/Assets/Scripts/MapGen/MapGenManager.cs
2025-06-27 08:23:17 +02:00

256 lines
8.6 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System.Collections.Generic;
using UnityEngine;
using System.Linq;
using System.Runtime.CompilerServices;
public class MapGenManager : MonoBehaviour
{
/* ------------------ INSPECTOR FIELDS ------------------ */
[Header("Room Prefabs")]
[SerializeField] private List<GameObject> mapPrefab = new();
[Header("Player")]
[SerializeField] private GameObject Player;
[Header("Corridor Prefabs")]
[SerializeField] private GameObject CorridorStraight;
[SerializeField] private GameObject CorridorStraightUnlit;
[SerializeField] private GameObject DoorCorridor;
[Header("Layout")]
[SerializeField] private MapLayout layout;
[Header("Generation Settings")]
[SerializeField] private int RoomDistance = 3;
private readonly Dictionary<Vector2Int, GameObject> gridToRoom = new();
private readonly Vector3 roomOriginOffset = Vector3.zero;
void Start() => GenerateFromLayout();
private void GenerateFromLayout()
{
if (layout == null || string.IsNullOrWhiteSpace(layout.grid))
{
Debug.LogError("Layout asset není přiřazený nebo je prázdný!");
return;
}
gridToRoom.Clear();
/* ----------- Create a spawn room ----------- */
GameObject spawnPrefab = mapPrefab[0];
Vector3 cellSize = GetPrefabXZ(spawnPrefab);
/* ---------- Text layout to grid ---------- */
string[] lines = layout.grid
.Split('\n')
.Select(line => line.TrimEnd('\r'))
.Where(line => !string.IsNullOrWhiteSpace(line))
.ToArray();
Vector2 firstRoomPos = GetFirstOrLastRoom(lines);
int bottomRowIdx = (int)firstRoomPos.y;
int spawnGridX = (int)firstRoomPos.x;
/* ---------- Create spawn room properly ---------- */
Vector3 spawnPos = roomOriginOffset + new Vector3(
spawnGridX * (cellSize.x + RoomDistance), // X
0,
0);
GameObject spawnRoom = Instantiate(spawnPrefab, spawnPos, Quaternion.identity, transform);
gridToRoom[new Vector2Int(spawnGridX, 0)] = spawnRoom;
/* ---------- Instantiate player ---------- */
if (Player)
{
Vector3 playerPos = spawnPos + new Vector3(0, 1, 3);
Instantiate(Player, playerPos, Quaternion.identity, transform);
}
/* ---------- Build the rest of rooms ---------- */
BuildRooms(lines, bottomRowIdx, cellSize);
/* ---------- Open walls based on aproximity to other rooms ---------- */
foreach (var keyValuePair in gridToRoom)
{
Vector2Int g = keyValuePair.Key;
RoomHandler rh = keyValuePair.Value.GetComponent<RoomHandler>();
bool north = gridToRoom.ContainsKey(g + Vector2Int.left);
bool south = gridToRoom.ContainsKey(g + Vector2Int.right);
bool east = gridToRoom.ContainsKey(g + Vector2Int.up);
bool west = gridToRoom.ContainsKey(g + Vector2Int.down);
rh.SetEntrances(northOpen: north, southOpen: south, eastOpen: east, westOpen: west);
}
/* ---------- 5) CHODBY ---------- */
BuildCorridors();
}
/// <summary>
/// Build corridors between rooms
/// </summary>
/// <returns></returns>
private void BuildCorridors()
{
float straightZ = CorridorStraight.GetComponent<PrefabSize>().prefabSize.y;
float straightX = CorridorStraight.GetComponent<PrefabSize>().prefabSize.x;
Vector2Int[] stepDirs = { Vector2Int.right, Vector2Int.up };
foreach (var kv in gridToRoom)
{
Vector2Int cell = kv.Key;
GameObject roomA = kv.Value;
foreach (Vector2Int dir in stepDirs)
{
Vector2Int nKey = cell + dir;
if (!gridToRoom.TryGetValue(nKey, out GameObject roomB)) continue;
Vector3 axis, axisNeg;
float halfA, halfB;
Quaternion rot;
if (dir == Vector2Int.right)
{
axis = Vector3.right; axisNeg = Vector3.left;
halfA = GetPrefabXZ(roomA).x * 0.5f;
halfB = GetPrefabXZ(roomB).x * 0.5f;
rot = Quaternion.Euler(0, 90, 0);
}
else
{
axis = Vector3.forward; axisNeg = Vector3.back;
halfA = GetPrefabXZ(roomA).z * 0.5f;
halfB = GetPrefabXZ(roomB).z * 0.5f;
rot = Quaternion.identity;
}
Vector3 start = roomA.transform.position + axis * halfA;
Vector3 end = roomB.transform.position + axisNeg * halfB;
CreateStraightCorridor(start, end, axis, rot, straightZ, straightX);
}
}
}
/// <summary>
/// Build all rooms based on the layout grid
/// </summary>
/// <param name="lines"></param>
/// <param name="bottomRowIdx"></param>
/// <param name="cellSize"></param>
private void BuildRooms(string[] lines, int bottomRowIdx, Vector3 cellSize)
{
int rows = lines.Length;
for (int rowsIter = 0; rowsIter < rows; rowsIter++)
{
string line = lines[rowsIter];
int gridZ = bottomRowIdx - rowsIter + 1;
for (int x = 0; x < line.Length; x++)
{
char ch = line[x];
if (ch == '-') continue;
if (!char.IsDigit(ch))
{
Debug.LogWarning($"Neznámý znak '{ch}' v layoutu ignoruji.");
continue;
}
int idx = ch - '0';
if (idx >= mapPrefab.Count)
{
Debug.LogWarning($"Index {idx} mimo rozsah mapPrefab!");
continue;
}
GameObject prefab = mapPrefab[idx];
Vector3 worldPos = roomOriginOffset + new Vector3(
x * (cellSize.x + RoomDistance),
0,
gridZ * (cellSize.z + RoomDistance));
GameObject room = Instantiate(prefab, worldPos, Quaternion.identity, transform);
gridToRoom[new Vector2Int(x, gridZ)] = room;
}
}
}
/// <summary>
/// Returns the (x, y) coordinates of the room with closest aproximity to spawn or the furthest aproximity to spawn
/// </summary>
/// <param name="lines"></param>
/// <param name="last"></param>
/// <returns>Vector2(x, y) where x is the line index and y is the column index</returns>
private Vector2 GetFirstOrLastRoom(string[] lines, bool last = false)
{
int colIdx = -1;
int spawnGridX = 0;
int rows = lines.Length;
for (int rowsIter = rows - 1; rowsIter >= 0; rowsIter--)
{
string line = lines[rowsIter];
for (int c = 0; c < line.Length; c++)
{
if (char.IsDigit(line[c]))
{
colIdx = rowsIter;
spawnGridX = c;
break;
}
}
if (colIdx != -1) break;
}
if (colIdx == -1)
{
Debug.LogError("Layout neobsahuje žádnou číslici (místnost)!");
return Vector2.zero;
}
return new Vector2(spawnGridX, colIdx);
}
private void CreateStraightCorridor(
Vector3 start, Vector3 end, Vector3 axis, Quaternion rot,
float stepLenZ, float stepLenX)
{
float dist = Vector3.Distance(start, end);
float step = Mathf.Approximately(Mathf.Abs(axis.z), 1f) ? stepLenZ : stepLenX;
int count = Mathf.Max(1, Mathf.FloorToInt(dist / step));
Vector3 segPos = start + axis * (step * 0.5f);
var door = Instantiate(CorridorStraight, segPos, rot, transform);
//door.GetComponent<DoorController>()?.OpenDoor();
for (int i = 1; i < count; i++)
{
GameObject prefab = (i % 2 == 0) ? CorridorStraight : CorridorStraightUnlit;
segPos = start + axis * (i * step + step * 0.5f);
Instantiate(prefab, segPos, rot, transform);
}
}
/// <summary>
/// Returns the size of a prefab room
/// </summary>
/// <param name="prefab"> Choose a prefab from available ones</param>
/// <returns>Size of the prefab as Vector3</returns>
private static Vector3 GetPrefabXZ(GameObject prefab)
{
var ps = prefab.GetComponent<PrefabSize>();
return ps ? new Vector3(ps.prefabSize.x, 0, ps.prefabSize.y) : new Vector3(10, 0, 10);
}
}