R-Type 0.1
Loading...
Searching...
No Matches
GameEngine.hpp
1#pragma once
2
3#include <Logger/Logger.hpp>
4#include <algorithm>
5#include <chrono>
6#include <string>
7#include <thread>
8
9#include "EngineContext.hpp"
10#include "Game.hpp"
11#include "events/busEvents.hpp"
12
13namespace rtk {
23public:
25 using Clock = std::chrono::steady_clock;
26
27 GameEngine() = default;
28 ~GameEngine() = default;
29
34 ecs::Registry &registry() { return _registry; }
35
40 ecs::Scheduler &scheduler() { return _scheduler; }
41
46 busEvent &eventBus() { return _eventBus; }
47
51 void stop() { _running = false; }
52
57 void setFPS(uint32_t fps) { _targetFps = fps; }
58
70 template <Game TGame> int run(TGame &game) {
71 EngineContext context{
72 .registry = _registry,
73 .scheduler = _scheduler,
74 .eventBus = _eventBus,
75 };
76
77 game.onLoad(context);
78 _running = true;
79
80 auto currentTime = Clock::now();
81 auto nextFrameTarget = currentTime;
82
83 float accumulator = 0.0f;
84 const float fixedDeltaTime = 1.0f / 60.0f;
85
86 _scheduler.start(_registry);
87
88 try {
89 while (_running) {
90 if constexpr (requires {
91 { game.shouldContinue() } -> std::convertible_to<bool>;
92 }) {
93 if (!game.shouldContinue())
94 break;
95 }
96
97 auto newTime = Clock::now();
98 float realDeltaTime =
99 std::chrono::duration<float>(newTime - currentTime).count();
100 currentTime = newTime;
101
102 float frameTime = std::min(realDeltaTime, 0.25f);
103 accumulator += frameTime;
104
105 while (accumulator >= fixedDeltaTime) {
106 accumulator -= fixedDeltaTime;
107 }
108
109 if constexpr (requires {
110 {
111 game.onUpdate(context, frameTime)
112 } -> std::same_as<void>;
113 }) {
114 game.onUpdate(context, frameTime);
115 }
116 _scheduler.run(_registry, frameTime);
117 _registry.flush();
118
119 updateFPS(realDeltaTime);
120
121 if (_targetFps > 0) {
122 const auto targetDuration =
123 std::chrono::nanoseconds(1000000000 / _targetFps);
124
125 nextFrameTarget += targetDuration;
126
127 auto sleepStart = Clock::now();
128
129 if (nextFrameTarget > sleepStart) {
130 auto timeToSleep = nextFrameTarget - sleepStart;
131
132 if (timeToSleep > std::chrono::milliseconds(2))
133 std::this_thread::sleep_for(timeToSleep -
134 std::chrono::milliseconds(2));
135
136 while (Clock::now() < nextFrameTarget) {
137 }
138 } else {
139 nextFrameTarget = Clock::now();
140 }
141 }
142 }
143 } catch (...) {
144 _scheduler.stop(_registry);
145 throw;
146 }
147
148 _scheduler.stop(_registry);
149 if constexpr (requires { game.onUnload(context); }) {
150 game.onUnload(context);
151 }
152 return 0;
153 }
154
155private:
156 void updateFPS(float realDeltaTime) {
157 _fpsTimer += realDeltaTime;
158 _fpsFrames++;
159 if (_fpsTimer >= 1.0f) {
160 LOG_DEBUG("FPS: " + std::to_string(_fpsFrames));
161 _fpsTimer -= 1.0f;
162 _fpsFrames = 0;
163 }
164 }
165
166 ecs::Registry _registry;
167 ecs::Scheduler _scheduler;
168 busEvent _eventBus;
169
170 bool _running = false;
171 uint32_t _targetFps = 0;
172
173 float _fpsTimer = 0.0f;
174 int _fpsFrames = 0;
175};
176
178} // namespace rtk
Type indexed publish and subscribe event bus.
Definition busEvents.hpp:20
Owns the ECS world and runs the main loop of a game.
Definition GameEngine.hpp:22
void stop()
Asks the main loop to exit after the current frame.
Definition GameEngine.hpp:51
void setFPS(uint32_t fps)
Sets the frame rate cap.
Definition GameEngine.hpp:57
busEvent & eventBus()
Returns the event bus shared by the systems.
Definition GameEngine.hpp:46
ecs::Scheduler & scheduler()
Returns the system scheduler.
Definition GameEngine.hpp:40
int run(TGame &game)
Loads the game and runs frames until it stops.
Definition GameEngine.hpp:70
ecs::Registry & registry()
Returns the entity and component registry.
Definition GameEngine.hpp:34
std::chrono::steady_clock Clock
Monotonic clock used to measure frame times.
Definition GameEngine.hpp:25
Engine services handed to the game and its scenes.
Definition EngineContext.hpp:20