A small Breakout clone in Nim built as a clear data-oriented game code example: typed game state, direct arrays of objects, explicit systems, hierarchical transforms, and a fixed-timestep loop on top of raylib.
This project is mainly for:
- Nim programmers who want a complete but readable game sample.
- People studying data-oriented design without wanting a full engine or ECS framework.
- Developers who like game code that stays explicit: update order, storage, and side effects are all easy to trace.
If you want a polished engine, editor tooling, scripting, or a reusable gameplay framework, this is not that project. This is a compact codebase you can read in an afternoon and learn from.
- The game state is concrete and direct.
Gameownsballs,bricks,particles,trails, andnodesinstead of routing everything through a generic entity abstraction. - Systems stay small and obvious. Movement, collision, fade, shake, transform, and draw each live in their own file.
- The transform graph is still hierarchical, so the camera and spawned effects can move as a group without a heavier scene abstraction.
- The main loop uses a fixed simulation step with interpolation, which keeps gameplay deterministic per tick while rendering stays smooth.
- The whole project is small enough to fork, rewrite, and experiment with.
Scene setup is plain data construction:
game.camera = Camera(
node: game.allocNode(vec2(0, 0)),
shake: Shake(duration: 0, strength: 10)
)
game.createPaddle(
float32(game.windowWidth / 2),
float32(game.windowHeight - 30)
)
game.createBall(
float32(game.windowWidth / 2),
float32(game.windowHeight - 60)
)And systems work directly on typed collections:
proc moveNode(game: var Game; node: NodeIdx; move: Move) =
if move.direction.x != 0 or move.direction.y != 0:
template transform: untyped = game.nodes[node.int].transform
transform.translation.x += move.direction.x * move.speed
transform.translation.y += move.direction.y * move.speed
game.markDirty(node)That is the main point of the project: simple data, simple passes, and no framework magic.
The repository is set up to compile against raylib.h and libraylib.so
located in the project root.
If you already have those files in place:
nim c -d:release game.nim
./gameIf you need to build raylib first:
git clone --depth 1 https://github.com/raysan5/raylib.git /tmp/raylib
make -C /tmp/raylib/src \
PLATFORM=PLATFORM_DESKTOP \
GLFW_LINUX_ENABLE_WAYLAND=TRUE \
GLFW_LINUX_ENABLE_X11=FALSE \
RAYLIB_LIBTYPE=SHARED \
CUSTOM_CFLAGS='-DSUPPORT_CUSTOM_FRAME_CONTROL=1'
cp /tmp/raylib/src/raylib.h .
cp /tmp/raylib/src/libraylib.so* .
nim c -d:release game.nim
./gameBuild raylib as a shared library, then place raylib.h and the resulting
libraylib*.dylib in the project root:
nim c -d:release game.nim
./gameWindows builds expect a vcpkg raylib install and require VcpkgRoot:
nim c --cc:vcc -d:VcpkgRoot="C:\path\to\vcpkg\installed\x64-windows-release" -d:release game.nimLeft ArroworA: move paddle leftRight ArroworD: move paddle rightEsc: quit
Start here if you are reading the code:
- game.nim: window setup, fixed-step loop, interpolation, and render cadence.
- breakout/gametypes.nim: the core data model, including
Game,TransformNode, and the typed gameplay objects. - breakout/blueprints.nim: scene construction plus spawning helpers for balls, bricks, trails, and particles.
- breakout/systems/transform2d.nim: hierarchical transform propagation and previous-frame data for interpolation.
- breakout/systems/collide.nim: AABB preparation and collision resolution flags.
- breakout/systems/draw2d.nim: interpolated rectangle rendering through raylib.
The code keeps concrete gameplay types front and center. You can search for
Ball, Brick, or Paddle and immediately find their storage, update logic,
and rendering path.
Simulation runs on a fixed tick. Rendering interpolates between the previous and current world transform, which is enough to keep movement readable without complicating the game loop.
game.update() calls the systems in a deliberate order:
- control
- shake and fade
- cleanup
- movement
- transform propagation
- collision
That order is easy to inspect and easy to change.
Debug build:
nim c game.nimRelease build:
nim c -d:release game.nimDistributed under the MIT license.