Files
3DBlobici-WorkingTitle/3D blobici/Assets/Scripts/MapGen/MapGenManager.cs
2025-06-30 16:28:30 +02:00

251 lines
10 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 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;
}
}