Merge branch 'roomGen'

This commit is contained in:
2025-06-27 07:15:26 +02:00
12 changed files with 426 additions and 160 deletions

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a08c64b8785870549887368e2a81e2db
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,19 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 990d24eb53743184e9b379e68eec63e1, type: 3}
m_Name: MapLayout1
m_EditorClassIdentifier:
grid: '123-2235454121
412123
11---4'

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 0c4ecdc10844c394b92ccd08ee36d635
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 78cd8de59ec022844907c273b05c0395
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -187,8 +187,8 @@ Transform:
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1.8, y: 1.8, z: 1.8}
m_ConstrainProportionsScale: 0
m_LocalScale: {x: 1.5, y: 1.5, z: 1.5}
m_ConstrainProportionsScale: 1
m_Children:
- {fileID: 1872598832695960773}
- {fileID: 1302341320372733696}

View File

@@ -0,0 +1,14 @@
using UnityEngine;
[CreateAssetMenu(fileName = "MapLayout", menuName = "Scriptable Objects/MapLayout")]
public class MapLayout : ScriptableObject
{
/// <summary>
/// 2D grid. Ka<4B>d<EFBFBD> <20><>dek je jeden <20><><EFBFBD>dek<65> mapy, naho<68>e = vy<76><79><EFBFBD> Z.
/// <20><>slice = index prefab-u v MapGenManager.mapPrefab,
/// '-' = pr<70>zdn<64> m<>sto,
/// 'S' = startovn<76> m<>stnost.
/// </summary>
[TextArea(5, 20)]
public string grid;
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 990d24eb53743184e9b379e68eec63e1

View File

@@ -1,11 +1,23 @@
using UnityEngine;
public class CreateEntrances : MonoBehaviour
///<summary>
/// Object handling Room logic
///</summary>
///
public class RoomHandler : MonoBehaviour
{
[SerializeField] private GameObject wallNorth;
[SerializeField] private GameObject wallSouth;
[SerializeField] private GameObject wallEast;
[SerializeField] private GameObject wallWest;
/// <summary>
/// Creates entrances to corridors leading to other rooms
/// </summary>
/// <param name="northOpen"></param>
/// <param name="southOpen"></param>
/// <param name="eastOpen"></param>
/// <param name="westOpen"></param>
public void SetEntrances(bool northOpen, bool southOpen, bool eastOpen, bool westOpen)
{
wallNorth.SetActive(!northOpen);

View File

@@ -149,20 +149,18 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier:
mapPrefab:
- {fileID: 819094401162878122, guid: 03f2147e5a186fc408b959faa2f97e86, type: 3}
- {fileID: 2783560673348224804, guid: b7111ccd107e56741a9790a50ab8e190, type: 3}
- {fileID: 7069902989848030098, guid: e8e0ba1faefe88f47b927a477b87ed22, type: 3}
- {fileID: 8524395191423973796, guid: 37b1623de3a4b8140bc4fd60f59c6329, type: 3}
- {fileID: 4362243528253867212, guid: 04cd18e800b8e854491a95529a91b3a1, type: 3}
- {fileID: 5061069654894918266, guid: 6ac586441811a834fbf148a96b779734, type: 3}
StartPoint: {fileID: 819094401162878122, guid: 03f2147e5a186fc408b959faa2f97e86, type: 3}
EndPoint: {fileID: 819094401162878122, guid: 03f2147e5a186fc408b959faa2f97e86, type: 3}
Player: {fileID: 6983871523237736218, guid: f0df263e5be65a041848d5a8bab85af1, type: 3}
CorridorStraight: {fileID: 8047827979703692770, guid: 1a5d554c0c76caf4195cae47e098b79d, type: 3}
CorridorStraightUnlit: {fileID: 2016417306107577256, guid: 92d9025262a022a499862d352c2724ee, type: 3}
DoorCorridor: {fileID: 5603417387143431118, guid: 9eccbf5a500a0d2429e6008a713a49fd, type: 3}
layout: {fileID: 11400000, guid: 0c4ecdc10844c394b92ccd08ee36d635, type: 2}
RoomDistance: 40
minRoomsNumber: 3
maxRoomsNumber: 7
--- !u!4 &23489964
Transform:
m_ObjectHideFlags: 0

View File

@@ -1,13 +1,13 @@
using System.Collections.Generic;
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 GameObject StartPoint;
[SerializeField] private GameObject EndPoint;
[SerializeField] private List<GameObject> mapPrefab = new();
[Header("Player")]
[SerializeField] private GameObject Player;
@@ -16,149 +16,240 @@ public class MapGenManager : MonoBehaviour
[SerializeField] private GameObject CorridorStraight;
[SerializeField] private GameObject CorridorStraightUnlit;
[SerializeField] private GameObject DoorCorridor;
/*[SerializeField] private GameObject CorridorL;
[SerializeField] private GameObject CorridorT;
[SerializeField] private GameObject CorridorCross;
[SerializeField] private GameObject CorridorEnd;*/
[Header("Layout")]
[SerializeField] private MapLayout layout;
[Header("Generation Settings")]
[SerializeField] private int RoomDistance = 3;
[SerializeField] private int minRoomsNumber = 3;
[SerializeField] private int maxRoomsNumber = 7;
private List<Vector3> roomPositions = new List<Vector3>();
private List<GameObject> placedRooms = new List<GameObject>();
private readonly Dictionary<Vector2Int, GameObject> gridToRoom = new();
private readonly Vector3 roomOriginOffset = Vector3.zero;
void Start()
void Start() => GenerateFromLayout();
private void GenerateFromLayout()
{
MapGen();
}
private void MapGen()
{
// Clear previous rooms and positions
roomPositions.Clear();
placedRooms.Clear();
// Add Start Point
Vector3 startPos = new Vector3(0, 0, 0);
GameObject startPoint = Instantiate(StartPoint, startPos, Quaternion.identity, transform);
roomPositions.Add(startPos);
placedRooms.Add(startPoint);
startPoint.GetComponent<CreateEntrances>().SetEntrances(false, false, true, false);
// Instantiate the player at the starting position
GameObject player = Instantiate(Player, new Vector3(startPos.x, 1, startPos.z - 5), Quaternion.identity, transform);
// Generate a random number of rooms
int roomCount = Random.Range(minRoomsNumber, maxRoomsNumber);
// Place Generate Rooms
for (int i = 0; i < roomCount; i++)
if (layout == null || string.IsNullOrWhiteSpace(layout.grid))
{
GameObject roomPrefab = mapPrefab[Random.Range(0, mapPrefab.Count)];
Vector3 roomPos = GetGridPosition(roomPrefab);
GameObject room = Instantiate(roomPrefab, roomPos, Quaternion.identity, transform);
placedRooms.Add(room);
roomPositions.Add(roomPos);
room.GetComponent<CreateEntrances>().SetEntrances(false, false, true, true);
Debug.LogError("Layout asset není přiřazený nebo je prázdný!");
return;
}
// Add End Point
GameObject endPoint = Instantiate(EndPoint, GetGridPosition(EndPoint), Quaternion.identity, transform);
roomPositions.Add(endPoint.transform.position);
placedRooms.Add(endPoint);
endPoint.GetComponent<CreateEntrances>().SetEntrances(false, false, false, false);
gridToRoom.Clear();
// Create corridors between rooms
for (int i = 0; i < roomPositions.Count - 1; i++)
/* ----------- Create a spawn room ----------- */
GameObject spawnPrefab = mapPrefab[0];
Vector3 cellSize = GetPrefabXZ(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);
GameObject spawnRoom = Instantiate(spawnPrefab, spawnPos, Quaternion.identity, transform);
gridToRoom[new Vector2Int(spawnGridX, 0)] = spawnRoom;
/* ---------- Instantiate player ---------- */
if (Player)
{
Vector3 startRoomPos = roomPositions[i];
PrefabSize startRoom = placedRooms[i].GetComponent<PrefabSize>();
Vector3 endRoomPos = roomPositions[i + 1];
PrefabSize endRoom = placedRooms[i + 1].GetComponent<PrefabSize>();
Vector3 firstCorridorPos = new Vector3(
startRoomPos.x,
startRoomPos.y,
startRoomPos.z + startRoom.prefabSize.y / 2
);
Vector3 lastCorridorPos = new Vector3(
endRoomPos.x,
endRoomPos.y,
endRoomPos.z - endRoom.prefabSize.y / 2
);
CreateCorridor(firstCorridorPos, lastCorridorPos);
Vector3 playerPos = spawnPos + new Vector3(0, 1, 3);
Instantiate(Player, playerPos, Quaternion.identity, transform);
}
}
private Vector3 GetGridPosition(GameObject roomPrefab)
{
Vector3 lastRoomPos = roomPositions[roomPositions.Count - 1];
PrefabSize lastRoom = placedRooms[placedRooms.Count - 1].GetComponent<PrefabSize>();
PrefabSize roomSize = roomPrefab.GetComponent<PrefabSize>();
/* ---------- Build the rest of rooms ---------- */
Vector3 roomPos = new Vector3(
lastRoomPos.x,
lastRoomPos.y - 0.01f,
lastRoomPos.z + lastRoom.prefabSize.y / 2f + roomSize.prefabSize.y / 2f + RoomDistance
);
BuildRooms(lines, bottomRowIdx, cellSize);
return roomPos;
}
private void CreateCorridor(Vector3 start, Vector3 end)
{
// Calculate the distance
float distance = Vector3.Distance(start, end);
PrefabSize corridorSize = CorridorStraight.GetComponent<PrefabSize>();
PrefabSize corridorUnlitSize = CorridorStraightUnlit.GetComponent<PrefabSize>();
PrefabSize doorSize = DoorCorridor.GetComponent<PrefabSize>();
// Calculate the number of corridors needed
int corridorCount = Mathf.FloorToInt(distance / corridorSize.prefabSize.y);
Debug.Log($"Creating {corridorCount} corridors from {start} to {end}");
// Create corridors
//Start with door
if (corridorCount > 0)
/* ---------- Open walls based on aproximity to other rooms ---------- */
foreach (var keyValuePair in gridToRoom)
{
Vector3 doorPos = new Vector3(
start.x,
start.y,
start.z + doorSize.prefabSize.y * 0.5f
);
Quaternion doorRotation = Quaternion.Euler(0, 0, 0);
GameObject doorCorridor = Instantiate(DoorCorridor, doorPos, doorRotation, transform);
doorCorridor.GetComponent<DoorController>().OpenDoor();
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);
for (int i = 1; i < corridorCount; i++)
rh.SetEntrances(northOpen: north, southOpen: south, eastOpen: east, westOpen: west);
}
/* ---------- 5) CHODBY ---------- */
BuildCorridors();
}
/// <summary>
/// Build corridors 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 };
foreach (var kv in gridToRoom)
{
Vector2Int cell = kv.Key;
GameObject roomA = kv.Value;
foreach (Vector2Int dir in stepDirs)
{
if (i % 2 != 0)
Vector2Int nKey = cell + dir;
if (!gridToRoom.TryGetValue(nKey, out GameObject roomB)) continue;
Vector3 axis, axisNeg;
float halfA, halfB;
Quaternion rot;
if (dir == Vector2Int.right)
{
// Create straight corridor
Vector3 pos = new Vector3(
start.x,
start.y,
start.z + i * corridorSize.prefabSize.y + corridorSize.prefabSize.y * 0.5f
);
Quaternion rotation = Quaternion.Euler(0, 0, 0);
GameObject corridor = Instantiate(CorridorStraight, pos, rotation, transform);
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
{
// Create unlit corridor
Vector3 pos = new Vector3(
start.x,
start.y,
start.z + i * corridorUnlitSize.prefabSize.y + corridorUnlitSize.prefabSize.y * 0.5f
);
Quaternion rotation = Quaternion.Euler(0, 0, 0);
GameObject corridorUnlit = Instantiate(CorridorStraightUnlit, pos, rotation, transform);
axis = Vector3.forward; axisNeg = Vector3.back;
halfA = GetPrefabXZ(roomA).z * 0.5f;
halfB = GetPrefabXZ(roomB).z * 0.5f;
rot = Quaternion.identity;
}
}
Vector3 start = roomA.transform.position + axis * halfA;
Vector3 end = roomB.transform.position + axisNeg * halfB;
CreateStraightCorridor(start, end, axis, rot, straightZ, straightX);
}
}
// If there is not enough space to create corridors, throw an exception
else throw new System.Exception("Not enough space to create corridors");
}
}
/// <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 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<DoorController>()?.OpenDoor();
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>
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);
}
}