The Koala engine is a type-safe and self-documenting implementation of an Entity-Component-System (ECS), with a focus on runtime extensibility and compile-time type safety and clarity.
This new version features a greatly optimized memory architecture, better cache-efficiency, and a more advanced API.
It has however (temporarily) lost a couple of features:
- Entities no longer have names. If you really need to associate strings to your Entities you can create a
NameComponent
or something similar - Entities can no longer be serialized to
ostream
. I am currently working to re-implement this feature.* Because of the previous point, it is no longer possible tosave
andload
the game state. I am currently working to re-implement this feature, in a manner that will be better optimized than the original (binary saving instead of JSON serialization) - Saves now work (in binary format, using a separate save file for each component pool and another file for the list of entities and their masks) but have not been merged into the
master
branch yet. If you desperately need them ASAP, either use thedevelop
branch or open an issue and I'll merge it.
The project uses git submodules extensively, and must therefore be cloned recursively.
git clone --recursive git@github.com:phiste/kengine
Alternatively, if using this resository as a submodule of your own project, make sure to init, sync and update your submodules in order to make sure the "putils" folder isn't empty.
The engine requires a C++17 compiler.
- Entity: represents an in-game entity. Is simply a container of
Components
- System: holds game logic. A
PhysicsSystem
might control the movement ofEntities
, for instance. - EntityManager: manages
Entities
,Components
andSystems
Note that there is no Component
class. Any type can be used as a Component
, and dynamically attached/detached to Entities
.
Many parts of the engine (such as the scripting systems or the OpenGL system) make use of putils
' reflection API. Most of the components in the following samples are thus defined as reflectible.
These are pre-built, extensible and pluggable elements that can be used in any project, or to bootstrap a project.
General purpose gamedev:
- TransformComponent: defines a
Entity
's position and size - PhysicsComponent: defines a
Entity
's movement
Behaviors:
- BehaviorComponent: defines a function to be called each frame for a
Entity
- LuaComponent: defines the lua scripts to be run by the
LuaSystem
for aEntity
- PyComponent: defines the Python scripts to be run by the
PySystem
for aEntity
- CollisionComponent: defines a function to be called when a
Entity
collides with another
Debug tools:
- AdjustableComponent: lets users modify variables through a GUI (such as the ImGuiAdjustableManager)
- ImGuiComponent: lets
Entities
render debug elements using ImGui - DebugGraphicsComponent: lets a
Entity
be used to draw debug information (such as lines, rectangles or spheres)
Graphics and gamedev:
- GraphicsComponent: provides graphical information about a
Entity
, such as its appearance, used by theSfSystem
- CameraComponent: lets
Entities
be used as in-game cameras, to define a frustrum and position. Follows the same conventions asTransformComponent
- InputComponent: lets
Entities
receive keyboard and mouse events - GUIComponent: lets
Entities
be used as GUI elements such as buttons, lists...)
- BehaviorSystem: executes behaviors attached to
Entities
- CollisionSystem: transfers collision notifications to
Entities
- LogSystem: logs messages
- LuaSystem: executes lua scripts attached to an entity
- PySystem: executes Python scripts attached to an entity
- PhysicsSystem: moves entities in a framerate-independent way
- SfSystem: displays entities in an SFML render window
- Log: received by the
LogSystem
, used to log a message - Collision: sent by the
PhysicsSystem
, indicates a collision between twoEntities
- RegisterAppearance: received by the
SfSystem
, maps an abstract appearance to a concrete texture file.
These are datapackets sent from one System
to another to communicate.
Here is a list of simple, half-a-day implementation of games:
Old API, deprecated:
- Flappy bird clone
- Tunnel game, dodging cubes
- Tower defense "game", not much at stake though
- Shoot'em up game
Below is a commented main function that creates an entity and attaches some components to it, as well as a lua script. This should let you get an idea of what is possible using the kengine's support for reflection and runtime extensibility, as well as the compile-time clarity and type-safety that were the two motivations behind the project.
#include <iostream>
#include "go_to_bin_dir.hpp"
#include "EntityManager.hpp"
#include "Entity.hpp"
#include "systems/LuaSystem.hpp"
#include "components/GraphicsComponent.hpp"
#include "components/TransformComponent.hpp"
// Simple system that outputs the transform and lua components of each entity that has them
class DebugSystem : public kengine::System<DebugSystem> {
public:
DebugSystem(kengine::EntityManager & em) : System<DebugSystem>(em), _em(em) {}
void execute() final {
for (const auto & [e, transform, lua] : _em.getEntities<kengine::TransformComponent3f, kengine::LuaComponent>()) {
std::cout << "Entity " << e.id << '\n';
std::cout << "\tTransform: " << transform.boundingBox << '\n';
std::cout << "\tScripts:" << '\n';
for (const auto & script : lua.getScripts())
std::cout << "\t\t[" << script << "]\n";
std::cout << '\n';
}
}
private:
kengine::EntityManager & _em;
};
int main(int, char **av) {
// Go to the executable's directory to be next to resources and scripts
putils::goToBinDir(av[0]);
// Create an EntityManager
kengine::EntityManager em; // Optionally, pass a number of threads as parameter (kengine::EntityManager em(4);)
// Load the specified systems, and any plugin placed in the executable's directory
// If you specify 'KENGINE_SFML' as TRUE in your CMakeLists.txt, this will load the SfSystem
em.loadSystems<DebugSystem, kengine::LuaSystem>(".");
// To add a new system, simply add a DLL with a
// `ISystem *getSystem(kengine::EntityManager &em)`
// To add a new system, simply add a DLL with a
// Create an Entity and attach Components to it
em += [](kengine::Entity e) {
e += kengine::TransformComponent3f({ 42.f, 0.f, 42.f }); // Parameter is a Point3f for position
e += kengine::LuaComponent({ "scripts/unit.lua" }); // Parameter is a vector of scripts
};
// Register types to be used in lua
try {
auto &lua = em.getSystem<kengine::LuaSystem>();
lua.registerTypes<
kengine::TransformComponent3f, putils::Point<float, 3>, putils::Rect<float, 3>,
kengine::LuaComponent
>();
}
catch (const std::out_of_range &) {} // If the LuaSystem wasn't found, ignore
// Start game
while (em.running)
em.execute();
return (EXIT_SUCCESS);
}
-- Simply modify component
local transform = self:getTransformComponent()
local pos = transform.boundingBox.topLeft
pos.x = pos.x + 1