Repository navigation
Home
Genode (Game EngiNe On DEmand) is a game framework that is written in C++17 and built on top of SFML V3.
As the name suggests, this project is on the contrary side of other common engines or frameworks; initially, it is not built for the general use case. It began with the idea that it would only evolve or change to satisfy the demands of my own game development needs, which favor certain conventions, patterns, and even my own personal preferences.
Genode is a standard CMake project that builds as a static library. It can be integrated into your game in three ways: as a subdirectory of your source tree, through FetchContent, or as a separately built library that you link against.
Place the Genode repository inside your project (e.g., as a git submodule under modules/Genode) and add it to your CMakeLists.txt:
add_subdirectory(modules/Genode)
target_link_libraries(MyGame PRIVATE Genode)Genode manages its own dependencies (including SFML) via FetchContent, so no additional setup is required beyond a standard CMake configure and build. Linking the Genode target is enough: the include directories and the dependencies used by the Genode headers (SFML, fmt, nlohmann_json and Boost.PFR) propagate to the consuming target automatically.
Alternatively, let CMake download Genode for you:
include(FetchContent)
FetchContent_Declare(
Genode
GIT_REPOSITORY https://github.com/SirusDoma/Genode.git
GIT_TAG master # or pin a specific commit
)
FetchContent_MakeAvailable(Genode)
target_link_libraries(MyGame PRIVATE Genode)Genode can also be built on its own and linked into your project manually:
cmake -B ./build -DCMAKE_BUILD_TYPE=Release
cmake --build ./build --config ReleaseThe compiled static library is placed in the bin directory (override the location with the OUTPUT_DIR CMake variable). Link the library into your project and add the include directory to your include paths. Since a static library does not carry its dependencies, your project must also link SFML and the rest of the Genode dependencies when building this way.
Genode requires sfml-system, sfml-window, sfml-graphics, sfml-audio and sfml-network. Unlike SFML, each module in Genode is not designed to be compiled or distributed separately. As such, omitting one of those dependencies may result in a compilation error (it is untested)
At the time of writing, upstream SFML supports only a single rendering backend, OpenGL. Genode fetches upstream SFML by default.
Enable the USE_CUSTOM_SFML CMake option to fetch a custom SFML fork that supports multiple rendering backends instead:
cmake -B ./build -DUSE_CUSTOM_SFML=ONWith the fork in use, the backend can be selected at runtime via sf::setGraphicsBackend, and the availability of a given backend can be probed via sf::isGraphicsBackendAvailable before selecting it.
The following articles introduce the modules and guide you to build your game with Genode. It is strongly recommended to read those articles in the following order, from low- to high-levels of abstraction, so you can decide the right amount of abstraction and become familiar with the layers of implementations that you wish to optimize later:
Genode primarily uses SceneGraph as its building block at the high level. However, you can always omit some of these components and roll out your implementation. This could be useful if you're particular about performance and/or implementation in specific areas. See Only pay for what you use for more details.
Typically, you will need to implement these 2 classes:
Gx::ApplicationGx::Scene
This class represents your application class; it provides some simple lifecycle events, such as Boot and Shutdown, which will be called when the application is about to start and exit, respectively. Here's how application class would typically look like:
Important
Gx::Application is designed to work with a single thread for polling inputs and rendering; it also only supports a single render window at a time.
If this does not fit your use case, use Gx::SceneDirector directly or manage Gx::Scene objects manually
#include <Genode/System/Application.hpp>
#include "MyAwesomeScene.hpp"
class MyAwesomeGame : public Gx::Application
{
public:
// Application constructor
MyAwesomeGame(std::string title, const sf::VideoMode& mode, const sf::View& view, const bool fullScreen, const sf::ContextSettings& settings) :
Gx::Application(std::move(title), mode, view, fullScreen, settings)
{
}
protected:
void Boot() override
{
// Initialize your game here.
// For example, you can load global resources, load game config, register scene loaders, etc.
// You should also present your initial scene here
auto& sceneDirector = GetModule<Gx::SceneDirector>();
sceneDirector.Present<MyAwesomeScene>();
}
int Shutdown() override
{
// Called when the game is about to close. Return application exit code here
return 0;
}
}Scene (also known as State or Page) represents a virtual environment comprising a collection of node objects. A game typically has multiple scenes, such as the Main Menu, Inventory, and Battle scenes.
In Genode, every object in the SceneGraph is made of a Gx::Node object, which is a class that represents an object inside the scene. In fact, the Gx::Scene class itself is made of Gx::Node. Each of node objects can have other nodes as children. This allows you to build a complex object hierarchy inside the scene.
For this purpose, many SFML drawable entities such as sf::Sprite, sf::Text and sf::Shape are re-implemented to work with Gx::Node; see Drawable re-implementation for more details.
Here's how a Gx::Scene implementation is typically written:
#include <Genode/SceneGraph/Scene.hpp>
#include <Genode/Graphics/Sprite.hpp>
class MyAwesomeScene : public Gx::Scene
{
public:
// Avoid performing complex initialization in the constructor.
// This is because many scenes' contexts are not initialized when the object is newly constructed.
MyAwesomeScene() = default;
// Similar advice also applies to the destructor.
~MyAwesomeScene() = default;
protected:
// Instead, you should do initialization and clean-up in `Initialize` and `Finalize` respectively
void Initialize() override
{
// Initialize texture and sprite
m_texture = sf::Texture("./some/path/to/texture.png");
m_sprite = Gx::Sprite(m_texture);
// Add sprite into the scene
// You need to ensure that textures and sprites are alive when they are still a part of the active scene
AddChild(m_sprite);
}
void Finalize() override
{
// Do clean-up here if necessary
}
private:
sf::Texture m_texture;
Gx::Sprite m_sprite;
}Running your game is straightforward: simply instantiate your application with the required parameters and call Start() function to start the game.
#include "MyAwesomeGame.hpp"
int main(int argc , char** argv)
{
auto myGame = MyAwesomeGame(
"My Awesome Game", // Title bar string
sf::VideoMode({1920, 1080}), // sf::VideoMode to initialize the window
sf::View({0, 0, 1920, 1080}), // Default view of the application
false // Fullscreen mode
);
// Start is a thread blocking function which returns the exit code of the application, propagated from `Shutdown()`
return myGame.Start();
}Copyright © 2024 - CXO2
This open-source library is licensed under the zlib/libpng license.