Loops
At the heart of many game engines lies the event loop, a continuous cycle that checks for and responds to events, such as user input or system triggers.
In Obscura, loops are extensions of dispatchers. This means every loop has its own dedicated dispatcher. When you want a loop to respond to specific events or inputs, you need to register the appropriate listeners with that particular loop.
We recommend to initialize and organize loops via the Scheduler.
There are three types of loops:
MainLoop: The main loop for the engine. There should only be one main loop.
SyncLoop: A loop that runs in sync with the main loop.
AsyncLoop: A loop that runs asynchronously to the main loop.
Available loops
-
template<Dispatchable Dispatcher>
class MainLoop : public obscura::LoopBase<Dispatcher>
-
template<Dispatchable Dispatcher, Loop BaseLoop>
class SyncLoop : public obscura::LoopBase<Dispatcher> Public Functions
-
inline void run(BaseLoop &newBaseLoop, const LoopConfig &config)
-
inline void stop()
-
inline void run(BaseLoop &newBaseLoop, const LoopConfig &config)
-
template<Dispatchable Dispatcher>
class AsyncLoop : public obscura::LoopBase<Dispatcher>
Loop base class
-
template<Dispatchable Dispatcher>
class LoopBase : public obscura::ConfigurableDispatcher<TickDefinition, KeyPressedDefinition, MouseMovedDefintion, KeyReleasedDefinition, KeyRepeatedDefinition, WindowClosedDefintion, MouseClickedDefinition, LoopEndDefinition> Subclassed by obscura::MainLoop< obscura::ConfigurableDispatcher >, obscura::AsyncLoop< Dispatcher >, obscura::MainLoop< Dispatcher >, obscura::SyncLoop< Dispatcher, BaseLoop >
Public Functions
-
inline LoopBase()
-
~LoopBase() = default
-
LoopBase(LoopBase<Dispatcher>&&) noexcept = default
-
auto operator=(LoopBase<Dispatcher>&&) noexcept -> LoopBase<Dispatcher>& = default
-
LoopBase(const LoopBase<Dispatcher>&) = delete
-
auto operator=(const LoopBase<Dispatcher>&) -> LoopBase<Dispatcher>& = delete
-
inline void tick()
-
template<class Event>
inline void scheduleOnce(Event &&event)
-
inline auto getFramePacer() const -> const FramePacer<>&
-
inline void waitForStop()
-
inline LoopBase()
Loop config
-
class LoopConfig
Subclassed by obscura::LoopSynchronizer< BaseLoop, DependentLoop >
Public Static Functions
-
static auto targetFPS(const FPS &fps)
-
static auto runAtFractionOfBaseLoop(float fraction) -> FractionalLoopConfig
-
static auto targetFPS(const FPS &fps)
-
class FractionalLoopConfig : public obscura::BaseLoopConfig<FractionalLoopConfig>
Public Functions
-
auto build() const -> LoopConfig
-
auto build() const -> LoopConfig
-
class TargetFPSLoopConfig : public obscura::BaseLoopConfig<TargetFPSLoopConfig>
Public Functions
-
auto build() const
-
auto build() const
-
template<class Child>
class BaseLoopConfig
Examples
Create and use multiple synchronous loops
#include <obscura/obscura.hxx>
using namespace obscura;
using namespace std::chrono_literals;
class Listener {
public:
explicit Listener(std::string shoutOut)
: shoutOut(std::move(shoutOut)) {
}
void onTick(const TickEvent& /*unused*/) {
logInfo("{}", shoutOut);
}
private:
std::string shoutOut;
};
// NOLINTBEGIN(cppcoreguidelines-avoid-magic-numbers,readability-magic-numbers)
auto main() -> int {
try {
auto application = obscura::Application<HeadlessWindow>();
auto defaultApplicationListener = DefaultApplicationListener<Application<HeadlessWindow>>(application);
auto mainListener = Listener("cha");
auto syncListener1 = Listener("Dooo!");
auto syncListener2 = Listener("MAA!");
auto& scheduler = Scheduler<Dispatcher>::getInstance();
scheduler.registerListener(mainListener);
scheduler.registerListener(defaultApplicationListener);
auto loopConfig1 = LoopConfig::runAtFractionOfBaseLoop(0.5).build();
scheduler.createSyncLoop("syncLoop1", loopConfig1);
auto loopConfig2 = LoopConfig::runAtFractionOfBaseLoop(0.25).build();
scheduler.createSyncLoop("syncLoop2", loopConfig2);
scheduler.registerListener(syncListener1, "syncLoop1");
scheduler.registerListener(syncListener2, "syncLoop2");
scheduler.scheduleOnce(WindowEvent { EventCause::WindowClosed }, 2s);
application.run();
} catch (const std::exception& e) {
logError("Exception caught: {}", e.what());
}
return EXIT_SUCCESS;
}
// NOLINTEND(cppcoreguidelines-avoid-magic-numbers,readability-magic-numbers)