@@ -1,7 +1,6 @@
using System.Collections.Generic ;
using UnityEngine ;
using System.Linq ;
using System.Runtime.CompilerServices ;
using UnityEngine ;
public class MapGenManager : MonoBehaviour
{
@@ -24,12 +23,9 @@ public class MapGenManager : MonoBehaviour
[SerializeField] private int RoomDistance = 3 ;
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,228 +33,216 @@ public class MapGenManager : MonoBehaviour
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 ) ;
/* ---------- Text layout to grid ---------- */
string [ ] lines = layout . g rid
. Split ( '\n' )
. Select ( line = > line . TrimEnd ( '\r' ) )
. Where ( line = > ! string . IsNullOrWhiteSpace ( line ) )
string [ ] lines = layout . grid . Split ( '\n' )
. Select ( l = > l . T rimEnd ( '\r' ) )
. Where ( l = > ! string . IsNullOrWhiteSpace ( l ) )
. ToArray ( ) ;
Vector2 first = GetFirstOrLastRoom ( lines ) ;
int bottomRow = ( int ) first . y ;
int spawnX = ( int ) first . x ;
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 = new Vector3 ( spawnX * ( cellSize . x + RoomDistance ) , 0 , 0 ) ;
GameObject spawnRoom = Instantiate ( spawnPrefab , spawnPos , Quaternion . identity , transform ) ;
gridToRoom [ new Vector2Int ( spawnGrid X , 0 ) ] = spawnRoom ;
gridToRoom [ new Vector2Int ( spawnX , 0 ) ] = spawnRoom ;
/* ---------- Instantiate player ---------- */
/* ----------- Spawn the 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." ) ;
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 the rest of rooms ---------- */
/* ----------- Build rooms ----------- */
BuildRooms ( lines , bottomRow , cellSize ) ;
BuildRooms ( lines , bottomRowIdx , cellSize ) ;
/* ---------- Open walls based on aproximity to other rooms ---------- */
foreach ( var keyValuePair in gridToRoom )
/* ----------- Set entrances ----------- */
foreach ( var kv in gridToRoom )
{
Vector2Int g = keyValuePair . Key ;
RoomHandler rh = keyValuePair . Value . GetComponent < RoomHandler > ( ) ;
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 ( northOpen : north , southOpen : south , eastOpen : east , westOpen : west ) ;
rh . SetEntrances ( north , south , east , west ) ;
}
/* ---------- 5) CHODBY ---------- */
/* ----------- Build corridors - ---------- */
BuildCorridors ( ) ;
/* ----------- Toggle all doors ----------- */
foreach ( var keyValuePair in gridToRoom )
{
RoomHandler rh = keyValuePair . Value . GetComponent < RoomHandler > ( ) ;
rh . ToggleAllDoors ( ) ;
}
}
/// <summary>
/// Build corridors between rooms
/// Builds corridors and doors between rooms.
/// </summary>
/// <returns></returns>
private void BuildCorridors ( )
{
float straightZ = CorridorStraight . GetComponent < PrefabSize > ( ) . prefabSize . y ;
float straightX = CorridorStraight . GetComponent < PrefabSize > ( ) . prefabSize . x ;
Vector2Int [ ] stepDirs = { Vector2Int . right , Vector2Int . up } ;
// 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 stepDir s)
foreach ( Vector2Int dir in direction s)
{
Vector2Int nKey = cell + dir ;
if ( ! gridToRoom . TryGetValue ( nKey , out GameObject roomB ) ) continue ;
Vector3 axis , axisNeg ;
float halfA , halfB ;
Quaternion rot ;
// Handle geometry
Vector3 axis ; float lenStraight , lenDoor ; Quaternion rot ;
if ( dir = = Vector2Int . right )
{
axis = Vector3 . right ; axisNeg = Vector3 . left ;
halfA = GetPrefabXZ ( roomA ) . x * 0.5f ;
halfB = GetPrefabXZ ( roomB ) . x * 0.5f ;
axis = Vector3 . right ;
lenStraight = straightLenX ;
lenDoor = doorLenX ;
rot = Quaternion . Euler ( 0 , 90 , 0 ) ;
}
else
else // Vector2Int.up
{
axis = Vector3 . forward ; axisNeg = Vector3 . back ;
halfA = GetPrefabXZ ( roomA ) . z * 0.5f ;
halfB = GetPrefabXZ ( roomB ) . z * 0.5f ;
axis = Vector3 . forward ;
lenStraight = straightLenZ ;
lenDoor = doorLenZ ;
rot = Quaternion . identity ;
}
Vector3 start = roomA . transform . position + axis * halfA ;
Vector3 end = roomB . transform . position + axisNeg * halfB ;
// 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 ;
CreateStraightCorridor ( start , end , axis , rot , straightZ , straightX ) ;
}
// 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>
/// Build all rooms based on the layout grid
/// 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>
/// <param name="lines"></param>
/// <param name="bottomRowIdx"></param>
/// <param name="cellSize"></param>
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 )
{
int rows = lines . Length ;
for ( int rowsIter = 0 ; rowsIter < rows ; rowsIter + + )
for ( int r = 0 ; r < lines . Length ; r + + )
{
string line = lines [ rowsIter ] ;
int gridZ = bottomRowIdx - rowsIter + 1 ;
string line = lines [ r ] ;
int gridZ = bottomRowIdx - r + 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 ;
}
if ( ch = = '-' | | ! char . IsDigit ( ch ) ) 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 ) ;
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 ;
}
}
}
/// <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 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 ) ) ;
Vector3 segPos = start + axis * ( step * 0.5f ) ;
var door = Instantiate ( DoorCorridor , segPos , rot , transform ) ;
door . GetComponent < DoorAnimation > ( ) ? . ToggleDoor ( ) ;
for ( int i = 1 ; i < count ; i + + )
{
GameObject prefab = ( i % 2 = = 0 ) ? CorridorStraight : CorridorStraightUnlit ;
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>
/* ============================================================ */
/* 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 ;
}
}