SDL3

2D Action Platformer

Stay close to the light and survive for as long as you can. Run and jump between torches, dash through enemies and collect upgrades that reduce your dash cooldown. Enemies spawn faster over time and merge into stronger forms, so you must keep moving and choose when to fight.

2D Action Platformer gameplay preview

Project Overview

This 2D survival platformer was built from scratch with C++ and SDL3. The player must fight enemies while moving through a dark level.

Torchlight creates safe areas. The player loses health while outside the light and gains health while inside it. This makes movement between torches part of both combat and survival.

Gameplay Systems

The player can run, jump, fall, and dash toward the mouse cursor. The dash works as both movement and an attack. Hitting an enemy during a dash deals damage. Touching an enemy without dashing hurts the player.

Enemies spawn outside the camera view and move toward the player. They spawn faster as the game continues. Enemies of the same strength merge when they overlap. The merged enemy becomes larger and stronger.

Power-ups reduce the dash cooldown. The game also includes player health, a kill score, sprite animation, sound effects, a start menu and a game-over screen.

Technical Approach

The game uses C++20, SDL3, SDL3_image, SDL3_ttf, GLM, ImGui, JSON, and CMake.

The level was made in Tiled and loaded from a JSON file. The map data defines tile layers, walls, the player start point, torch positions, power-up points and background images.

The game uses delta time for movement and physics. Custom systems handle gravity, tile collision, animation, audio, camera movement and rendering.

A screen-sized texture creates the darkness effect. The renderer cuts clear areas around each torch before drawing the texture over the game.

Architecture

The code separates engine systems from the main game loop. Engine modules handle the player, enemies, collisions, maps, animation, resources, audio, lighting, menus, health and power-ups.

All active objects use a shared GameObject type. Each object stores data for its role, such as player, enemy, light or power-up data.

GameState stores the active objects, player reference, score and menu state. The main loop handles input, updates the game, checks collisions, spawns objects, moves the camera and draws each frame.