Eurekiel: Galaxy Tetris Destroyer is a 2D hybrid game built from scratch in C++ on a custom engine. The player controls a spaceship that shoots enemies while managing falling Tetris pieces on a grid. The spaceship orients toward the mouse cursor, enemies spawn in waves, and Tetris blocks can be destroyed by bullets or cleared by completing lines. I built every system from the ground up, including the entity framework, event bus, particle effects, UI layer, and a data-driven content pipeline.
Main Menu and UI
The game features a full main menu system with animated transitions. The UI layer is built on a custom WidgetManager that handles widget lifecycle, input routing, and render ordering across both screen-space and world-space cameras.
Spaceship Mouse Control
The player ship constantly orients toward the mouse cursor. I implemented this by converting the screen-space mouse position into world-space coordinates using the engine’s dual-camera system (a 1600x800 screen camera and a 200x100 world camera). The ship’s orientation angle is calculated via atan2 from the ship position to the converted mouse world position, giving smooth and responsive aiming.
Tetris Block System
Breakable Blocks and Collision
Each Tetris piece (Tetromino) is composed of individual Cube entities placed on a grid. Cubes have health and can be damaged by player bullets. When a cube’s health reaches zero, it is destroyed and removed from the grid. The grid tracks occupancy per cell, and destroying part of a Tetromino leaves the remaining cubes in place.
Destroying Entire Tetrominos
When all cubes belonging to a single Tetromino are destroyed, a TetrominoAllChildDieEvent fires through the event system. This awards bonus points to the player, rewarding aggressive play.
Line Clear Algorithm
The grid implements a line-clear algorithm similar to classic Tetris. When an entire row of cells is occupied, the row is cleared and all blocks above shift down. The scoring scales with the number of lines cleared simultaneously, rewarding the player for setting up multi-line clears. The PointGainEvent broadcasts the score change to the UI and wave system.
Vertical Tetris Movement
Players can use the mouse wheel to move the active Tetris piece vertically, adding a strategic layer to block placement. This input is processed through the event system via TetrominoRequestOperateEvent, which validates the move against grid boundaries before applying it.
Wave System
The LevelHandler manages wave progression through a score threshold system. Each wave defines which enemy types spawn, their spawn rates, and the score required to advance. When the player’s accumulated score reaches the threshold, the next wave unlocks with new enemy types and increased difficulty. The wave configuration is driven by FLevel data structures that designers can tune without modifying code.
Enemy Types
Beetle
The Beetle is a slow-moving enemy that drifts toward the player at a constant speed. It serves as the baseline threat in early waves, teaching players to aim and manage space.
Wasp
The Wasp is a faster, more aggressive enemy that accelerates toward the player. It applies increasing velocity over time, creating pressure and forcing the player to prioritize targets.
2D Particle System
I built a custom 2D particle system with physics-based simulation. The ParticleHandler manages particle pools, and each particle has configurable properties including lifetime, velocity, acceleration, color interpolation, and scale curves defined in FParticleProperty. Particles are used for bullet impacts, explosions, and visual feedback throughout the game.
Sprite Sheet Resources
All game visuals are rendered from sprite sheets, with each sprite defined through the data-driven SpriteSheetDefinition system. This allows adding or modifying visual content by editing XML files rather than recompiling code.
System Architecture
The game is built on an event-driven architecture with clear separation between systems. The EventManager acts as a central message bus, decoupling the scoring, grid, wave, and entity systems from each other.
graph TD
subgraph Core["Core Engine"]
APP[App] --> GAME[Game Loop]
GAME --> EM[EventManager]
GAME --> RM[ResourceManager]
GAME --> WM[WidgetManager]
end
subgraph Entities["Entity System"]
PS[PlayerShip]
BUL[Bullet]
BEE[Beetle]
WSP[Wasp]
AST[Asteroid]
TET[Tetromino]
CUB[Cube]
end
subgraph Grid["Grid System"]
GR[Grid] --> LC[Line Clear]
GR --> OCC[Cell Occupancy]
end
subgraph Data["Data-Driven Definitions"]
XML[XML Files] --> SPR[SpriteSheetDefinition]
XML --> SND[SoundDefinition]
XML --> TXR[TextureDefinition]
XML --> TDF[TetrominoDefinition]
end
subgraph Systems["Game Systems"]
LH[LevelHandler / Waves]
PH[ParticleHandler]
SC[Score System]
end
GAME --> Entities
GAME --> Grid
RM --> Data
PS -->|fires| BUL
BUL -->|damages| CUB
CUB -->|occupies| GR
TET -->|spawns| CUB
LC -->|PointGainEvent| EM
EM -->|score update| SC
SC -->|threshold check| LH
LH -->|spawn enemies| Entities
CUB -->|death| PH
Data-Driven Content Pipeline
Game content is defined in XML files under Run/Data/Definitions/. Each definition type has a corresponding C++ loader class that parses the XML at startup:
SpriteSheetDefinitionmaps sprite names to atlas regions with frame dimensions and animation dataTetrominoDefinitiondefines the shape, color, and block layout of each Tetris piece type (I, J, L, O, S, T, Z)SoundDefinitionmaps sound names to audio files with volume and playback settingsTextureDefinitionregisters texture assets with their file paths and sampling parameters
This separation means designers can add new Tetromino shapes, adjust sprite animations, or swap sound effects by editing XML without touching C++.
Design Philosophy
Event-Driven Decoupling — Systems communicate through a central EventManager rather than direct references. When a cube dies, it fires a CubeTouchBaseLineEvent. When a Tetromino loses all children, it fires TetrominoAllChildDieEvent. The score system, wave system, and UI all subscribe independently. This makes adding new reactive behaviors trivial without modifying existing code.
Data-Driven Content — All visual assets, audio, and Tetromino shapes are defined in XML. The C++ code provides the runtime systems, while XML definitions provide the content. This clean boundary means gameplay tuning and content creation happen without recompilation.
Dual-Camera Coordinate System — The engine runs two cameras simultaneously: a world camera (200x100 units) for gameplay entities and a screen camera (1600x800 pixels) for UI. Mouse input arrives in screen space and is converted to world space for gameplay interactions, keeping the coordinate systems clean and resolution-independent.
Component-Style Entity Design — All game objects inherit from a base Entity class that provides position, velocity, collision, health, and rendering. Specialized behaviors (mouse aiming for PlayerShip, acceleration for Wasp, grid occupancy for Cube) are added in subclasses, keeping the base lean and each entity focused on its unique behavior.
Physics-Based Particles — Rather than using pre-baked animations, the particle system simulates each particle with velocity, acceleration, and lifetime. This produces organic, varied visual effects from a small set of configurable parameters defined in FParticleProperty.