Skip to content

Commit 7f14ee3

Browse files
committed
Updated DungeonGenerator algorithm to spread the work of CreateDungeon on multiple frames
1 parent bed9061 commit 7f14ee3

File tree

2 files changed

+96
-30
lines changed

2 files changed

+96
-30
lines changed

Source/ProceduralDungeon/Private/DungeonGenerator.cpp

Lines changed: 73 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
#include "Room.h"
1111
#include "ProceduralDungeonUtils.h"
1212
#include "ProceduralDungeonLog.h"
13-
#include "QueueOrStack.h"
1413
#include "DungeonGraph.h"
1514

1615
// Sets default values
@@ -29,14 +28,17 @@ bool ADungeonGenerator::CreateDungeon_Implementation()
2928
if (!HasAuthority())
3029
return false;
3130

32-
// Maybe move from plugin settings to generator's variable?
33-
int TriesLeft = Dungeon::MaxGenerationTryBeforeGivingUp();
34-
bool ValidDungeon = false;
35-
36-
// generate level until IsValidDungeon return true
37-
do
31+
switch (CurrentState)
3832
{
39-
TriesLeft--;
33+
case EState::Idle:
34+
DungeonLog_Debug("--- Idle State");
35+
// Maybe move from plugin settings to generator's variable?
36+
CurrentTriesLeft = Dungeon::MaxGenerationTryBeforeGivingUp();
37+
CurrentState = EState::Initializing;
38+
// No break to execute immediatly the Initializing state
39+
case EState::Initializing: {
40+
DungeonLog_Debug("--- Initializing State");
41+
--CurrentTriesLeft;
4042

4143
// Reset generation data
4244
StartNewDungeon();
@@ -60,41 +62,82 @@ bool ADungeonGenerator::CreateDungeon_Implementation()
6062
if (!IsValid(def))
6163
{
6264
DungeonLog_Error("ChooseFirstRoomData returned null.");
63-
continue;
6465
}
66+
else
67+
{
68+
// Create the first room
69+
URoom* root = CreateRoomInstance(def);
70+
AddRoomToDungeon(root, /*DoorsToConnect = */ {}, /*bFailIfNotConnected = */ false);
71+
72+
// Build the list of rooms
73+
PendingRooms.SetMode(listMode);
74+
PendingRooms.Push(root);
6575

66-
// Create the first room
67-
URoom* root = CreateRoomInstance(def);
68-
AddRoomToDungeon(root, /*DoorsToConnect = */ {}, /*bFailIfNotConnected = */ false);
76+
CurrentState = EState::AddingRooms;
77+
}
78+
// No break to execute immediatly the AddingRooms state
79+
}
80+
case EState::AddingRooms: {
81+
DungeonLog_Debug("--- AddingRooms State");
6982

70-
// Build the list of rooms
71-
TQueueOrStack<URoom*> roomStack(listMode);
72-
roomStack.Push(root);
73-
URoom* currentRoom = nullptr;
74-
TArray<URoom*> newRooms;
75-
while (!roomStack.IsEmpty())
83+
TArray<URoom*> NewRooms;
84+
int BatchCount = RoomBatchSize;
85+
while (!PendingRooms.IsEmpty() && BatchCount > 0)
7686
{
77-
currentRoom = roomStack.Pop();
78-
check(IsValid(currentRoom)); // currentRoom should always be valid
87+
--BatchCount;
88+
URoom* CurrentRoom = PendingRooms.Pop();
89+
check(IsValid(CurrentRoom)); // CurrentRoom should always be valid
7990

80-
if (!AddNewRooms(*currentRoom, newRooms))
91+
if (!AddNewRooms(*CurrentRoom, NewRooms))
92+
{
93+
// Stop generation here
94+
DungeonLog_Debug("--- Stopping generation as AddNewRooms returned false.");
95+
PendingRooms.Empty();
8196
break;
97+
}
8298

83-
for (URoom* room : newRooms)
99+
DungeonLog_Debug("--- %d rooms added to the dungeon.", NewRooms.Num());
100+
for (URoom* room : NewRooms)
84101
{
85-
roomStack.Push(room);
102+
PendingRooms.Push(room);
86103
}
87104
}
88105

106+
if (!PendingRooms.IsEmpty())
107+
{
108+
DungeonLog_Debug("--- Still pending rooms, yielding.");
109+
}
110+
else
111+
{
112+
DungeonLog_Debug("--- No more pending rooms, finalizing.");
113+
CurrentState = EState::Finalizing;
114+
}
115+
// Proceed to next tick
116+
YieldGeneration();
117+
break;
118+
}
119+
case EState::Finalizing:
120+
DungeonLog_Debug("--- Finalizing State");
89121
// Initialize the dungeon by eg. altering the room instances
90122
FinalizeDungeon();
91-
92-
ValidDungeon = IsValidDungeon();
93-
} while (TriesLeft > 0 && !ValidDungeon);
94-
95-
if (!ValidDungeon)
96-
{
97-
DungeonLog_Error("Generated dungeon is not valid after %d tries. Make sure your ChooseNextRoomData and IsValidDungeon functions are correct.", Dungeon::MaxGenerationTryBeforeGivingUp());
123+
CurrentState = EState::Idle;
124+
if (!IsValidDungeon())
125+
{
126+
DungeonLog_Debug("--- Dungeon is not valid, tries left: %d", CurrentTriesLeft);
127+
if (CurrentTriesLeft <= 0)
128+
{
129+
DungeonLog_Error("Generated dungeon is not valid after %d tries. Make sure your ChooseNextRoomData and IsValidDungeon functions are correct.", Dungeon::MaxGenerationTryBeforeGivingUp());
130+
return false;
131+
}
132+
else
133+
{
134+
CurrentState = EState::Initializing;
135+
YieldGeneration();
136+
}
137+
}
138+
break;
139+
default:
140+
DungeonLog_Error("CurrentState value is not supported.");
98141
return false;
99142
}
100143

Source/ProceduralDungeon/Public/DungeonGenerator.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "CoreMinimal.h"
1111
#include "DungeonGeneratorBase.h"
1212
#include "BoundsParams.h"
13+
#include "QueueOrStack.h"
1314
#include "DungeonGenerator.generated.h"
1415

1516
class IReadOnlyRoom;
@@ -125,6 +126,28 @@ class PROCEDURALDUNGEON_API ADungeonGenerator : public ADungeonGeneratorBase
125126
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Procedural Generation", AdvancedDisplay)
126127
bool bAutoDiscardRoomIfNull = false;
127128

129+
// Number of room as parent room to process per tick.
130+
UPROPERTY(BlueprintReadOnly, Category = "Procedural Generation", AdvancedDisplay)
131+
int RoomBatchSize {10};
132+
128133
// Flag to explicitely tell we don't want to place a room.
129134
bool bDiscardRoom = false;
135+
136+
private:
137+
138+
enum class EState : uint8
139+
{
140+
Idle,
141+
Initializing,
142+
AddingRooms,
143+
Finalizing,
144+
Completed
145+
};
146+
147+
EState CurrentState {EState::Idle};
148+
149+
// Holds rooms pending to have new rooms added to them.
150+
TQueueOrStack<URoom*> PendingRooms;
151+
152+
int CurrentTriesLeft {0};
130153
};

0 commit comments

Comments
 (0)