Quick Start¶
This guide walks through building a minimal simulation: entities with Position and Velocity components updated by a MovementSystem.
1. Define components¶
Components are plain data structs that inherit from fr::Component. No logic, no methods — just fields.
// components.hpp
#include <Freyr/Freyr.hpp>
struct Position : fr::Component {
float x = 0.f;
float y = 0.f;
float z = 0.f;
};
struct Velocity : fr::Component {
float dx = 0.f;
float dy = 0.f;
float dz = 0.f;
};
2. Define a system¶
Systems inherit from fr::System and override lifecycle hooks. The constructor must accept a Ref<fr::Scene>.
// movement_system.hpp
#include <Freyr/Freyr.hpp>
#include "components.hpp"
class MovementSystem : public fr::System {
public:
explicit MovementSystem(const Ref<fr::Scene>& scene) : System(scene) {}
void Update(float deltaTime) override {
mScene->ForEachParallel<Position, Velocity>(
[deltaTime](fr::Entity, Position& pos, const Velocity& vel) {
pos.x += vel.dx * deltaTime;
pos.y += vel.dy * deltaTime;
pos.z += vel.dz * deltaTime;
});
}
};
3. Define the application¶
// app.hpp
#include <Freyr/Freyr.hpp>
#include "components.hpp"
class MyApp : public skr::IApplication {
public:
explicit MyApp(const Ref<skr::ServiceProvider>& sp) : IApplication(sp) {
mScene = sp->GetService<fr::Scene>();
// Bulk-create 1 million entities with default values
mScene->CreateArchetypeBuilder()
.WithComponent(Position {})
.WithComponent(Velocity { .dx = 1.f, .dy = 0.5f })
.WithEntities(1'000'000)
.Build();
}
void Run() override {
constexpr float dt = 1.0f / 60.0f;
while (true)
mScene->Update(dt);
}
private:
Ref<fr::Scene> mScene;
};
4. Bootstrap with FreyrExtension¶
// main.cpp
#include <Freyr/Freyr.hpp>
#include "app.hpp"
#include "components.hpp"
#include "movement_system.hpp"
int main() {
auto app = skr::ApplicationBuilder()
.AddExtension<fr::FreyrExtension>([](fr::FreyrExtension& freyr) {
freyr
.WithOptions([](fr::FreyrOptionsBuilder& opts) {
opts
.WithMaxEntities(1'500'000) // max live entities
.WithArchetypeChunkCapacity(512) // entities per chunk
.WithThreadCount(8); // worker threads
})
.WithComponent<Position>()
.WithComponent<Velocity>()
.WithSystem<MovementSystem>();
})
.Build<MyApp>();
app->Run();
return 0;
}
5. What happens at runtime¶
main()
└─ ApplicationBuilder::Build<MyApp>()
├─ FreyrExtension registers Position, Velocity, MovementSystem
├─ Scene is created and added to the DI container
└─ MyApp constructor runs
└─ ArchetypeBuilder creates 1 000 000 entities in chunks of 512
MyApp::Run() loop
└─ Scene::Update(dt) each frame
├─ MovementSystem::PreUpdate(dt)
├─ MovementSystem::Update(dt) ← ForEachParallel distributes chunks across 8 threads
├─ MovementSystem::PostUpdate(dt)
└─ deferred entity destruction (none here)
Next steps¶
- ECS Overview — understand the model behind the API
- Scene API — full reference for entity and component operations
- ArchetypeBuilder — efficient bulk entity creation
- Parallel Processing guide — tune parallelism for your workload