Dungeon Slayer: A Rogue-like Deck-Builder
Project Overview
Dungeon Slayer is a 3D first-person rogue-like deck-builder developed in Unreal Engine 5 as part of CS3247 Game Development course in the National University of Singapore. As part of a 6-person team, I took on the role of lead developer for the modular card crafting system and utility-based AI decision system.
The Concept
Players take on the role of a sorcerer trapped in a cursed dungeon, caught in an endless cycle of death and rebirth. Each death reshapes the dungeon, ensuring no two runs are identical. Armed with arcane knowledge, players must collect magical charms to forge powerful spell cards and ultimately escape the living labyrinth.
Key Features:
- Modular Card Crafting System: Build spells from individual components rather than collecting pre-made cards
- Utility-Based AI: Intelligent enemy decision-making that adapts to combat situations
- Procedural Generation: Dynamic dungeon layouts with randomized encounters
- Stylised Visuals: Moebius-inspired sketch art style with custom shaders
- AI-Generated Content: Leveraged modern AI tools for music, sound effects, and sprites for enemy characters
Modular Card Crafting System
The card system was something that I have been thinking about extensively since the concept phase of the game. Traditional card games limit players to pre-designed cards, but I wanted to create something more flexible and creative. Eventually, I figured that as opposed to having each card with a set of static and pre-defined behaviours, why not leave the choice to the players as to what kind of cards they wish to create? Our modular system treats individual card effects as building blocks, allowing players to construct spells with complete creative freedom.
This system creates emergent gameplay where players discover powerful combinations through experimentation. Rather than memorizing optimal pre-built decks, players engage in creative problem-solving, making each play-through feel unique and personal.
Architecture Overview
The system is built around three core charm types:
Impact Charms
- Serve as the foundation of every spell
- Provide the primary effect (damage, healing, utility)
- Must be present in every valid spell recipe
Enchantment Charms
- Act as modifiers that enhance or alter Impact effects
- Can be chained together for compound modifications
- Applied recursively through the spell’s effect tree
Special Charms
- Dual Casting: Execute two spell effects simultaneously
- Power Fusion: Combine two Impact charms into new, unique effects
Technical Implementation
First, I created a data type for the cards:
Card Data Structure:
├── Mana Cost: int
├── Recipe: recipe object
├── Effects: array[card effect]
├── Root: charm node object
├── Craft Cost: map[resource, int]
└── Durability: int
Here, the “charm node” object is the key to the modular card effect system. Essentially, this root node stores a reference to a binary tree data structure where each node represents a charm. When applying a card, an iterator traverses the tree by depth-first search, during which every single root-leaf directed path is collected and built into a run-time card effect! This approach provides several key advantages:
Recursive Processing Algorithm
The system processes spells through tree traversal:
- Enchantment Charms: Recursively build effects on subtree, then apply modifier
- Dual Casting/Power Fusion: Process both subtrees and combine effects accordingly
- Impact Charms: Generate base effect and mark as leaf node
As such, this tree structure ensures that enchantments which could potentially be applied to multiple other nodes can be stored in a memory-efficient way without having to duplicate the nodes for every impact located at the leaves.
Chain of Responsibility Pattern
By our very design, a card can potentially have lots of effects, with each applicable to a different component on the target. For example, a card should first apply a mana deduction effect to the player, after which a damage effect is applied on the targeted enemy, where different types of damages should be considered against the enemy’s defence and resistance stats.
To prevent bugs and enhance code maintainability, I implemented a processor pipeline for spell execution during combat. The pipeline consists of a sequence of effect processors, where every processor only handles a very specific aspect of the card’s effect. If a card does not have the effect which a processor is expected to handle, the processor simply skips the work and pass the card to the next processor in the pipeline.
An example flow might be this:
Card |> Mana Cost Processor |> Defence Processor |> Damage Processor |> Healing Processor |> Special Effect Processor |> ...
This pattern ensures:
- Scalability: Easy to add new effect types
- Maintainability: Each processor handles one responsibility
- Modularity: Effects can be processed independently
Utility AI System
Creating believable, challenging enemies required a responsive AI decision model. To simulate realism, this AI decision model should present some randomness or fuzziness when selecting an action, much like in real-world contexts where information is often vague or incomplete. However, the randomness should also be contained in a reasonable scale for games because we still wish the players to have a sense of control over the combat.
Towards this goal, I developed a utility-based AI system that makes contextual decisions, creating opponents that feel intelligent and adaptive.
Three-Phase Decision Making
Phase 1: Observation
The AI captures a “screenshot” of the current battle state:
Combat Context:
├── Self: the enemy's own stats and status
├── Enemies: array of all enemy characters
└── Player: current player state and attributes
This context feeds into all subsequent decision-making phases as a blackboard holding the data relevant to decision-making.
Phase 2: Thinking
The enemy can perform multiple skills in a turn. To simplify AI evaluation, I further divided each skill into a series of actions. For example, a single skill might damage the target and increase the enemy’s own defence at the same time, so this skill consists of two actions.
For every possible skill, the AI first iterate over the actions in the skill. For each action, the AI calculates a utility score using custom formulae. To ensure fair comparison, every formula should normalise the raw utility into a range between 0 and 1.
Damage Utility Example:
Raw Utility = Damage Value / Target Remaining Health
Healing Utility Example:
Raw Utility = Healing Amount / Health Before Healing
Another feature of the AI system is evaluation curves, which represent the importance or urgency of an action given a specific combat state. Each action type uses a custom evaluation curve that scale the base utility according to situational factors:
- Low Health Scenarios: Healing and defensive actions receive higher priority scaling
- Near-Death Enemies: Damage actions gain increased utility when they can finish targets
- High Health or High Defence Scenarios: Healing is less important and the AI is braver in aggressive attack
For skills containing $N \geq 1$ actions, I developed a formula that balances the impact of the strongest effect with overall skill versatility:
Skill Utility = MaxActionScore + (1 - MaxActionScore) × (TotalActionScores / N)
This ensures the AI considers both powerful singular effects and well-rounded abilities.
Phase 3: Decision-Making
Before choosing a skill, the skill’s utility is modified with a “randomness coefficient” that prevents completely predictable behaviour. This coefficient is exposed to the editor so that the team can experiment for an optimal value.
Final Utility = Base Utility × Random(1 - R, 1 + R)
Other Core Game Mechanics & Design Philosophy
The Gameplay Loop
The game follows a tight, engaging loop designed to maintain tension and progression:
- Exploration Phase: Navigate procedurally generated dungeons, discovering resources and charms
- Combat Phase: Engage enemies using crafted spell cards with tactical decision-making
- Crafting Phase: Combine collected charms into powerful spells at enchanting altars
- Progression: Choose between exploring more areas or challenging the final boss
This structure ensures players always have meaningful choices while building toward the climactic boss encounter.
Portal System & Player Agency
The dual-portal system gives players control over their risk-reward balance:
- Green Portals: Lead to new areas with more resources but increased danger
- Red Portals: Direct path to the boss fight for players ready to conclude their run
This design respects player autonomy while maintaining the roguelike tradition of permanent consequences.
Advanced Visual Systems
Moebius-Style Rendering Pipeline
The team developed a custom post-processing shader that creates a distinctive comic book aesthetic:
- Toon Shading: Quantized lighting levels create solid regions of light and shadow
- Outline Detection: Filtering on depth and normal maps to generate clean edge lines
- Cross-Hatching: Procedural texture application in shadow areas adds hand-drawn character
- Dynamic Effects: Real-time particle systems and camera shake enhance combat feedback
Procedural Content Generation
Our team implemented a comprehensive procedural system to ensure replayability:
Room-First Level Generation
- Pre-designed room templates with fixed entrances and 1-3 exits
- Collision detection prevents overlapping rooms during generation
- Configurable dungeon size parameters (MinDungeonSize, MaxDungeonSize)
- Automatic wall closure for unused exits
Dynamic Encounter Placement
- Configurable
DangerLevel
parameter controls combat density - Weighted random selection for enemy party composition
- Interactive object placement at predefined spawn points
Modern AI Integration
Recognising our team’s limitations in art and audio, we embraced AI tools to obtain quality assets:
- Music: Generated ambient tracks and main theme using Suno AI and Udio
- Sound Effects: Created using ElevenLabs for consistent audio design
- Visual Assets: Enemy sprites and UI icons generated with Stable Diffusion
- Workflow: Deployed ComfyUI for streamlined asset generation pipeline
This approach allowed our small team to achieve visual and audio quality typically requiring specialized artists.
Robust Character Systems
Built on Unreal Engine’s Gameplay Abilities System:
- Unified Attribute Management: Health, mana, defence, and damage resistances
- Effect-Driven Changes: All attribute modifications go through Gameplay Effects
- Automatic Clamping: Built-in value validation and range enforcement
- Custom Damage Formulas: Flexible calculation system supporting complex interactions
Technical Challenges & Solutions
Challenge: Scalable Effect System
Problem: Need to support dozens of unique spell effects without creating unmaintainable code
Solution: Implemented Chain of Responsibility pattern with data-driven effect definitions
Challenge: Believable Enemy Behavior
Problem: Simple state machines created predictable, exploitable AI opponents
Solution: Developed utility-based system with fuzzy logic for human-like decision variance
Challenge: Complex Card Interactions
Problem: Nested effect modifications and parallel spell execution required flexible architecture
Solution: Binary tree structure with recursive processing enables unlimited effect combinations
Challenge: Procedural Quality Control
Problem: Random generation often created unplayable or boring layouts
Solution: Template-based rooms with intelligent connection algorithms and configurable parameters
Key Learnings & Takeaways
This project reinforced several important principles in game development:
- Systems Thinking: Complex games emerge from simple, well-designed systems working together
- Player Agency: The most engaging mechanics give players meaningful choices and creative freedom
- Technical Architecture: Investing time in solid foundations pays dividends when adding features
- AI as a Tool: Modern AI can augment small teams but requires thoughtful integration
- Iteration Importance: The utility AI and card systems improved dramatically through multiple iterations
The combination of technical depth and creative design made Dungeon Slayer both challenging to develop and rewarding to play. Leading the development of core systems while collaborating on the broader vision provided invaluable experience in both technical leadership and team coordination.
Dungeon Slayer was developed using Unreal Engine 5 as part of CS3247 Game Development. Team members: Gerard Jeremiah Matthew, Lai Yiwen, Li Chengzhen, Lin Yuxiang, Ma Yirui, Zhang Puyu.
Technologies Used: Unreal Engine 5, C++, Blueprint Visual Scripting, Stable Diffusion, Suno AI, Udio, ElevenLabs
My Primary Contributions: Modular Card Crafting System, Utility-Based AI System, Game Concept and Mechanic Design, System Integration