Saga
Saga Game Engine
Loading...
Searching...
No Matches
Saga Engine

This is a game engine created in Spring 2023 by Thuleanx for Brown's 3D Game Engine course. It is written from scratch, with the exception of a stencil graphics library, and heavily uses OpenGL and a custom Entity Component System. Here are the main selling points of the engine:

  • Simple: Component can be any struct or class, Systems are any function that accepts a GameWorld, and [Entity](Saga::entity_type ) is an integer.
  • Fast: Continuous collision detection runs at 60 fps even with a scene with 130000+ triangles on its mesh colliders. All dynamic colliders are modelled as axis-aligned ellipsoids, which means fast continuous collision detection.
  • Quality of life integrations: With FMOD Audio Engine for adaptive 3D sound, and imgui for editor GUI.

How it works

Applications inherits from App and is managed by Core. They manage a collection of active GameWorld, each containing entities, components, and systems. Applications can create and add the current game world to the list of active game world by running:

std::shared_ptr<GameWorld> world = createGameWorld();

After this, entities, components, and systems can be added to the world. For instance, to add a Sphere that circles around the origin, you can do:

// entity creation
Saga::Entity entity = world->createEntity();
// adding components (mesh, transform, material)
world->emplace<Saga::Material>(entity, glm::vec3(1,0,0)); // red material
world->emplace<Saga::Transform>(entity);
// adding systems
// systems can be any function that accept a GameWorld. In this case,
// since this system runs every update, it also accept time arguments
auto objectCirclingSystem = [](std::shared_ptr<Saga::GameWorld> world,
float time, float deltaTime) {
for (auto transform : *world->viewAll<Saga::Transform>()) {
glm::vec2 orbitDir(std::cos(time), std::sin(time));
float orbitDistance = 3;
transform->setPos(orbitDistance * orbitDir);
}
};
world->getSystems()->addStagesSystem(objectCirclingSystem, Stage::Update);
Wrapper around the GraphisEngine's material.
Definition: material.h:13
A wrapper around a shared_ptr to a GraphicsEngine's shape.
Definition: mesh.h:11
A wrapper around a shared pointer to a ModelTransform. This tells object where to position themselves...
Definition: transform.h:12

Under the hood, Saga stores components next to each other in memory, allowing for better cache locality when iterating through all components of the same type. Of course, one can also iterate through a group of components, which contains all entities with the specified components. The following code iterate through all meshes and draw them on screen.

void drawSystem(std::shared_ptr<GameWorld> world) {
//... setup camera
for (auto [entity, material, mesh, transform] : *world->viewGroup<Material, Mesh, Transform>()) {
GraphicsEngine::Global::graphics.drawShape(*mesh, *transform, *material);
}
}

If these groups are registered beforehand with `registerGroup`, then these group can be iterated through in $O(n)$ where n is the number of elements in the group.