Introduction
Warning: The engine is still in its early stages of development, and many features are still being worked on.
If you came here is because you are curious about the engine, right? So let's start with the basics.
What is Project-E?
Project-E aims to:
- Provide a simple and easy-to-use API for development
- Facilitate the creation of games by providing a set of pre-made modules
- Fast enough to run games with a decent amount of entities
- Be cross-platform (Windows, Linux)
- Data Focused
Project-E is a game engine written in C++ for educational purposes during the R-Type project at EPITECH. It is designed to be modular, easy to use, and easy to understand. The engine is based on the Entity-Component-System (ECS) architecture, which allows for a high level of modularity and flexibility.
Lexicon
this page lists all relevant terminologies regarding the project.
ECS
Term | Definition |
---|---|
ECS | Entity-Component-System, design pattern used to manage modular objects |
Entity | An object within the application. Can be a player, enemy, ui element etc... |
Component | Data that can be attached to an entity to give it specific properties. |
System | An operation that perform actions on an entity that has a specific set of components. |
Blob | A ressource that can be used by multiple entities (i.e. meshes for collision shapes) |
Networking
Term | Definition |
---|---|
Networking | The process of transmitting data over the a network connection |
Protocol | A set of rules that define how data is transmitted |
TCP | Transmission Control Protocol, slower but reliable |
UDP | User Datagram Protocol, faster but unreliable |
Latency | The time it takes for a packet to travel to its destination |
Game Engine
Term | Definition |
---|---|
Module | A collection of components, systems, blobs, and premade entities |
Scene | A collection of entities that are loaded into the game |
Getting Started
This section will guide you through the process of setting up the project and building it.
Prerequisites
1. Setup the project
git init
2. Add Project-E as a submodule
git submodule add https://mathematisse/Project-E engine
3. Add the engine to your Project
basic CMakeLists.txt:
cmake_minimum_required(VERSION 3.15)
project(MyProject)
set(CMAKE_CXX_STANDARD 20)
set(RENDER_ENABLED ON)
set(PHYSICS_ENABLED ON)
set(ECS_ENABLED OFF)
set(NETWORK_ENABLED ON)
# Compile the project
add_subdirectory(engine)
target_link_libraries(MyProject PRIVATE core spatial2d render network)
ECS
All logic in Project-E is based on the Entity-Component-System (ECS) architecture. This design pattern is used to manage modular objects in the engine. The ECS architecture is composed of three main components: Entities, Components, and Systems.
For example, an entity can be a player, an enemy, a UI element, etc. A component is data that can be attached to an entity to give it specific properties. A system is an operation that performs actions on an entity that has a specific set of components.
To make use of the ECS architecture, it is encouraged to use break down the logic of your game into small, reusable components. This will allow you to create complex behaviors by combining simple components.
Project-E's Implementation of ECS
Component
A component is a simple data structure that holds data relevant to a specific aspect of an entity. For example, a Position
component might hold the x and y coordinates of an entity.
DECLARE_COMPONENT(
Position,
float, // x
float // y
);
System
class ApplyVelocitySystem : public S::AMonoSystem<C::Position::Pool, C::Velocity::Pool>,
public S::ADeltaTimeSystem {
public:
~ApplyVelocitySystem() override = default;
protected:
void _innerOperate(
typename C::Position::Pool::Types &cposition, typename C::Velocity::Pool::Types &cvelocity
) override
{
auto [x, y] = cposition;
auto [vX, vY] = cvelocity;
x += vX * _deltaTime;
y += vY * _deltaTime;
}
};
Here, ApplyVelocitySystem
is a system that operates on entities with both Position
and Velocity
components.
_innerOperate
is a method that is called for each entity that has the required components. The method receives the data of the components as arguments.
S::AMonoSystem
is a base class that defines the system as a system that operates on entities with a single component.
S::ADeltaTimeSystem
is a base class that provides the system with the delta time of the frame.
Each Component has a Pool
that stores the data of all entities that have that component. The Pool
is a collection of Types
that represent the data of each entity.
for more information on ECS inner workings, you can refer to the ECS documentation
Basic Simulation Loop
DECLARE_COMPONENT(
Position,
float, // x
float // y
);
DECLARE_COMPONENT(
Velocity,
float, // x
float // y
);
using SomeEntity = Aspect<C::Position, C::Velocity>;
auto main() -> int
{
ECS::EntityManager em;
engine::module::Core core(em);
ApplyVelocitySystem applyVelocitySystem;
ECS::E::SomeEntity ePool(64);
ECS::S::SystemTreeNode rootNode(
"Root", {&applyVelocitySystem}
);
if (em.registerFixedSystemNode(rootNode) == false) {
return 1;
}
auto start = std::chrono::high_resolution_clock::now();
while (true) {
auto end = std::chrono::high_resolution_clock::now();
auto deltaTime = std::chrono::duration<float>(end - start).count();
start = end;
em.addTime(deltaTime);
}
em.deleteEverything();
}
Component
System
Modules
Overview
Modules are extensions of the ECS that implement features of the game engine. They provide a collection of defined Components and Systems related to commonly used concepts in game development to speed the process of making games.
Using the build system, you may selectively enable modules to be built along-side your game.
Sections
How to add a module to the engine
1. Create a new module
Create a new directory in the modules
directory. The name of the directory should be the name of the module.
2. Create a CMakeLists.txt
file
create a in modules/<module-name>
directory. The content of the file should be similar to the following:
cmake_minimum_required(VERSION 3.15)
project(<module-name>) # Swap <module-name> with the name of your module
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_library(spatial2d STATIC Systems.cpp) # Change the source file to the source file of the module
target_link_libraries(spatial2d lib_ecs) # link the needed libraries (ex: raylib, etc...)
Don't forget to change <module-name>
to the name of the module and the source files.
3. Make the module available in the engine
Edit the CMakeLists.txt
file in engine/modules/
directory. Add the following lines to the top of the file:
add_module(<module-name>)
4. Notes
- The module doesn't need to specify which other modules it depends on. This is the user's responsibility.
- The module should be documented using Doxygen comments.
- The module should have its own markdown file documenting it. The file should be listed to the registry using a similar format with a hyperlink to the file.
Creating a module
Before creating a module, make sure you have read the how to add a module guide.
Once you have created a new module, you can start adding systems and components to it.
Base Module class
Each module should have a base class that inherits from engine::module::IModule
. This class should have a constructor that takes an ECS::EntityManager
as an argument. The constructor should register all the systems and components of the module.
namespace engine::module {
class PrintHello : public IModule {
ECS::S::PrintHelloSystem printhelloSystem;
ECS::S::PrintHelloNode PrintHelloNode;
public:
PrintHello():
PrintHelloNode("PrintHello", {&printhelloSystem})
{
}
void load(ECS::EntityManager &entityManager) override
{
LOG_INFO("Loading PrintHello module");
if (!entityManager.registerFixedSystemNode(PrintHelloNode, FIXED_ROOT_SYS_GROUP)) {
LOG_ERROR("Failed to register PrintHello system node");
}
}
};
}
Registering the module
To make the module available in the game, you need to add it mannually.
#include "modules/PrintHello/PrintHello.hpp"
auto main() -> int
{
ECS::EntityManager em;
engine::module::PrintHello printHello(em);
printHello.load(em);
}
Module Registry
- Render: Sprites drawing & sprite sheet iteration for animated sprites.
- Spatial 2D: 2D spatial representation with basic movement logic.
- Network: TCP/UDP networking tools for Server/client netcode infrastructure.
Spatial 2D
The Spatial2D
module provides a 2D spatial representation with basic movement logic. It is used to manage the position, rotation, and scale of 2D objects in the game world.
Components
- Position2D (x: float, y: float)
- Size2D (width: float, height: float)
- Rotation (angle: float)
- Velocity2D (x: float, y: float)
- Acceleration2D (x: float, y: float)
Systems
- ApplyVelocity (Position2D, Velocity2D)
- ApplyAcceleration (Velocity2D, Acceleration2D)
Aspects
- StaticEntity (Position2D, Rotation)
- DynamicEntity (+StaticEntity, Velocity2D, Acceleration2D)
Render
The Render
module is responsible for drawing sprites and sprite sheets to the screen. It also provides tools for animating sprites.
Components
- Sprite (textureId: size_t)
- AnimatedSprite (textureId: size_t, nbFrame: uint8_t, animationSpeed: float)
Systems
- DebugDrawSprite (Position2D, Color, Size2D)
- DrawSprite (Position2D, Size2D, Rotation, Sprite)
- DrawAnimatedSprite (Position2D, Size2D, Rotation, AnimatedSprite)
- SpriteAnimation (AnimatedSprite, Timer)
Aspects
- SpriteEntity (Sprite, Size)
- AnimatedSpriteEntity (AnimatedSprite, Timer, Size)
Network Module
The network module provides a set of functions to interact with the network interfaces of the device.
Networking
How the build system works
Introduction
todo...
Dependencies
The build system is based on CMake, a cross-platform build system generator. CMake is used to generate the build files for the project, which can be used to build the project on different platforms and with different compilers.
On linux:
Ubuntu:
sudo apt install build-essential git
sudo apt install libasound2-dev libx11-dev libxrandr-dev libxi-dev libgl1-mesa-dev libglu1-mesa-dev libxcursor-dev libxinerama-dev libwayland-dev libxkbcommon-dev
Fedora:
sudo dnf groupinstall "Development Tools" "Development Libraries"
sudo dnf install alsa-lib-devel mesa-libGL-devel libX11-devel libXrandr-devel libXi-devel libXcursor-devel libXinerama-devel libatomic
Arch Linux:
sudo pacman -S base-devel git
sudo pacman -S alsa-lib mesa libx11 libxrandr libxi libxcursor libxinerama
How to build on Linux
Using CMake:
Release build:
Configure the project:
cmake -B cmake-build-release -S . -DCMAKE_BUILD_TYPE=Release
Build the project:
cmake --build cmake-build-release --parallel --config Release
Debug build:
Configure the project:
cmake -B cmake-build-debug -S . -DCMAKE_BUILD_TYPE=Debug
Build the project:
cmake --build cmake-build-debug --parallel --config Debug
If you are using WSL2, you might have the following error:
c++ fatal error killed signal terminated program cc1plus
You can fix this by removing the --parallel
flag from the build command. (you can enable it again after the first build)
You can also use the provided scripts to build the project:
./scripts/build.sh
How to build on Windows
Using CMake:
Release build:
Configure the project:
cmake -B cmake-build-release -S . -DCMAKE_BUILD_TYPE=Release
Build the project:
cmake --build cmake-build-release --config Release
Debug build:
Configure the project:
cmake -B cmake-build-debug -S . -DCMAKE_BUILD_TYPE=Debug
Build the project:
cmake --build cmake-build-debug --config Debug
You can also use the provided scripts to build the project:
.\scripts\build.ps1
How to run
On linux:
The engine's libraries are located in the cmake-build-release/engine
directory. And are directly linked to the binary. So you need to set the LD_LIBRARY_PATH
environment variable to the cmake-build-release/engine
directory.
Your binary will be located in the cmake-build-release
or cmake-build-debug
directory depending on the build type you chose.
The binary is standalone and can be run from anywhere.
./cmake-build-release/<project>/<binary-name>
On Windows:
On Windows, the engine's libraries are located in the build/engine
directory. And are directly linked to the binary. So you need to set the PATH
environment variable to the build/engine
directory.
Your binary will be located in the build
.
The binary is standalone and can be run from anywhere.
.\build\<project>\<binary-name>.exe
or double-click on the executable, found in the root of the project.