What is Goodluck?
Goodluck is a template for creating small browser games which fit in a few kilobytes. It's designed for extreme hackability.
Goodluck is a bit unusual in that it's not a typical library; you don't import code from it to use in your project. Instead, Goodluck is a repository template — generate a new repository from it, remove the code you don't need, and hack away. This comes in handy when optimizing code for size (see below), or when you just need to hardcode something one day before the deadline.
Goodluck implements the ECS architecture.
- Game object data is stored in arrays of components, each responsible for a different concern (
- Entities are indices into these arrays (really, they are just
numbers). They also index a special
- Systems are called one by one, and iterate over all entities in the scene and execute logic on the entitie whose signatures match the system's query mask. The game data flows in one direction. It's stored in component arrays and goes through the systems in a deterministic order.
The ECS architecture is all about composition over inheritance. Components can be added, removed, and re-added dynamically during the entity's lifetime. You can mix and match different behaviors without worrying about pulling in too much logic from a superclass. As the project grows, the behaviors continue to be well isolated from each other. The ease of adding new behaviors to existing entities makes it easy and pleasant to experiment with new gameplay ideas.
The key insight of Goodluck is that abstractions, generalizations, and parametrization are responsible for a lot of cruft in the code. And so, one of the design principles is to write simple, unsurprising code. Systems are just functions with a
Goodluck and ECS are by no means perfect. It's helpful to understand their limitations and challenges they come with.
Why not ECS?
- One of the challenges with ECS is ordering systems correctly. It’s an inherent design problem in ECS and if done wrong, can lead to hard to track bugs.
- Related to the above, the communication between entities can be hard, confusing, or hard to follow.
- Coming from the object-oriented paradigm, sometimes it may be mildly annoying that there isn't an
update()method that can be tweaked. Instead, you may need to write a whole new system (OTOH, they're just for loops, so it's not a lot of work), or try to find another solution, in particular for some one-off logic.
- Debugging and steping through the code can be sometimes made difficult by the fact that the logic is encapsulated in systems, which process all entities in the world before yielding to another system. If you want to follow a single entity as it moves through multiple systems, you'll need to use conditional breakpoints to pause the runtime only when the entity of interest is being processed.
Why not Goodluck?
- Goodluck has seen usage in js13kGames, Gamedev.js, and even Ludum Dare, but it was mostly always the same people: @michalbe and myself, @stasm. There's a chance the barrier to entry for anyone else is high.
- Goodluck is rough. I keep evolving it as I learn new things about gamedev and rendering and encounter new use-cases. Every new game I make with Goodluck is an opportunity for upstreaming improvements. If you prefer more stable frameworks, you may want to pass on Goodluck.
- Goodluck started out as a solution for building 3D games. In 2022 I began working on a 2D renderer and a collection of 2D systems. I'm sure there are many improvements still waiting to be made.
- As of August 2022, Goodluck's codebase has no tests.
- As of August 2022, Goodluck's documentation is scarce. I've been progressively documenting Goodluck's codebase to generate the API reference docs. There are multiple examples in the Goodluck repo, but they don't explain what they do nor how they do it. You're on your own... Good luck, and have fun!