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

265 lines
8.9 KiB
C#
Raw Normal View History

2025-06-25 23:54:00 +02:00
using System.Collections.Generic;
2025-06-23 16:30:18 +02:00
using UnityEngine;
using System.Linq;
2025-06-27 07:13:39 +02:00
using System.Runtime.CompilerServices;
2025-06-23 16:30:18 +02:00
public class MapGenManager : MonoBehaviour
{
2025-06-27 07:13:39 +02:00
/* ------------------ INSPECTOR FIELDS ------------------ */
2025-06-23 16:30:18 +02:00
[Header("Room Prefabs")]
2025-06-27 07:13:39 +02:00
[SerializeField] private List<GameObject> mapPrefab = new();
2025-06-24 16:59:41 +02:00
2025-06-23 16:30:18 +02:00
[Header("Player")]
[SerializeField] private GameObject Player;
2025-06-24 16:59:41 +02:00
2025-06-23 16:30:18 +02:00
[Header("Corridor Prefabs")]
[SerializeField] private GameObject CorridorStraight;
[SerializeField] private GameObject CorridorStraightUnlit;
[SerializeField] private GameObject DoorCorridor;
2025-06-25 23:54:00 +02:00
[Header("Layout")]
[SerializeField] private MapLayout layout;
2025-06-24 16:59:41 +02:00
2025-06-23 16:30:18 +02:00
[Header("Generation Settings")]
[SerializeField] private int RoomDistance = 3;
2025-06-23 16:30:18 +02:00
2025-06-27 07:13:39 +02:00
private readonly Dictionary<Vector2Int, GameObject> gridToRoom = new();
2025-06-25 23:54:00 +02:00
private readonly Vector3 roomOriginOffset = Vector3.zero;
2025-06-23 16:30:18 +02:00
2025-06-27 07:13:39 +02:00
2025-06-25 23:54:00 +02:00
void Start() => GenerateFromLayout();
2025-06-27 07:13:39 +02:00
2025-06-25 23:54:00 +02:00
private void GenerateFromLayout()
2025-06-23 16:30:18 +02:00
{
2025-06-25 23:54:00 +02:00
if (layout == null || string.IsNullOrWhiteSpace(layout.grid))
{
Debug.LogError("Layout asset není přiřazený nebo je prázdný!");
return;
}
2025-06-24 16:59:41 +02:00
2025-06-25 23:54:00 +02:00
gridToRoom.Clear();
2025-06-24 16:59:41 +02:00
2025-06-27 07:13:39 +02:00
/* ----------- Create a spawn room ----------- */
2025-06-25 23:54:00 +02:00
GameObject spawnPrefab = mapPrefab[0];
2025-06-27 07:13:39 +02:00
Vector3 cellSize = GetPrefabXZ(spawnPrefab);
2025-06-24 16:59:41 +02:00
2025-06-27 07:13:39 +02:00
/* ---------- Text layout to grid ---------- */
2025-06-25 23:54:00 +02:00
string[] lines = layout.grid
.Split('\n')
2025-06-27 07:13:39 +02:00
.Select(line => line.TrimEnd('\r'))
.Where(line => !string.IsNullOrWhiteSpace(line))
2025-06-25 23:54:00 +02:00
.ToArray();
2025-06-24 16:59:41 +02:00
2025-06-27 07:13:39 +02:00
Vector2 firstRoomPos = GetFirstOrLastRoom(lines);
int bottomRowIdx = (int)firstRoomPos.y;
int spawnGridX = (int)firstRoomPos.x;
2025-06-24 16:59:41 +02:00
2025-06-27 07:13:39 +02:00
/* ---------- Create spawn room properly ---------- */
Vector3 spawnPos = roomOriginOffset + new Vector3(
spawnGridX * (cellSize.x + RoomDistance), // X
0,
0);
2025-06-25 23:54:00 +02:00
2025-06-27 07:13:39 +02:00
GameObject spawnRoom = Instantiate(spawnPrefab, spawnPos, Quaternion.identity, transform);
gridToRoom[new Vector2Int(spawnGridX, 0)] = spawnRoom;
2025-06-25 23:54:00 +02:00
2025-06-27 07:13:39 +02:00
/* ---------- Instantiate player ---------- */
if (Player)
{
Vector3 playerPos = spawnPos + new Vector3(0, 1, 3);
GameObject playerInstance = Instantiate(Player, playerPos, Quaternion.identity, transform);
Transform controllerObj = playerInstance.transform.Find("Player");
if (controllerObj != null)
{
controllerObj.tag = "Player";
}
}
else
{
Debug.LogWarning("Player prefab není přiřazený, hráč nebude umístěn do mapy.");
2025-06-27 07:13:39 +02:00
}
2025-06-24 16:59:41 +02:00
2025-06-27 07:13:39 +02:00
/* ---------- Build the rest of rooms ---------- */
2025-06-25 23:54:00 +02:00
2025-06-27 07:13:39 +02:00
BuildRooms(lines, bottomRowIdx, cellSize);
2025-06-24 16:59:41 +02:00
2025-06-27 07:13:39 +02:00
/* ---------- Open walls based on aproximity to other rooms ---------- */
foreach (var keyValuePair in gridToRoom)
2025-06-25 23:54:00 +02:00
{
2025-06-27 07:13:39 +02:00
Vector2Int g = keyValuePair.Key;
RoomHandler rh = keyValuePair.Value.GetComponent<RoomHandler>();
2025-06-25 23:54:00 +02:00
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);
2025-06-27 07:13:39 +02:00
rh.SetEntrances(northOpen: north, southOpen: south, eastOpen: east, westOpen: west);
2025-06-25 23:54:00 +02:00
}
2025-06-27 07:13:39 +02:00
/* ---------- 5) CHODBY ---------- */
2025-06-25 23:54:00 +02:00
BuildCorridors();
2025-06-23 16:30:18 +02:00
}
2025-06-27 07:13:39 +02:00
/// <summary>
/// Build corridors between rooms
/// </summary>
/// <returns></returns>
2025-06-25 23:54:00 +02:00
private void BuildCorridors()
2025-06-23 16:30:18 +02:00
{
2025-06-27 07:13:39 +02:00
float straightZ = CorridorStraight.GetComponent<PrefabSize>().prefabSize.y;
float straightX = CorridorStraight.GetComponent<PrefabSize>().prefabSize.x;
2025-06-25 23:54:00 +02:00
Vector2Int[] stepDirs = { Vector2Int.right, Vector2Int.up };
2025-06-27 07:13:39 +02:00
foreach (var kv in gridToRoom)
2025-06-25 23:54:00 +02:00
{
2025-06-27 07:13:39 +02:00
Vector2Int cell = kv.Key;
GameObject roomA = kv.Value;
2025-06-25 23:54:00 +02:00
foreach (Vector2Int dir in stepDirs)
{
2025-06-27 07:13:39 +02:00
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;
}
2025-06-25 23:54:00 +02:00
2025-06-27 07:13:39 +02:00
Vector3 start = roomA.transform.position + axis * halfA;
Vector3 end = roomB.transform.position + axisNeg * halfB;
2025-06-25 23:54:00 +02:00
2025-06-27 07:13:39 +02:00
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))
{
2025-06-27 07:13:39 +02:00
Debug.LogWarning($"Neznámý znak '{ch}' v layoutu ignoruji.");
continue;
}
2025-06-27 07:13:39 +02:00
int idx = ch - '0';
if (idx >= mapPrefab.Count)
{
2025-06-27 07:13:39 +02:00
Debug.LogWarning($"Index {idx} mimo rozsah mapPrefab!");
continue;
}
2025-06-25 23:54:00 +02:00
2025-06-27 07:13:39 +02:00
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;
2025-06-25 23:54:00 +02:00
2025-06-27 07:13:39 +02:00
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;
}
2025-06-25 23:54:00 +02:00
}
2025-06-27 07:13:39 +02:00
if (colIdx != -1) break;
}
if (colIdx == -1)
{
Debug.LogError("Layout neobsahuje žádnou číslici (místnost)!");
return Vector2.zero;
2025-06-25 23:54:00 +02:00
}
2025-06-27 07:13:39 +02:00
return new Vector2(spawnGridX, colIdx);
2025-06-25 23:54:00 +02:00
}
private void CreateStraightCorridor(
Vector3 start, Vector3 end, Vector3 axis, Quaternion rot,
float stepLenZ, float stepLenX)
{
2025-06-27 07:13:39 +02:00
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));
2025-06-25 23:54:00 +02:00
2025-06-27 07:13:39 +02:00
Vector3 segPos = start + axis * (step * 0.5f);
var door = Instantiate(DoorCorridor, segPos, rot, transform);
door.GetComponent<DoorAnimation>()?.ToggleDoor();
2025-06-25 23:54:00 +02:00
2025-06-27 07:13:39 +02:00
for (int i = 1; i < count; i++)
2025-06-25 23:54:00 +02:00
{
GameObject prefab = (i % 2 == 0) ? CorridorStraight : CorridorStraightUnlit;
2025-06-27 07:13:39 +02:00
segPos = start + axis * (i * step + step * 0.5f);
2025-06-25 23:54:00 +02:00
Instantiate(prefab, segPos, rot, transform);
2025-06-23 16:30:18 +02:00
}
}
2025-06-27 07:13:39 +02:00
/// <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);
}
}