251 lines
10 KiB
C#
251 lines
10 KiB
C#
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using UnityEngine;
|
||
|
||
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();
|
||
|
||
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);
|
||
|
||
string[] lines = layout.grid.Split('\n')
|
||
.Select(l => l.TrimEnd('\r'))
|
||
.Where(l => !string.IsNullOrWhiteSpace(l))
|
||
.ToArray();
|
||
Vector2 first = GetFirstOrLastRoom(lines);
|
||
int bottomRow = (int)first.y;
|
||
int spawnX = (int)first.x;
|
||
|
||
Vector3 spawnPos = new Vector3(spawnX * (cellSize.x + RoomDistance), 0, 0);
|
||
GameObject spawnRoom = Instantiate(spawnPrefab, spawnPos, Quaternion.identity, transform);
|
||
gridToRoom[new Vector2Int(spawnX, 0)] = spawnRoom;
|
||
|
||
|
||
/* ----------- Spawn the player ----------- */
|
||
if (Player)
|
||
{
|
||
GameObject p = Instantiate(Player, spawnPos + new Vector3(0, 1, 3), Quaternion.identity, transform);
|
||
Transform inner = p.transform.Find("Player");
|
||
if (inner) inner.tag = "Player";
|
||
}
|
||
|
||
/* ----------- Build rooms ----------- */
|
||
BuildRooms(lines, bottomRow, cellSize);
|
||
|
||
/* ----------- Set entrances ----------- */
|
||
foreach (var kv in gridToRoom)
|
||
{
|
||
Vector2Int g = kv.Key;
|
||
RoomHandler rh = kv.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(north, south, east, west);
|
||
}
|
||
|
||
/* ----------- Build corridors ----------- */
|
||
BuildCorridors();
|
||
|
||
|
||
/* ----------- Toggle all doors ----------- */
|
||
foreach (var keyValuePair in gridToRoom)
|
||
{
|
||
RoomHandler rh = keyValuePair.Value.GetComponent<RoomHandler>();
|
||
rh.ToggleAllDoors();
|
||
}
|
||
}
|
||
|
||
/* ============================================================ */
|
||
/* CORRIDORS */
|
||
/* ============================================================ */
|
||
private void BuildCorridors()
|
||
{
|
||
// Jediné dva směry, které musíme zkontrolovat z každé místnosti (right/up)
|
||
Vector2Int[] directions = { Vector2Int.right, Vector2Int.up };
|
||
|
||
// Délky prefebů – podél X a Z osy
|
||
float straightLenX = CorridorStraight.GetComponent<PrefabSize>().prefabSize.x;
|
||
float straightLenZ = CorridorStraight.GetComponent<PrefabSize>().prefabSize.y;
|
||
float doorLenX = DoorCorridor.GetComponent<PrefabSize>().prefabSize.x;
|
||
float doorLenZ = DoorCorridor.GetComponent<PrefabSize>().prefabSize.y;
|
||
|
||
foreach (var kv in gridToRoom)
|
||
{
|
||
Vector2Int cell = kv.Key;
|
||
GameObject roomA = kv.Value;
|
||
|
||
foreach (Vector2Int dir in directions)
|
||
{
|
||
Vector2Int nKey = cell + dir;
|
||
if (!gridToRoom.TryGetValue(nKey, out GameObject roomB)) continue; // žádná sousední místnost
|
||
|
||
// Geometrie pro daný směr
|
||
Vector3 axis; float lenStraight, lenDoor; Quaternion rot;
|
||
if (dir == Vector2Int.right)
|
||
{
|
||
axis = Vector3.right;
|
||
lenStraight = straightLenX;
|
||
lenDoor = doorLenX;
|
||
rot = Quaternion.Euler(0, 90, 0);
|
||
}
|
||
else // Vector2Int.up
|
||
{
|
||
axis = Vector3.forward;
|
||
lenStraight = straightLenZ;
|
||
lenDoor = doorLenZ;
|
||
rot = Quaternion.identity;
|
||
}
|
||
|
||
// Pozice stěn (počítáme od středu místnosti k jejímu okraji)
|
||
float halfA = Vector3.Scale(GetPrefabXZ(roomA), axis).magnitude * 0.5f;
|
||
float halfB = Vector3.Scale(GetPrefabXZ(roomB), axis).magnitude * 0.5f;
|
||
Vector3 wallA = roomA.transform.position + axis * halfA;
|
||
Vector3 wallB = roomB.transform.position - axis * halfB;
|
||
|
||
// DVEŘE – pivot dveří do středu úsečky mezi okraji stěn
|
||
Vector3 doorPos = (wallA + wallB) * 0.5f;
|
||
GameObject doorGO = Instantiate(DoorCorridor, doorPos, rot, transform);
|
||
DoorAnimation anim = doorGO.GetComponent<DoorAnimation>();
|
||
|
||
// REGISTRACE do obou místností
|
||
RoomHandler rhA = roomA.GetComponent<RoomHandler>();
|
||
RoomHandler rhB = roomB.GetComponent<RoomHandler>();
|
||
if (dir == Vector2Int.right)
|
||
{
|
||
rhA.RegisterDoor(RoomHandler.Side.East, anim);
|
||
rhB.RegisterDoor(RoomHandler.Side.West, anim);
|
||
}
|
||
else
|
||
{
|
||
rhA.RegisterDoor(RoomHandler.Side.North, anim);
|
||
rhB.RegisterDoor(RoomHandler.Side.South, anim);
|
||
}
|
||
|
||
// ROVNÉ SEGMENTY z obou stran dveří
|
||
Vector3 doorEdgeA = doorPos - axis * (lenDoor * 0.5f);
|
||
Vector3 doorEdgeB = doorPos + axis * (lenDoor * 0.5f);
|
||
|
||
PlaceStraightSegments(doorEdgeA, wallA, -axis, rot, lenStraight);
|
||
PlaceStraightSegments(doorEdgeB, wallB, axis, rot, lenStraight);
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Vyplní úsek mezi startEdge (hrana dveří nebo předchozího dílu)
|
||
/// a wallEdge (vnější hrana stěny místnosti) rovnými segmenty tak,
|
||
/// aby žádný segment nepřečníval do stěny.
|
||
/// </summary>
|
||
private void PlaceStraightSegments(
|
||
Vector3 startEdge,
|
||
Vector3 wallEdge,
|
||
Vector3 direction, // normalizovaný (+/- axis)
|
||
Quaternion rot,
|
||
float len) // délka STANDARDNÍHO rovného segmentu
|
||
{
|
||
const float EPS = 0.01f; // ≈1 cm tolerance
|
||
float dist = Vector3.Distance(startEdge, wallEdge);
|
||
if (dist < EPS) return; // nic netřeba
|
||
|
||
int fullCount = Mathf.FloorToInt(dist / len);
|
||
float remainder = dist - fullCount * len; // 0 .. len
|
||
|
||
// -------- 1) CELÉ segmenty --------
|
||
Vector3 firstPivot = startEdge + direction * (len * 0.5f); // hrana sedí na startEdge
|
||
for (int i = 0; i < fullCount; i++)
|
||
{
|
||
Vector3 pos = firstPivot + direction * (i * len);
|
||
GameObject prefab = (i % 2 == 0) ? CorridorStraight : CorridorStraightUnlit;
|
||
Instantiate(prefab, pos, rot, transform);
|
||
}
|
||
|
||
// -------- 2) POSLEDNÍ ZKRÁCENÝ segment (pokud je třeba) --------
|
||
if (remainder > EPS) // zbylo něco > 1 cm
|
||
{
|
||
// pivot tak, aby přední hrana nepřesáhla wallEdge
|
||
Vector3 remPivot = wallEdge - direction * (remainder * 0.5f);
|
||
GameObject last = Instantiate(CorridorStraightUnlit, remPivot, rot, transform);
|
||
|
||
// zmenšíme délku po místní ose Z (prefab je "dlouhý" po Z)
|
||
Vector3 sc = last.transform.localScale;
|
||
sc.z *= remainder / len; // poměr 0 .. 1
|
||
last.transform.localScale = sc;
|
||
}
|
||
}
|
||
|
||
/* ============================================================ */
|
||
/* ROOMS */
|
||
/* ============================================================ */
|
||
private void BuildRooms(string[] lines, int bottomRowIdx, Vector3 cellSize)
|
||
{
|
||
for (int r = 0; r < lines.Length; r++)
|
||
{
|
||
string line = lines[r];
|
||
int gridZ = bottomRowIdx - r + 1;
|
||
for (int x = 0; x < line.Length; x++)
|
||
{
|
||
char ch = line[x];
|
||
if (ch == '-' || !char.IsDigit(ch)) continue;
|
||
int idx = ch - '0';
|
||
if (idx >= mapPrefab.Count) continue;
|
||
Vector3 pos = new Vector3(x * (cellSize.x + RoomDistance), 0, gridZ * (cellSize.z + RoomDistance));
|
||
GameObject room = Instantiate(mapPrefab[idx], pos, Quaternion.identity, transform);
|
||
gridToRoom[new Vector2Int(x, gridZ)] = room;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* ============================================================ */
|
||
/* HELPERS */
|
||
/* ============================================================ */
|
||
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);
|
||
}
|
||
|
||
private Vector2 GetFirstOrLastRoom(string[] lines)
|
||
{
|
||
for (int r = lines.Length - 1; r >= 0; r--)
|
||
{
|
||
string l = lines[r];
|
||
int c = l.ToCharArray().ToList().FindIndex(char.IsDigit);
|
||
if (c != -1) return new Vector2(c, r);
|
||
}
|
||
Debug.LogError("Layout neobsahuje žádnou číslici (místnost)!");
|
||
return Vector2.zero;
|
||
}
|
||
}
|