connect multiple rooms with doors

This commit is contained in:
2022-11-03 21:20:00 +01:00
parent bce88d0504
commit 807eae1c62
24 changed files with 391 additions and 88 deletions

View File

@@ -1,20 +1,44 @@
namespace Escape_Room_Engine.Engine.Scripts
using UnityEngine;
namespace Escape_Room_Engine.Engine.Scripts
{
public readonly struct Dimensions
public struct Dimensions
{
internal Dimensions(float width, float length, float x, float y)
internal Dimensions(int width, int length, int x, int z)
{
Width = width;
Length = length;
X = x;
Y = y;
this.width = width;
this.length = length;
this.x = x;
this.z = z;
}
public float Width { get; }
public float Length { get; }
public float X { get; }
public float Y { get; }
public int width;
public int length;
public int x;
public int z;
public override string ToString() => $"({Width}, {Length}) at ({X}, {Y})";
public Vector2Int Position
{
get => new(x, z);
set
{
x = value.x;
z = value.y;
}
}
public Vector2Int Size
{
get => new(width, length);
set
{
width = value.x;
length = value.y;
}
}
public int Area => width * length;
public override string ToString() => $"({width}, {length}) at ({x}, {z})";
}
}

View File

@@ -1,18 +0,0 @@
namespace Escape_Room_Engine.Engine.Scripts
{
public enum DoorType
{
Entrance = ModuleType.DoorEntrance, Exit = ModuleType.DoorExit
}
public class DoorModule : Module
{
public bool IsEntrance => IsType((ModuleType)DoorType.Entrance);
public bool IsExit => IsType((ModuleType)DoorType.Exit);
internal DoorModule(DoorType type)
{
_types.Add((ModuleType)type);
}
}
}

View File

@@ -17,7 +17,10 @@ namespace Escape_Room_Engine.Engine.Scripts.Editor
public void CreateGUI()
{
_generateRoomButton = new Button(GenerateRoom);
_generateRoomButton = new Button(GenerateRoom)
{
text = "Generate Room"
};
rootVisualElement.Add(_generateRoomButton);
@@ -29,7 +32,7 @@ namespace Escape_Room_Engine.Engine.Scripts.Editor
{
if (EditorApplication.isPlaying)
{
Engine.DefaultEngine.DisposeOldestRoom();
Engine.DefaultEngine.HideOldestRoom();
Engine.DefaultEngine.GenerateRoom();
UpdateUI();
}
@@ -38,7 +41,6 @@ namespace Escape_Room_Engine.Engine.Scripts.Editor
private void UpdateUI()
{
_generateRoomButton.SetEnabled(EditorApplication.isPlaying);
_generateRoomButton.text = Engine.DefaultEngine.NumberOfRooms == 0 ? "Generate Room" : "Regenerate Room";
}
}
}

View File

@@ -1,20 +1,34 @@
using System;
using System.Collections.Generic;
using Escape_Room_Engine.Engine.Scripts.Modules;
using UnityEngine;
using Logger = Escape_Room_Engine.Engine.Scripts.Utilities.Logger;
using LogType = Escape_Room_Engine.Engine.Scripts.Utilities.LogType;
using Random = UnityEngine.Random;
namespace Escape_Room_Engine.Engine.Scripts
{
public class Engine : MonoBehaviour
{
public static Engine DefaultEngine => FindObjectOfType<Engine>();
public static Engine DefaultEngine
{
get
{
if (_foundEngine == null)
{
_foundEngine = FindObjectOfType<Engine>();
}
return _foundEngine;
}
}
private static Engine _foundEngine;
public Material roomMaterial;
[Tooltip("The minimum size that should be allowed for rooms.")]
public Vector2Int minRoomSize;
[Tooltip("The minimum size that should be allowed for rooms.")] public Vector2Int minRoomSize;
[Tooltip("The size of the physical play space available to the engine.")] public Vector2Int playSpace;
public int NumberOfRooms => _rooms.Count;
private int NumberOfRooms => _rooms.Count;
private readonly List<Room> _rooms = new(1);
private GameObject _playSpaceOrigin;
@@ -28,8 +42,10 @@ namespace Escape_Room_Engine.Engine.Scripts
public void GenerateRoom()
{
Logger.Log("Generating room...", LogType.RoomGeneration);
// get the last entrance from the newest room or create a spawn passage with no entrance door for where the player will start
var entrance = NumberOfRooms > 0 ? _rooms[0].exit : new Passage();
var entrance = NumberOfRooms > 0 ? _rooms[0].exit : new SpawnPassage();
var room = new Room(entrance);
_rooms.Add(room);
@@ -38,20 +54,63 @@ namespace Escape_Room_Engine.Engine.Scripts
room.InstantiateRoom(_playSpaceOrigin.transform, (_rooms.Count - 1).ToString());
}
private void GenerateSpace(Room room, Passage from)
private void GenerateSpace(Room room, Passage entrance)
{
var exit = new Passage();
var space = new Space(GenerateSpaceDimensions(), from, exit);
Logger.Log("Generating space...", LogType.RoomGeneration);
// create space
var rrDimensions = GenerateSpaceDimensions(entrance.rrPosition);
var space = new Space(rrDimensions, entrance);
// add exit
var exitDoor = new DoorModule(DoorType.Exit, space);
exitDoor.Place(new Vector2Int(
Random.Range(0, rrDimensions.width),
Random.Range(0, rrDimensions.length)
));
var exit = new Passage(exitDoor);
space.AddModule(exitDoor);
room.AddSpace(space, exit);
}
private Dimensions GenerateSpaceDimensions()
/// <summary>
/// Generate space dimensions that fit the required size constraints and cover the position of the entrance.
/// </summary>
/// <param name="entranceRrPosition">The room relative (<i>RR</i>) position of the entrance.</param>
/// <returns>The generated room relative (<i>RR</i>) dimensions.</returns>
private Dimensions GenerateSpaceDimensions(Vector2Int entranceRrPosition)
{
var xMin = Random.Range(0, playSpace.x - minRoomSize.x + 1);
var xMax = Random.Range(xMin + minRoomSize.x, playSpace.x + 1);
var yMin = Random.Range(0, playSpace.y - minRoomSize.y + 1);
var yMax = Random.Range(yMin + minRoomSize.y, playSpace.y + 1);
return new Dimensions(xMax - xMin, yMax - yMin, xMin, yMin);
var xMin = Utilities.Utilities.RandomInclusive(
0,
Math.Min(entranceRrPosition.x, playSpace.x - minRoomSize.x)
);
var xMax = Utilities.Utilities.RandomInclusive(
Math.Max(entranceRrPosition.x, xMin + minRoomSize.x),
playSpace.x
);
var zMin = Utilities.Utilities.RandomInclusive(
0,
Math.Min(entranceRrPosition.y, playSpace.y - minRoomSize.y)
);
var zMax = Utilities.Utilities.RandomInclusive(
Math.Max(entranceRrPosition.y, zMin + minRoomSize.y),
playSpace.y
);
var dimensions = new Dimensions(xMax - xMin, zMax - zMin, xMin, zMin);
Logger.Log($"Generated space dimensions {dimensions}", LogType.RoomGeneration);
return dimensions;
}
public void HideOldestRoom()
{
if (NumberOfRooms > 0)
{
_rooms[NumberOfRooms - 1].roomObject.SetActive(false);
}
}
/// <summary>

View File

@@ -1,14 +0,0 @@
using System.Collections.Generic;
namespace Escape_Room_Engine.Engine.Scripts
{
public class Module
{
protected List<ModuleType> _types = new();
public bool IsType(ModuleType type)
{
return _types.Contains(type);
}
}
}

View File

@@ -1,7 +0,0 @@
namespace Escape_Room_Engine.Engine.Scripts
{
public enum ModuleType
{
DoorEntrance, DoorExit
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: cee3c3328d46482ba3f565eeee492f59
timeCreated: 1667812846

View File

@@ -0,0 +1,26 @@
using UnityEngine;
namespace Escape_Room_Engine.Engine.Scripts.Modules
{
public enum DoorType
{
Entrance = ModuleType.DoorEntrance, Exit = ModuleType.DoorExit
}
public class DoorModule : Module
{
public bool IsEntrance => IsType((ModuleType)DoorType.Entrance);
public bool IsExit => IsType((ModuleType)DoorType.Exit);
internal DoorModule(DoorType type, Space space) : base(space)
{
types.Add((ModuleType)type);
srDimensions.Size = Vector2Int.one; // door always has size 1x1
}
public override string ToString()
{
return $"{(IsEntrance ? "Entrance" : IsExit ? "Exit" : "Unknown")} door";
}
}
}

View File

@@ -0,0 +1,68 @@
using System;
using System.Collections.Generic;
using Escape_Room_Engine.Engine.Scripts.Utilities;
using UnityEngine;
using Logger = Escape_Room_Engine.Engine.Scripts.Utilities.Logger;
using LogType = Escape_Room_Engine.Engine.Scripts.Utilities.LogType;
namespace Escape_Room_Engine.Engine.Scripts.Modules
{
public class Module
{
/// <summary>
/// Get the space relative (<i>SR</i>) position of this module.
/// </summary>
internal Vector2Int SrPosition => srDimensions.Position;
/// <summary>
/// Get the room relative (<i>RR</i>) position of this module.
/// </summary>
internal Vector2Int RrPosition => space.ToRoomRelative(SrPosition);
internal readonly Space space;
protected GameObject _moduleObject;
protected readonly List<ModuleType> types = new();
/// <summary>
/// The space relative (<i>SR</i>) dimensions of this module.
/// </summary>
protected Dimensions srDimensions;
internal Module(Space space)
{
this.space = space;
}
internal bool IsType(ModuleType type)
{
return types.Contains(type);
}
/// <summary>
/// Place this module with a position relative to the room.
/// </summary>
/// <param name="rrPosition">The room relative (<i>RR</i>) position of this module. Must be inside the space dimensions.</param>
/// <exception cref="Exception">If the position is not inside the space dimensions.</exception>
internal void PlaceRoomRelative(Vector2Int rrPosition) => Place(space.ToSpaceRelative(rrPosition));
/// <summary>
/// Place this module with a position relative to the space it is in.
/// </summary>
/// <param name="srPosition">The space relative (<i>SR</i>) position of this module. Must be inside the space dimensions.</param>
/// <exception cref="Exception">If the position is not inside the space dimensions.</exception>
internal void Place(Vector2Int srPosition) {
if (space != null && !srPosition.IsInsideRelative(space.rrDimensions))
{
throw new Exception($"Trying to place {this} at {srPosition}, which is outside space dimensions {space.rrDimensions}.");
}
srDimensions.Position = srPosition;
Logger.Log($"{this} has been placed at {srPosition} (SR)", LogType.ModulePlacement);
}
public override string ToString()
{
return $"Module ({string.Join(',', types.ConvertAll(type => type.ToString()))})";
}
}
}

View File

@@ -0,0 +1,7 @@
namespace Escape_Room_Engine.Engine.Scripts.Modules
{
public enum ModuleType
{
DoorEntrance, DoorExit, // door types
}
}

View File

@@ -1,17 +1,47 @@
namespace Escape_Room_Engine.Engine.Scripts
using System;
using Escape_Room_Engine.Engine.Scripts.Modules;
using UnityEngine;
using Logger = Escape_Room_Engine.Engine.Scripts.Utilities.Logger;
using LogType = Escape_Room_Engine.Engine.Scripts.Utilities.LogType;
namespace Escape_Room_Engine.Engine.Scripts
{
public class Passage
{
internal DoorModule fromOut, toIn;
/// <summary>
/// The room relative (<i>RR</i>) position of this passage.
/// </summary>
internal Vector2Int rrPosition;
internal Passage(DoorModule from)
{
ConnectFrom(from);
}
protected Passage() {}
internal void ConnectFrom(DoorModule door)
{
fromOut = door;
rrPosition = fromOut.RrPosition;
Logger.Log($"Connected passage from {door} at {rrPosition} (RR).", LogType.PassageConnection);
}
internal void ConnectTo(DoorModule door)
internal virtual void ConnectTo(DoorModule door)
{
if (fromOut == null)
{
throw new Exception("Cannot connect passage to entrance if it hasn't been connected to an exit first.");
}
toIn = door;
// to make sure the origin of the player doesn't move, the two doors must be placed in the same location
toIn.PlaceRoomRelative(rrPosition);
Logger.Log($"Connected passage to {door} at {rrPosition} (RR).", LogType.PassageConnection);
}
}
}

View File

@@ -6,8 +6,8 @@ namespace Escape_Room_Engine.Engine.Scripts
public class Room
{
internal Passage entrance, exit;
internal GameObject roomObject;
private GameObject _roomObject;
private List<Space> _spaces = new();
internal Room(Passage entrance)
@@ -23,12 +23,12 @@ namespace Escape_Room_Engine.Engine.Scripts
internal void InstantiateRoom(Transform parent, string name)
{
_roomObject = new GameObject($"Room {name}");
_roomObject.transform.SetParent(parent, false);
roomObject = new GameObject($"Room {name}");
roomObject.transform.SetParent(parent, false);
for (var i = 0; i < _spaces.Count; i++)
{
_spaces[i].InstantiateSpace(_roomObject.transform, i.ToString());
_spaces[i].InstantiateSpace(roomObject.transform, i.ToString());
}
}
@@ -38,7 +38,7 @@ namespace Escape_Room_Engine.Engine.Scripts
{
space.Destroy();
}
Object.Destroy(_roomObject);
Object.Destroy(roomObject);
}
}
}

View File

@@ -1,26 +1,26 @@
using System.Collections.Generic;
using Escape_Room_Engine.Engine.Scripts.Modules;
using UnityEngine;
namespace Escape_Room_Engine.Engine.Scripts
{
public class Space
{
// public DoorModule Entrance => (DoorModule)_modules.Find(module => module.IsType((ModuleType)DoorType.Entrance));
// public DoorModule Exit => (DoorModule)_modules.Find(module => module.IsType((ModuleType)DoorType.Exit));
/// <summary>
/// The room relative (<i>RR</i>) dimensions of this space.
/// </summary>
internal readonly Dimensions rrDimensions;
private readonly Dimensions _dimensions;
private GameObject _spaceObject;
private List<Module> _modules = new(2);
internal Space(Dimensions dimensions, Passage entrance, Passage exit)
internal Space(Dimensions rrDimensions, Passage entrance)
{
_dimensions = dimensions;
this.rrDimensions = rrDimensions;
// connect the space to its passages
entrance.ConnectTo(new DoorModule(DoorType.Entrance));
// connect the space to its passage
entrance.ConnectTo(new DoorModule(DoorType.Entrance, this));
AddModule(entrance.toIn);
exit.ConnectFrom(new DoorModule(DoorType.Exit));
AddModule(exit.fromOut);
}
internal void AddModule(Module module)
@@ -32,7 +32,7 @@ namespace Escape_Room_Engine.Engine.Scripts
{
_spaceObject = new GameObject($"Space {name}", typeof(MeshFilter), typeof(MeshRenderer));
_spaceObject.transform.SetParent(parent, false);
_spaceObject.transform.localPosition = new Vector3(_dimensions.X, 0, _dimensions.Y);
_spaceObject.transform.localPosition = new Vector3(rrDimensions.x, 0, rrDimensions.z);
var meshFilter = _spaceObject.GetComponent<MeshFilter>();
meshFilter.mesh = GenerateMesh();
@@ -41,6 +41,19 @@ namespace Escape_Room_Engine.Engine.Scripts
meshRenderer.material = Engine.DefaultEngine.roomMaterial;
}
/// <summary>
/// Convert a position relative to this space to one relative to the room.
/// </summary>
/// <param name="srPosition">The space relative (<i>SR</i>) position that should be converted to a room relative (<i>RR</i>) position.</param>
/// <returns></returns>
internal Vector2Int ToRoomRelative(Vector2Int srPosition) => srPosition + rrDimensions.Position;
/// <summary>
/// Convert a position relative to the room to one relative to this space.
/// </summary>
/// <param name="rrPosition">The room relative (<i>RR</i>) position that should be converted to a space relative (<i>SR</i>) position.</param>
/// <returns></returns>
internal Vector2Int ToSpaceRelative(Vector2Int rrPosition) => rrPosition - rrDimensions.Position;
private Mesh GenerateMesh()
{
var mesh = new Mesh();
@@ -48,9 +61,9 @@ namespace Escape_Room_Engine.Engine.Scripts
mesh.vertices = new[]
{
new Vector3(0, 0, 0),
new Vector3(_dimensions.Width, 0, 0),
new Vector3(0, 0, _dimensions.Length),
new Vector3(_dimensions.Width, 0, _dimensions.Length)
new Vector3(rrDimensions.width, 0, 0),
new Vector3(0, 0, rrDimensions.length),
new Vector3(rrDimensions.width, 0, rrDimensions.length)
};
mesh.triangles = new[]

View File

@@ -0,0 +1,23 @@
using Escape_Room_Engine.Engine.Scripts.Modules;
using UnityEngine;
namespace Escape_Room_Engine.Engine.Scripts
{
/// <summary>
/// A SpawnPassage is a type of passage used solely for entering the very fist space. It always places the entrance door at (0, 0).
/// </summary>
public class SpawnPassage : Passage
{
internal SpawnPassage() {}
internal override void ConnectTo(DoorModule door)
{
// this door module does not really exist, but is required since a passage must include an entry
// it also defines, where the exit will be placed (in this case always at the play space origin)
fromOut = new DoorModule(DoorType.Exit, null);
fromOut.Place(new Vector2Int(0, 0));
base.ConnectTo(door);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 0a168ccfeb4e46b9a2c04442afef6c4c
timeCreated: 1667813431

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ec385c62be0244e78c6a13de6c7df71f
timeCreated: 1667815385

View File

@@ -0,0 +1,37 @@
using System.Collections.Generic;
using UnityEngine;
namespace Escape_Room_Engine.Engine.Scripts.Utilities
{
public enum LogType
{
Important, ModulePlacement, PassageConnection, RoomGeneration
}
public class Logger : MonoBehaviour
{
public static Logger DefaultLogger
{
get
{
if (_foundLogger == null)
{
_foundLogger = FindObjectOfType<Logger>();
}
return _foundLogger;
}
}
private static Logger _foundLogger;
public bool loggingEnabled;
public List<LogType> typeFilter;
public static void Log(string message, LogType type = LogType.Important)
{
if (DefaultLogger.loggingEnabled && DefaultLogger.typeFilter.Contains(type))
{
Debug.Log($"<b>[{type}]</b> {message}");
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: debf8d65bda642bc938a94c7639e01a4
timeCreated: 1667815421

View File

@@ -0,0 +1,23 @@
using UnityEngine;
namespace Escape_Room_Engine.Engine.Scripts.Utilities
{
public static class Utilities
{
#region Math
public static int RandomInclusive(int from, int to) => Random.Range(from, to + 1);
/// <summary>
/// Returns whether a position relative to some dimensions is inside those dimensions.
/// </summary>
/// <param name="position">The position to check, relative to the dimensions.</param>
/// <param name="dimensions">The dimensions to check the position against.</param>
public static bool IsInsideRelative(this Vector2Int position, Dimensions dimensions)
{
return position.x >= 0 && position.y >= 0 && position.x < dimensions.width && position.y < dimensions.length;
}
#endregion
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 092cc048ba0e4bb7a7862083fb702884
timeCreated: 1667812284

View File

@@ -1212,6 +1212,7 @@ GameObject:
m_Component:
- component: {fileID: 1568048335}
- component: {fileID: 1568048334}
- component: {fileID: 1568048336}
m_Layer: 0
m_Name: Engine
m_TagString: Untagged
@@ -1249,6 +1250,20 @@ Transform:
m_Father: {fileID: 0}
m_RootOrder: 5
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &1568048336
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1568048333}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: debf8d65bda642bc938a94c7639e01a4, type: 3}
m_Name:
m_EditorClassIdentifier:
loggingEnabled: 1
typeFilter: 00000000010000000200000003000000
--- !u!1 &1718957584
GameObject:
m_ObjectHideFlags: 0