Soulless Knight is a top-down roguelite dungeon crawler for iOS. Built from scratch over 6-weeks, we integrated a custom Entity-Component-System (ECS) engine in pure Swift and rendered natively using Apple's SpriteKit. We developed the game as part of a 4-person team for NUS CS3217.
Our team put extensive effort into architectural abstractions, applying software engineering best practices and comprehensive unit/integration testing.
- PlatformiOS 16.0+ (iPhone & iPad)
- StackSwift, SpriteKit, Custom ECS Engine
- GitHubcs3217-2526/group-project-dungeonCrawler
Key Gameplay Features
Custom ECS Engine
A pure Swift implementation of the Entity-Component-System pattern, ensuring highly decoupled logic, cache-friendly data structures, and hardware-agnostic runtime systems.
Fluid Twin-Stick Combat
Responsive, native iOS controls supporting virtual analog sticks decoupled via joystick adapters and a command-driven action queue.
Deep Customization & Weapons
A versatile two-weapon loadout system supporting distinct playstyles: Firearms (requiring ammo/reloads), Magic (drawing from regenerating Mana), and resource-free Melee combat.
Technical Architecture & Insights
Our core software engineering goal was to build a robust codebase that prioritizes extensibility, and partitionability. Game logic was isolated from standard iOS rendering layers so that team members could develop core physics/systems and UI/adapters in parallel.
- Topological System Ordering (DAG): The engine computes system execution order dynamically. Systems explicitly declare dependencies, which are resolved using Kahn's topological sorting algorithm on every frame.
- In-Place Component Semantics: Components are reference types (classes). Systems query component references from the world and mutate them directly in-place. This facilitates instant runtime state propagation without expensive manual write-back loops.
- Rigorous Design Patterns Applied:
- Adapter Pattern: Completely decouples ECS logic from SpriteKit. Rendering and HUD backends are governed by protocols; SpriteKit implementations are injected dynamically at app launch.
- Factory Pattern: Centralizes entity composition (e.g.,
EnemyEntityFactory), preventing system logic from directly assembling raw components. - Command Pattern: Touch screen and joystick interactions produce discrete
Commandobjects. These are queued and executed linearly during the system tick. - Strategy Pattern: Used to abstract procedural layout generation algorithms and flexible enemy behavior AI (e.g.,
StandardStrategyvsTimidStrategy). - Chain of Responsibility: Manages weapons' effects (e.g.,
ConsumeAmmo→SpawnProjectile) in a pipeline where any stage can intercept or block downstream actions. - Facade Pattern: Simplified generation, game states, and lockdown routines behind a single, clean interface:
LevelOrchestrator.