A Simple Game Engine, built to stay out of your way.
ASGE is a modular, lightweight, cross-platform 2D game engine written in modern C++ (C++23) on top of SDL3. Small composable subsystems, an ECS-based gameplay layer, and a growing set of runnable examples — targeting Windows, Linux and macOS.
Built around four ideas
Performance
Thin abstractions over SDL3, custom allocators, and a thread pool for concurrent work — the engine tries to stay out of your frame budget's way.
Simplicity
Small, composable subsystems instead of one monolithic framework. Pull in what a game needs through the ASGE.hpp umbrella header.
Extensibility
An ECS-based gameplay layer plus a signal/event system (asge::signals::Signal) for decoupling systems from each other.
Tooling
A debug logger, timing/profiling utilities (ScopedTimer), a virtual filesystem, and a growing set of runnable examples.
Repository layout
| Path | Contents |
|---|---|
src/ASGE/ | Engine source — Application, Core, Events, Game, Input, Video, Audio |
examples/ | Small standalone programs demonstrating each subsystem |
editor/ | The Dear ImGui level editor (asge-editor) |
tests/ | GoogleTest unit test suite |
docs/roadmap/ | Design docs and the phased implementation roadmap |
docs/level_editor_roadmap/ | Design docs and phases for the level editor |
docsite/ | This documentation site: generators, content and the Doxygen config |
scripts/ | Dependency-fetching scripts (SDL3, stb, Dear ImGui) for each platform |
third-party/ | Vendored dependencies, fetched by the scripts above |
cmake/ | CMake package config used when ASGE is installed |
Roadmap status
ASGE is under active development, worked through a phased roadmap. Tiers are priority order; each system also has its own phase docs for scope/maturity.
Tier 0 — Foundation
Tier 1 — MVP-critical
Tier 2 — Makes it feel like a game
Tier 3 — Revisit once Tier 1-2 exist
Tier 4 — Explicitly deferred
Jump into an example
Every example ships with real screenshots and its full embedded source.
Minimal game
A Game<TStateId> subclass drives a stack of
IGameStates — TStateId is whatever identifies a screen. See
Moving Box for the complete, playable version of this.
#include <ASGE/ASGE.hpp>
class MyGameState : public asge::game::state::IGameState<int>
{
public:
std::optional<asge::game::state::Transition<int>>
Update(float inDeltaTime, asge::input::InputState const& inInput) override
{
// Game logic goes here
return std::nullopt; // engaged instead: push/pop/replace to another state
}
void Render(asge::video::IRenderer& inRenderer) override
{
// Draw calls go here
}
void OnSystemEvent(asge::event::SystemEvent const& inSysEvent) override
{
// Handle window/system events
}
};
class MyGame : public asge::game::Game<int>
{
public:
explicit MyGame(asge::video::IRenderer& inRenderer) : Game(inRenderer)
{
SetInitialState(0);
}
protected:
std::unique_ptr<StateType> CreateState(int inId) override
{
return std::make_unique<MyGameState>();
}
};
int main()
{
asge::Application<MyGame> app(asge::ApplicationConfig{});
app.Run();
return 0;
}