Skip to main content

Build Your Own 2D Game Engine

"Every shipped game is a study in compromises. Building an engine is where you discover which compromises matter."

A 2D game engine is one of the most fun and visible portfolio projects. By the end you have an engine that can run a small game — Tetris, Snake, a platformer, a roguelike — and you understand the game loop, scene graphs, sprite rendering, input handling, audio, and entity-component systems.


1. Overview & motivation

A game engine has a small set of core systems:

  1. Game loop — fixed-timestep update + variable-rate render.
  2. Window / input / audio — usually via SDL2, raylib, or a similar library.
  3. Renderer — sprite drawing, layers, camera transforms.
  4. Scene graph or ECS — how game objects are organized and updated.
  5. Physics — collision detection at minimum; a physics engine at more.
  6. Asset pipeline — loading sprites, sounds, levels.

What you can only learn by building one:

  • Why fixed-timestep updates with variable rendering is the universal pattern (Glenn Fiedler's "Fix Your Timestep!").
  • Why entity-component-systems dominate modern engine design (cache locality, composition over inheritance).
  • Why scene graphs are simple in concept but quickly accumulate edge cases.
  • Why game development is half engineering, half art direction — your engine choices constrain what games you can make on it.

2. Where this fits in the degree

  • Phase: Production
  • Semester: Capstone elective
  • Modules deepened: Sem 8 Module 4 (scale, reliability, performance) — frame budgets are a strict performance discipline.

Cross-phase relevance:


3. Prerequisites

  • A language: C++, Rust, C#, JavaScript, or Lua. C++ and Rust are the production defaults; the others are excellent for tutorials.
  • Basic linear algebra: vectors, matrices, 2D transforms.
  • A graphics library: SDL2 (recommended), raylib, or MonoGame (C#).

4. Theory & research

Required reading

  • Robert Nystrom, Game Programming Patterns (gameprogrammingpatterns.com) — free online. Patterns specific to game development: game loop, update method, component, observer, state, double buffer, object pool, dirty flag, spatial partition, command. ⭐ start here.
  • Glenn Fiedler, "Fix Your Timestep!" (gafferongames.com/post/fix_your_timestep/) — the canonical game-loop article.
  • Jason Gregory, Game Engine Architecture (3rd ed.)the game-engine textbook. Industrial-strength.
  • Bruno Yamaguchi, "Game Programming in C++" — alternative book.
  • handmadehero.org — Casey Muratori's 1,000+ episode "from scratch" game programming series. Spectacular for systems-level engagement.

For ECS specifically

  • Unity DOTS documentation — modern ECS in production.
  • Bevy ECS (bevyengine.org) — Rust, clean modern ECS.

5. Curated tutorial list (from BYO-X)

The BYO-X "Game" section is huge. Selected highlights:

  • C: Handmade HeroCasey Muratori, handmadehero.orgthe deepest path
  • C++: Breakout, Beginning Game Programming v2.0LazyFoo's SDL tutorials, lazyfoo.net ⭐ classic SDL series
  • C++: Space Invaders from Scratch
  • JavaScript: 2D breakout game using PhaserMDN tutorial
  • JavaScript: How to Make Flappy Bird in HTML5 With Phaser
  • JavaScript: Developing Games with React, Redux, and SVG
  • JavaScript: Build your own 8-Ball Pool game from scratch [video]
  • JavaScript: How to Make Your First Roguelike
  • JavaScript: Think like a programmer: How to build Snake using only JavaScript, HTML & CSS
  • Lua: BYTEPATHa16bitnes's blog
  • Python: Developing Games With PyGame
  • Python: Making Games with Python & Pygame [pdf] — Al Sweigart's free book
  • Python: Roguelike Tutorial RevisedRoguelike Dev Reddit's libtcod tutorialrecommended primary for roguelikes
  • Ruby: Developing Games With Ruby
  • Ruby: Ruby Snake
  • Rust: Adventures in Rust: A Basic 2D Game
  • Rust: Roguelike Tutorial in Rust + tcodbfnightly.bracketproductions.com
  • Java: Code a 2D Game Engine using Java - Full Course for Beginners [video] — long, well-paced
  • Java: 3D Game Development with LWJGL 3 — bonus for 3D
  • C#: Learn C# by Building a Simple RPG, Creating a Roguelike Game in C#, Build a C#/WPF RPG
  • C: Chess Engine In C [video], Let's Make: Dangerous Dave [video]
  • Go: Games With Go [video]

Pick a target game first; the engine emerges from the game. Three recommended starting projects, by ambition:

Path A: Pong / Breakout (1 weekend)

LazyFoo's SDL tutorials in C++. By tutorial 12 you have a working Breakout. Excellent for first contact.

Path B: Snake or Tetris (1 week)

Implement classic mechanics. Fixed playfield, simple physics, simple input.

Path C: Roguelike (3–6 weeks)

The recommended primary path for this degree. Roguelike Dev's "Complete Roguelike Tutorial Revised" (Python + libtcod) or bracket-lib's "Roguelike Tutorial in Rust". Both walk through:

  • Map generation (BSP, cellular automata, random rooms).
  • Field of view (FoV).
  • Entity-component system.
  • Combat, items, magic.
  • Saving / loading.

Roguelikes are a sweet spot: deeply systemic without requiring sprite art (ASCII or simple tiles work).

Path D: Handmade Hero (years)

Casey Muratori's full series. Build everything — even the file I/O — from scratch. Not for the impatient. Unparalleled depth.

For this degree: Path C (roguelike) is the default for portfolio. Path A or B is fine if shorter scope is needed.


7. Implementation milestones

(Generic 2D engine, distilled from Roguelike tutorials and Nystrom's Game Programming Patterns.)

Milestone 1: Window + game loop

Open a window. Implement a fixed-timestep loop with variable rendering (Fiedler's "the gist").

double t = 0.0;
const double dt = 1.0 / 60.0; // 60 Hz logic

double currentTime = getTime();
double accumulator = 0.0;

while (!quit) {
double newTime = getTime();
double frameTime = newTime - currentTime;
currentTime = newTime;

accumulator += frameTime;
while (accumulator >= dt) {
update(dt);
accumulator -= dt;
t += dt;
}

render(accumulator / dt); // interpolation factor
}

Evidence: Window stays open. FPS displays. Frame budget visible.

Milestone 2: Render a moving sprite

Load a PNG, render at varying positions.

Evidence: Sprite moves across the screen at constant speed regardless of frame rate.

Milestone 3: Input handling

Map keyboard / mouse / gamepad events to game actions.

Evidence: Player sprite moves in response to WASD.

Milestone 4: Entity-component-system (ECS)

Replace ad-hoc object hierarchies with components.

struct Position { x: f32, y: f32 }
struct Velocity { dx: f32, dy: f32 }
struct Sprite { texture_id: u32 }

// systems:
fn physics_system(entities: &mut [Entity], dt: f32) {
for e in entities {
if let (Some(p), Some(v)) = (e.position_mut(), e.velocity()) {
p.x += v.dx * dt;
p.y += v.dy * dt;
}
}
}

Evidence: Adding a new component (e.g., Health) requires no changes to existing system code.

Milestone 5: Collision detection (AABB)

Axis-aligned bounding boxes. Check overlap for each pair.

fn aabb_collide(a: &AABB, b: &AABB) -> bool {
a.x < b.x + b.w && a.x + a.w > b.x &&
a.y < b.y + b.h && a.y + a.h > b.y
}

For many objects: spatial partitioning (grid or quadtree). Read Nystrom's "Spatial Partition" chapter.

Evidence: Player can pick up items by colliding with them.

Milestone 6: Camera and viewport

The world is larger than the screen. The camera follows the player.

let camera = Camera { x: player.x - SCREEN_WIDTH / 2.0, y: player.y - SCREEN_HEIGHT / 2.0 };
// when rendering:
draw_at(world_x - camera.x, world_y - camera.y, sprite);

Evidence: Player can walk across a world larger than the screen.

Milestone 7: Audio

Load sounds; play them on game events.

Evidence: Footsteps, item pickup, damage all play correct sounds.

Milestone 8: Scene management

Title screen, gameplay screen, pause menu. A scene stack: push, pop, top scene gets update/render.

Evidence: ESC pauses; tap again to resume. Game-over screen shows score.

Milestone 9: Save / load

Serialize the entire ECS to disk. Reload restores state exactly.

Milestone 10 (game-specific): Procedural map generation

For roguelikes: BSP, cellular automata, drunkard's walk.

For platformers: hand-designed levels loaded from TMX (Tiled) files.


8. Tests & evidence

TestHow
Frame rateStable 60 FPS on the target hardware
DeterminismSame input sequence → same game state (essential for tests, replays)
CollisionHand-traced collision scenarios
ECS performance10,000 entities at 60 FPS
Save/loadSave mid-game, load, identical state
A real gameTetris, Snake, Pong, or a roguelike level playable

The strongest evidence: a video or GIF of someone playing your game.


9. Common pitfalls

  • Variable timestep physics. Causes non-determinism. Use a fixed timestep.
  • No FPS cap. Burns CPU/GPU for no benefit. Cap at 60.
  • Allocation in the inner loop. Pre-allocate; reuse. Object pools are the canonical pattern (Nystrom chapter 19).
  • Tight coupling between game logic and rendering. Logic should be testable without a window.
  • Naive O(n²) collision. Slow with 100+ entities. Use spatial partition.
  • Mixing screen and world coordinates. Be explicit: world_pos, screen_pos. Convert via camera.
  • No ECS. Object hierarchies become unwieldy quickly. Even a simple ECS is worth the upfront cost.
  • Input read from keyboard polling AND from event queue. Pick one model.

10. Extensions

  • Particle systems. Sparks, smoke, explosions.
  • Tilemap rendering. Standard 2D map representation.
  • Animation system. Sprite sheets, state machine for animations.
  • Lighting. 2D dynamic lighting (e.g., flashlight effect).
  • Multiplayer (local). Two players on one keyboard.
  • Multiplayer (networked). Hard. See "client prediction" and "lag compensation."
  • Modding API. Expose Lua or Python; let users write scripts.
  • Asset packaging. Bundle all sprites, sounds, levels into one file.

A game engine is one of those projects with no obvious ceiling.


11. Module integration

ModuleWhat the game engine deepens
Sem 4 Module 1 — C / Rust fundamentalsSubstantial project with strict performance requirements.
Sem 4 Module 3 — Computer organizationCache-friendly data layout (ECS). Frame budgets.
Sem 8 Module 4 — Scale & performanceFrame budget is the performance budget.
Memory Allocator tutorialArena allocators are common in engines.
Physics Engine tutorialNatural extension.
3D Renderer tutorial2D → 3D is a real conceptual leap; revisit linear algebra.

12. Portfolio framing

What to publish:

  • Engine source (engine/{core, render, input, audio, ecs, physics}).
  • One complete playable game built on top.
  • A README with:
    • Video of gameplay.
    • Architecture diagram.
    • Performance numbers.
  • Free downloads of compiled binaries for at least one platform (Windows or macOS or Linux).

Reviewer entry points:

  • engine/core/loop.cpp — the game loop.
  • engine/ecs/world.cpp — the ECS.
  • game/main.cpp — the actual game.
  • README with playable build link or web demo.

A working 2D engine + game is one of the most engaging portfolio pieces a programmer can publish. Everyone can immediately see what it does; everyone respects the work.


Source

This tutorial draws from the BYO-X catalog "Game" section. Nystrom's Game Programming Patterns and Gregory's Game Engine Architecture are the canonical references. Roguelike tutorials are the recommended starting project shape.