Files
3DBlobici-WorkingTitle/3D blobici/Assets/Scripts/MapGen/MapGenManager.cs

254 lines
9.7 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;
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();
public event Action<MapGenManager> OnGenerationComplete;
public IReadOnlyDictionary<Vector2Int, GameObject> GridToRoom => gridToRoom;
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();
}
OnGenerationComplete?.Invoke(this);
}
/// <summary>
/// Builds corridors and doors between rooms.
/// </summary>
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;
// Handle geometry
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;
}
// Wall calculation
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;
// Doors
Vector3 doorPos = (wallA + wallB) * 0.5f;
GameObject doorGO = Instantiate(DoorCorridor, doorPos, rot, transform);
DoorAnimation anim = doorGO.GetComponent<DoorAnimation>();
// Register the corridor to both rooms
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,
Quaternion rot,
float len)
{
const float EPS = 0.01f;
float dist = Vector3.Distance(startEdge, wallEdge);
if (dist < EPS) return;
int fullCount = Mathf.FloorToInt(dist / len);
float remainder = dist - fullCount * len;
// Full segments
Vector3 firstPivot = startEdge + direction * (len * 0.5f);
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);
}
// Short segment to fill the gap
if (remainder > EPS)
{
Vector3 remPivot = wallEdge - direction * (remainder * 0.5f);
GameObject last = Instantiate(CorridorStraightUnlit, remPivot, rot, transform);
Vector3 sc = last.transform.localScale;
sc.z *= remainder / len;
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;
}
}