A very capable, yet simple 2D game engine made entirely for hobby purposes. It is full of features that target 2D games/applications, with no plans to target 3D any time soon. It is purely a 2D game engine. It currently fully supports Windows, Linux, and the Web.
- Cross-platform window creation with OpenGL4.3 and GLES3 using SOKOL.
- Gamepad, keyboard, and mouse input support.
- A flexible and configurable 2D renderer
- A fully-fledged audio system with both 2D and 3D spatialized audio.
- A robust and easy-to-use game UI system, using HTML and CSS to decalre and style widgets.
- A robust 2D physics engine, using the fantastic Box2D library.
- A flexible Entity Component System (ECS) module, using the EnTT library.
- Integrated ImGui support, featuring an abstracted
guilayer for editing engine-specific types through a GUI. - A durable, but simple noise generation module for both 2D and 3D.
- A simple tile map system for easily editing and adding tile maps.
- GLFW
- GLM
- sokol-gfx
- sokol-gp
- ImGui
- OpenAL-Soft
- Box2D
- RmlUi
- EnTT
- stb
- dr_libs
- Tracy
- moodycamel
- FastNoiseLite
- LUA (CMake-based)
- FileWatch
Before proceeding with any build or compilation step, Freya needs to be cloned from the Git repo.
git clone https://github.com/FrodoAlaska/Freya.gitAnd now that Freya is cloned, we can start the build process...
In general there are two ways to build Freya. The first to use CMake commands just like any other C++ project. The second way is to use a handy build script to manage the build project for you.
There are two main ways to build Freya. The first is to use the traditional CMake commands below:
mkdir build
cd build
cmake .. CMake will generate the required build files, but it will also fetch all of the dependencies mentioned above. This is all handled by CMake itself, so you don't have to worry about any specifics.
And then to build Freya you can use:
cmake --build . --config Debug --parallel 12The command above will build Freya in the debug configuration, using 12 worker threads. You can omit the --parallel flag if you so wish. However, since Freya will build all of the dependencies itself, it might take a while.
Now the second way to build Freya is to use the build scripts found in the scripts directory. There are a few build scripts but there are only two important ones: build-freya.sh for Linux and build-freya.ps1 for Windows. Each script takes in a few flags to make both the development and build process easier.
[Usage]: .\build-freya.ps1 [options]
An easy to use build script to build Freya on Windows
-clean = Have a new fresh build
-debug = Build for the debug configuration
-rel = Build for the release configuration
-jobs [threads] = Threads to use when building
-run-testbed = Run the testbed examples
-help = Display this help message
Currently, the -run flag can only run a single testbed, but there are plans to expand on this in the future.
The -debug and -rel must be used independently. Moreover, one of the flags have to be used to know which configuration to build for. If none of them are used, the script will default to a Debug build. Depending on which flag is passed to the build script, a build-debug and/or build-release will be created in the main directory. Any build artifacts generated by -run will be dumped into one of the previously mentioned directories.
Of course, the project can be built regardless of this build script. This is not a necessary step. It is only a convenience for the development process on our end.
Below is a simple example of the Freya Engine being used.
If the exmaple below is too simple, you can check out the testbed directory where more extensive examples exist. However, they are not as "educational" as the example below. Despite that, they do have a variety of use cases of Freya.
#include <freya.h>
struct App {
freya::Window* window;
freya::AssetGroupID group_id;
freya::EntityWorld world;
};
static App s_app;
bool app_init(const freya::Args& args, freya::Window* window) {
// Carry the window just in case
s_app.window = window;
// Initialize the GUI
freya::gui_init(window);
// Create the application's asset group
s_app.group_id = freya::asset_group_create("app_assets");
// Build an asset package, pointing to a `assets_list.lua` file, spiting out `assets.frpkg`
//
// @NOTE: This function will only build the package if there was any change from last time.
// It should be kept at minimum usage on dev-builds only.
//
freya::asset_group_build(s_app.group_id, "../assets/assets_list.lua", "assets.frpkg");
// Load the asset package that was built by the previous function.
//
// @NOTE: The `frpkg` format is specifically built for this engine.
// It embeds all of the assets gathered and built by the previous function
// and builds them into a neat binary package. On load, the package's content
// is loaded and the given asset group is populated with the assets.
//
// Unlike the previous function, loading the packages should occur at least once
// on the application's initialization to use any assets.
//
freya::asset_group_load_package(s_app.group_id, "assets.frpkg");
// Give our entity world to the renderer so that it can process any rendering
// commands that gets issued from the application.
freya::renderer_sumbit_world(&s_app.world);
//
// Create entities or do other initialization stuff here...
//
// Nothing went wrong. Return true.
return true;
}
void app_shutdown() {
freya::entity_world_clear(s_app.world);
freya::asset_group_destroy(s_app.group_id);
freya::gui_shutdown();
//
// Do other shutdown/cleanup stuff here...
//
}
void app_update(freya::f32 dt) {
// Quit the application when the specified exit key is pressed
if(freya::input_key_pressed(freya::KEY_ESCAPE)) {
freya::event_dispatch(freya::Event{.type = freya::EVENT_APP_QUIT});
return;
}
// Enable/disable the GUI using the F1 key
if(freya::input_key_pressed(freya::KEY_F1)) {
freya::gui_toggle_active();
}
// Update the entity world
freya::entity_world_update(s_app.world, dt);
//
// Update other stuff before here...
//
}
void app_render_gui() {
if(!freya::gui_is_active()) {
return;
}
//
// GUI commands
//
freya::gui_begin();
freya::gui_edit_entity_world("Main", s_app.world);
freya::gui_end();
}
// Main function _must_ have this exact signature, since the `FREYA_MAIN` macro will
// expect it. However, if you wish to make a console application or do not want Freya to
// hijack the main function, you can use the regular `main` function and omit `FREYA_MAIN`
// at the bottom.
int engine_main(int argc, char* argv[]) {
freya::AppDesc app_desc {
.init_fn = app_init,
.shutdown_fn = app_shutdown,
.update_fn = app_update,
.gui_fn = app_render_gui,
.window_title = "Freya Example",
.window_width = 800,
.window_height = 600,
.window_flags = (freya::i32)(freya::WINDOW_FLAGS_CENTER_MOUSE),
.samples_count = 1,
.has_vsync = false,
.args_values = argv,
.args_count = argc,
};
// Run the application in a loop and return the
// result once the application exists.
return freya::engine_run(app_desc);
}
// A macro to specify a main entry function depending on the platform.
// The given `engine_main` will be called inside the entry function.
FREYA_MAIN(engine_main)