Why Roguelikes Keep Growing
Roguelikes are one of the most commercially successful indie genres. Hades, Slay the Spire, Balatro, Vampire Survivors — the genre produces breakout hits consistently. The reason: roguelikes have infinite replayability built into their structure, and they're scope-manageable for small teams.
A roguelike's content comes from combinations, not quantity. 20 weapons × 30 upgrades × 15 room layouts = thousands of unique runs. One level designer's work creates months of playtime.
Core Architecture
The Run Loop
Every roguelike follows this structure:
[Meta Hub] → [Start Run] → [Room/Encounter] → [Reward Choice] →
[Room/Encounter] → [Reward Choice] → ... → [Boss] →
[Next Floor] → ... → [Death or Victory] → [Meta Progression] → [Meta Hub]
System Component Architecture
// Key systems for a roguelike
UCLASS()
class URunManager : public UGameInstanceSubsystem
{
GENERATED_BODY()
public:
void StartNewRun(int32 Seed = 0);
void CompleteRoom(ERoomResult Result);
void EndRun(bool bVictory);
UPROPERTY()
FRunState CurrentRun; // Current run data
UPROPERTY()
FMetaProgressionData MetaData; // Persistent across runs
};
USTRUCT()
struct FRunState
{
GENERATED_BODY()
int32 Seed;
int32 CurrentFloor;
int32 CurrentRoom;
TArray<FName> CollectedItems;
TArray<FName> ActiveUpgrades;
int32 Gold;
float RunTime;
int32 EnemiesKilled;
};
Procedural Level Generation
Room-Based Generation
The most common roguelike approach: pre-designed rooms connected procedurally.
Step 1: Define room templates
Create 50-100+ room layouts in the editor:
- 10+ combat rooms (varying difficulty and enemy composition)
- 5+ reward rooms (item shops, upgrade altars)
- 5+ event rooms (story encounters, NPCs, challenges)
- 5+ rest rooms (healing, item management)
- 3+ boss rooms
- 5+ corridor/transition rooms
Step 2: Tag rooms with metadata
USTRUCT()
struct FRoomTemplate
{
GENERATED_BODY()
UPROPERTY(EditAnywhere)
TSoftObjectPtr<UWorld> RoomLevel;
UPROPERTY(EditAnywhere)
ERoomType Type; // Combat, Reward, Event, Rest, Boss
UPROPERTY(EditAnywhere)
int32 DifficultyTier; // 1-5
UPROPERTY(EditAnywhere)
int32 MinFloor; // Don't appear before this floor
UPROPERTY(EditAnywhere)
TArray<FName> RequiredDoors; // North, South, East, West
};
Step 3: Generate the floor layout
void UFloorGenerator::GenerateFloor(int32 FloorIndex, FRandomStream& RNG)
{
// Create a grid-based map
TArray<FIntPoint> RoomPositions;
FIntPoint CurrentPos(0, 0);
RoomPositions.Add(CurrentPos);
// Random walk to create connected room positions
for (int32 i = 0; i < RoomsPerFloor; i++)
{
// Pick random adjacent cell that isn't occupied
TArray<FIntPoint> ValidNeighbors = GetUnoccupiedNeighbors(CurrentPos, RoomPositions);
if (ValidNeighbors.Num() > 0)
{
CurrentPos = ValidNeighbors[RNG.RandHelper(ValidNeighbors.Num())];
RoomPositions.Add(CurrentPos);
}
}
// Assign room types based on position
// First room: starting room
// Last room: boss
// Others: weighted random based on floor difficulty
AssignRoomTypes(RoomPositions, FloorIndex, RNG);
}
Map Shapes
Different generation algorithms create different map feels:
Linear path: Room → Room → Room → Boss. Simple, fast-paced.
Branching tree: Fork at junctions, player chooses path. Strategic.
Grid/dungeon: Connected rooms on a grid. Exploratory.
Graph-based: Rooms connected by edges with complex topology. Slay the Spire style.
Seed System
Seeds make runs reproducible:
void URunManager::StartNewRun(int32 Seed)
{
if (Seed == 0)
Seed = FMath::Rand(); // Random seed if none provided
CurrentRun.Seed = Seed;
FRandomStream RNG(Seed);
// All procedural decisions use this stream
FloorGenerator->GenerateFloor(1, RNG);
ItemPool->ShufflePool(RNG);
EnemySpawner->ConfigureSpawns(RNG);
}
Share seeds between players for competitive runs ("Try seed 42069 — it's brutal") or use them for daily challenges (everyone plays the same seed).
Item and Upgrade Design
The Power Fantasy Curve
Roguelike items should make the player feel increasingly powerful throughout a run:
Power
▲
│ ╱
│ ╱
│ ╱
│ ╱
│ ╱
│ ╱
│ ╱
│ ╱
│╱
└──────────────────────────▶ Run Progress
Start Early Mid Late Boss
By the end of a successful run, the player should feel dramatically more powerful than when they started.
Item Design Principles
Synergies create depth: Items should interact with each other in interesting ways.
Item: "Burning Blade" — Attacks inflict Burn
Item: "Explosion on Burn" — Burning enemies explode when killed
Item: "Chain Reaction" — Explosions spread Burn to nearby enemies
Combined: Every kill starts a chain reaction of burning explosions.
Categories for balance: Organize items into categories:
- Offensive: Damage, attack speed, crit chance
- Defensive: Health, armor, dodge, lifesteal
- Utility: Movement speed, pickup radius, XP gain
- Synergy: Items that modify how other items work
Rarity and weighting: Control power creep with rarity tiers:
- Common (60% of pool): Small, reliable buffs
- Uncommon (25%): Stronger, more specific bonuses
- Rare (12%): Build-defining items with strong effects
- Legendary (3%): Game-changing items that reshape playstyle
Item Pool Management
UCLASS()
class UItemPool : public UObject
{
public:
FName DrawItem(ERarity MaxRarity, FRandomStream& RNG);
void RemoveItemFromPool(FName ItemId); // Items already found don't reappear
void AddItemToPool(FName ItemId); // Unlocked items enter the pool
private:
TArray<FWeightedItem> AvailableItems;
TArray<FName> DrawnThisRun; // Prevent duplicates
};
Meta-Progression
What Persists Between Runs
Meta-progression keeps players coming back after death:
- Permanent unlocks: New characters, weapons, items added to the pool
- Upgrades: Small permanent stat bonuses (health +5%, damage +3%)
- Knowledge: Bestiary entries, lore fragments, map reveals
- Cosmetics: Character skins, visual effects, victory screens
- Currency: Meta-currency earned per run, spent at the hub
Designing Meta-Progression
Horizontal, not vertical: Unlock variety (new items, new characters), not power. A player on their 100th run should have more options than a new player, but not be 10x stronger. The skill improvement IS the progression.
Breadth early, depth later: First unlocks should expand the item/character pool dramatically (lots of new options quickly). Later unlocks are more niche or cosmetic.
Run-earned currency: Currency earned scales with run performance, not just victories. Dying on floor 3 should still earn something. This prevents frustration loops where weak players can't earn enough to improve.
Hub Design
The meta hub is where players spend between runs:
- Persistent NPC: Vendor/mentor who accepts meta-currency for upgrades
- Trophy room: Visual representation of achievements and progress
- Character select: Choose starting character/loadout
- Challenge board: Daily challenges, special modifiers
- Lore archive: Collected lore and story fragments
Balancing a Roguelike
The Balance Challenge
Roguelike balance is uniquely difficult because you're balancing combinations, not individual items. Item A might be balanced alone, and Item B might be balanced alone, but together they break the game.
Approaches
Playtest extensively: The only reliable way to find broken combinations is to play thousands of runs. Use automated playtesting where possible.
Cap stacking: Limit how many times the same effect can stack (max 5 burn stacks, max 200% attack speed).
Diminishing returns: Each instance of the same stat gives less benefit (first +10% speed, second +8%, third +5%).
Anti-synergy items: Some items should deliberately not combine well, forcing players to make choices rather than collecting everything.
Run difficulty scaling: As the player gets stronger, increase enemy scaling. This prevents any build from trivializing the game.
The Death Loop: Why Players Keep Playing
Making Death Meaningful, Not Frustrating
Death in a roguelike should feel like:
- "I almost had that run" (motivates another attempt)
- "Next time I'll prioritize defensive items" (learning)
- "I unlocked a new weapon, let me try that" (anticipation)
Death should NOT feel like:
- "That was unfair" (balance problem)
- "I just lost an hour of progress" (run length problem)
- "Every run feels the same" (variety problem)
Run Length Sweet Spot
| Run Duration | Player Experience | Best For |
|---|---|---|
| 5-15 min | Quick, arcade feel. Easy to start "one more run" | Mobile, casual roguelites |
| 20-40 min | Standard. Long enough to build power, short enough to not sting on death | Most roguelikes |
| 45-90 min | Epic runs. Each death feels significant. | Hardcore roguelikes, story-focused |
| 2+ hours | Very long. Needs save-and-quit between sessions. | Traditional roguelikes |
The 20-40 minute sweet spot is where most commercially successful roguelikes land. It's the "one more run" zone.
Information on Death
When the player dies, show them:
- How far they got (floor/room number)
- What killed them (enemy, trap, boss)
- Run statistics (time, kills, items collected)
- Meta-currency earned
- New unlocks from this run
- "Try again" button prominently displayed
Make death a moment of reflection and anticipation, not frustration.
Roguelikes are one of the best-suited genres for indie development — deep gameplay from systems, not content volume. Get the core loop right (satisfying combat + meaningful choices + fair but challenging difficulty + meta-progression that adds variety), and you have a game players will sink hundreds of hours into.