@@ -1,11 +1,13 @@
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 List < GameObject > ( ) ;
[SerializeField] private List < GameObject > mapPrefab = new ( ) ;
[Header("Player")]
[SerializeField] private GameObject Player ;
@@ -21,12 +23,13 @@ public class MapGenManager : MonoBehaviour
[Header("Generation Settings")]
[SerializeField] private int RoomDistance = 3 ;
private Dictionary < Vector2Int , GameObject > gridToRoom = new ( ) ;
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 ) )
@@ -37,167 +40,216 @@ public class MapGenManager : MonoBehaviour
gridToRoom . Clear ( ) ;
/* ---------- 0) VYTVOŘÍME SPAWN-ROOM ---------- */
/* ----------- Create a spawn room - ---------- */
GameObject spawnPrefab = mapPrefab [ 0 ] ;
PrefabSize spawnSizeComp = spawn Prefab. GetComponent < PrefabSize > ( ) ;
Vector3 cellSize = spawnSizeComp ! = null
? new Vector3 ( spawnSizeComp . prefabSize . x , 0 , spawnSizeComp . prefabSize . y )
: new Vector3 ( 10 , 0 , 10 ) ; // fallback
Vector3 cellSize = Get PrefabXZ ( 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 ) ;
Vector3 spawnPos = roomOriginOffset ;
GameObject spawnRoom = Instantiate ( spawnPrefab , spawnPos , Quaternion . identity , transform ) ;
gridToRoom [ new Vector2Int ( 0 , 0 ) ] = spawnRoom ;
gridToRoom [ new Vector2Int ( spawnGridX , 0 ) ] = spawnRoom ;
// hráče necháme na spawnu
if ( Player ! = null )
/* ---------- Instantiate player ---------- */
if ( Player )
{
Vector3 playerPos = spawnPos + new Vector3 ( 0 , 1 , 3 ) ;
Instantiate ( Player , playerPos , Quaternion . identity , transform ) ;
}
/* ---------- 1) PARSOVÁNÍ LAYOUTU ---------- */
string [ ] lines = layout . grid
. Split ( '\n' )
. Select ( l = > l . TrimEnd ( '\r' ) )
. Where ( l = > ! string . IsNullOrEmpty ( l ) )
. ToArray ( ) ;
/* ---------- Build the rest of rooms ---------- */
int rows = lines . Length ;
BuildRooms ( lines , bottomRowIdx , cellSize ) ;
/* ---------- 2) SMYČKA PŘES GRID --- ---------- */
for ( int z = 0 ; z < rows ; z + + )
/* ---------- Open walls based on aproximity to other rooms ---------- */
foreach ( var keyValuePair in gridToRoom )
{
string line = lines [ rows - 1 - z ] ; // dolní řádek layoutu = nejnižší Z v textu
for ( int x = 0 ; x < line . Length ; x + + )
{
char c = line [ x ] ;
if ( c = = '-' ) continue ; // prázdné místo
if ( ! char . IsDigit ( c ) )
{
Debug . LogWarning ( $"Neznámý znak {c} v layoutu – ignorováno." ) ;
continue ;
}
int index = c - '0' ;
if ( index > = mapPrefab . Count )
{
Debug . LogWarning ( $"Index {index} mimo rozsah mapPrefab!" ) ;
continue ;
}
GameObject prefab = mapPrefab [ index ] ;
PrefabSize roomSize = prefab . GetComponent < PrefabSize > ( ) ;
Vector3 rCell = roomSize ! = null
? new Vector3 ( roomSize . prefabSize . x , 0 , roomSize . prefabSize . y )
: cellSize ; // použij spawn-size jako fallback
Vector3 worldPos = roomOriginOffset + new Vector3 (
x * ( cellSize . x + RoomDistance ) ,
0 ,
( z + 1 ) * ( cellSize . z + RoomDistance ) ) ;
GameObject room = Instantiate ( prefab , worldPos , Quaternion . identity , transform ) ;
gridToRoom [ new Vector2Int ( x , z + 1 ) ] = room ;
}
}
/* ---------- 3) OTEVŘEME ZDI VŠEM MÍSTNOSTEM ---------- */
foreach ( var kvp in gridToRoom )
{
Vector2Int g = kvp . Key ;
RoomHandler rh = kvp . Value . GetComponent < RoomHandler > ( ) ;
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 ) ;
rh . SetEntrances ( northOpen : north , southOpen : south , eastOpen : east , westOpen : west ) ;
}
/* ---------- 5) CHODBY ---------- */
BuildCorridors ( ) ;
}
/// <summary>
/// Build corridors between rooms
/// </summary>
/// <returns></returns>
private void BuildCorridors ( )
{
// velikosti přímých dílů
float straightLengthZ = CorridorStraight . GetComponent < PrefabSize > ( ) . prefabSize . y ;
float straightLengthX = CorridorStraight . GetComponent < PrefabSize > ( ) . prefabSize . x ;
// Abychom každý pár řešili jen jednou, projdeme jen Right a Up souseda
float straightZ = CorridorStraight . GetComponent < PrefabSize > ( ) . prefabSize . y ;
float straightX = CorridorStraight . GetComponent < PrefabSize > ( ) . prefabSize . x ;
Vector2Int [ ] stepDirs = { Vector2Int . right , Vector2Int . up } ;
foreach ( var kvp in gridToRoom )
foreach ( var kv in gridToRoom )
{
Vector2Int cell = kvp . Key ;
GameObject roomA = kvp . Value ;
Vector2Int cell = kv . Key ;
GameObject roomA = kv . Value ;
foreach ( Vector2Int dir in stepDirs )
{
Vector2Int neigh Key = cell + dir ;
if ( ! gridToRoom . TryGetValue ( neigh Key , out GameObject roomB ) )
continue ; // není soused – žádná chodba
Vector2Int nKey = cell + dir ;
if ( ! gridToRoom . TryGetValue ( nKey , out GameObject roomB ) ) continue ;
// 1) Urči směr v prostoru
Vector3 axis , axisNeg ; // +směr a − směr podél stejné osy
float halfA , halfB ; // půl šířky/hloubky pro start/end bod
Quaternion corridorRot ; // natočení pro koridor
Vector3 axis , axisNeg ;
float halfA , halfB ;
Quaternion rot ;
if ( dir = = Vector2Int . right ) // grid +X (svět +X)
if ( dir = = Vector2Int . right )
{
axis = Vector3 . right ;
axisNeg = Vector3 . left ;
halfA = roomA . GetComponent < PrefabSize > ( ) . prefabSize . x * 0.5f ;
halfB = roomB . GetComponent < PrefabSize > ( ) . prefabSize . x * 0.5f ;
corridorRot = Quaternion . Euler ( 0 , 90 , 0 ) ; // koridor leží po X
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 // dir == Vector2Int.up → svět +Z
else
{
axis = Vector3 . forward ;
axisNeg = Vector3 . back ;
halfA = roomA . GetComponent < PrefabSize > ( ) . prefabSize . y * 0.5f ;
halfB = roomB . GetComponent < PrefabSize > ( ) . prefabSize . y * 0.5f ;
corridorRot = Quaternion . identity ; // prefab míří po +Z
axis = Vector3 . forward ; axisNeg = Vector3 . back ;
halfA = GetPrefabXZ ( roomA ) . z * 0.5f ;
halfB = GetPrefabXZ ( roomB ) . z * 0.5f ;
rot = Quaternion . identity ;
}
// 2) Vypočti start a end body (středy zdí)
Vector3 startPos = roomA . transform . position + axis * halfA ;
Vector3 endPos = roomB . transform . position + axisNeg * halfB ;
Vector3 start = roomA . transform . position + axis * halfA ;
Vector3 end = roomB . transform . position + axisNeg * halfB ;
// 3) Vytvoř sekvenci dílů
CreateStraightCorridor ( startPos , endPos , axis , corridorRot ,
straightLengthZ , straightLengthX ) ;
CreateStraightCorridor ( start , end , axis , rot , straightZ , straightX ) ;
}
}
}
/// Vytvoří přímý koridor od startu k endu podél 'axis'.
/// <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 fullD ist = Vector3 . Distance ( start , end ) ;
float stepLen = Mathf . Approximately ( Mathf . Abs ( axis . z ) , 1f ) ? stepLenZ : stepLenX ;
float d ist = Vector3 . Distance ( start , end ) ;
float step = Mathf . Approximately ( Mathf . Abs ( axis . z ) , 1f ) ? stepLenZ : stepLenX ;
int count = Mathf . Max ( 1 , Mathf . FloorToInt ( dist / step ) ) ;
int segmentCount = Mathf . Max ( 1 , Mathf . FloorToInt ( fullDist / stepLen ) ) ;
Vector3 segPos = start + axis * ( step * 0.5f ) ;
var door = Instantiate ( DoorCorridor , segPos , rot , transform ) ;
door . GetComponent < DoorController > ( ) ? . OpenDoor ( ) ;
// 0) dveřní díl
Vector3 segPos = start + axis * ( stepLen * 0.5f ) ;
GameObject door = Instantiate ( DoorCorridor , segPos , rot , transform ) ;
door . GetComponent < DoorController > ( ) ? . OpenDoor ( ) ; // pokud existuje
// 1) zbytek – střídavě osvětlené / neosvětlené
for ( int i = 1 ; i < segmentCount ; i + + )
for ( int i = 1 ; i < count ; i + + )
{
GameObject prefab = ( i % 2 = = 0 ) ? CorridorStraight : CorridorStraightUnlit ;
segPos = start + axis * ( i * stepLen + stepLen * 0.5f ) ;
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 ) ;
}
}