mirror of
https://github.com/MoleskiCoder/EightBit.git
synced 2026-10-24 20:02:21 +00:00
For consistency with CS code, move to an event driven SDL3 model. (Probably part 1)
This commit is contained in:
1 parent
3a64cc74d1
commit
7fcf123248
4 files changed
+106
-96
No files matched your search
+8
-9
@@ -19,11 +19,17 @@ namespace Gaming {
|
||||
|
||||
class Game : public EightBit::Device {
|
||||
public:
|
||||
Game(bool verbose = false);
|
||||
Game(SDL_LogPriority logging = SDL_LOG_PRIORITY_WARN);
|
||||
virtual ~Game();
|
||||
|
||||
virtual void runLoop();
|
||||
virtual SDL_AppResult runFrame();
|
||||
virtual SDL_AppResult handleEvent(SDL_Event& e);
|
||||
|
||||
void raisePOWER() noexcept override;
|
||||
void lowerPOWER() noexcept override;
|
||||
|
||||
virtual void initialise();
|
||||
virtual void terminate();
|
||||
|
||||
protected:
|
||||
[[nodiscard]] virtual float fps() const noexcept = 0;
|
||||
@@ -39,11 +45,8 @@ namespace Gaming {
|
||||
|
||||
[[nodiscard]] virtual std::string title() const noexcept = 0;
|
||||
|
||||
virtual void handleEvents();
|
||||
virtual void update();
|
||||
virtual void draw();
|
||||
virtual bool maybeSynchronise(); // true, if manual synchronisation required
|
||||
virtual void synchronise();
|
||||
|
||||
virtual void runRasterLines() {};
|
||||
virtual void runVerticalBlank() {}
|
||||
@@ -88,10 +91,6 @@ namespace Gaming {
|
||||
SDL_PixelFormat m_pixelType = SDL_PIXELFORMAT_ARGB8888;
|
||||
|
||||
bool m_vsync = false;
|
||||
Uint64 m_performanceFrequency = 0;
|
||||
double m_targetFrameTime = 0.0; // in seconds
|
||||
Uint64 m_frameStartTime = 0; // in performance frequency
|
||||
Uint64 m_frameEndTime = 0; // in performance frequency
|
||||
|
||||
std::map<int, std::shared_ptr<GameController>> m_gameControllers;
|
||||
|
||||
|
||||
+15
-3
@@ -5,10 +5,19 @@
|
||||
#include <sstream>
|
||||
|
||||
namespace Gaming {
|
||||
class Wrapper final {
|
||||
class Wrapper final : public EightBit::Device {
|
||||
|
||||
using base = Device;
|
||||
|
||||
public:
|
||||
Wrapper(bool verbose = false);
|
||||
~Wrapper();
|
||||
Wrapper(SDL_LogPriority logging = SDL_LOG_PRIORITY_WARN);
|
||||
~Wrapper() = default;
|
||||
|
||||
void raisePOWER() noexcept final;
|
||||
void lowerPOWER() noexcept final;
|
||||
|
||||
void initialise();
|
||||
void terminate();
|
||||
|
||||
static void throwException(const std::string& failure) {
|
||||
std::ostringstream output;
|
||||
@@ -24,5 +33,8 @@ namespace Gaming {
|
||||
static void maybeThrowException(void* handle, const std::string& failure) {
|
||||
maybeThrowException(handle != nullptr, failure);
|
||||
}
|
||||
|
||||
private:
|
||||
SDL_LogPriority m_logging;
|
||||
};
|
||||
}
|
||||
+61
-76
@@ -3,10 +3,12 @@
|
||||
|
||||
#include "../inc/GameController.h"
|
||||
|
||||
#include <format>
|
||||
|
||||
namespace Gaming {
|
||||
|
||||
Game::Game(bool verbose /* = false */)
|
||||
: m_wrapper(verbose) {}
|
||||
Game::Game(SDL_LogPriority logging /* = SDL_LOG_PRIORITY_WARN */)
|
||||
: m_wrapper(logging) {}
|
||||
|
||||
Game::~Game() {}
|
||||
|
||||
@@ -27,8 +29,18 @@ int Game::displayHeight() const noexcept {
|
||||
}
|
||||
|
||||
void Game::raisePOWER() noexcept {
|
||||
|
||||
Device::raisePOWER();
|
||||
initialise();
|
||||
}
|
||||
|
||||
void Game::lowerPOWER() noexcept {
|
||||
terminate();
|
||||
Device::lowerPOWER();
|
||||
}
|
||||
|
||||
void Game::initialise() {
|
||||
|
||||
m_wrapper.raisePOWER();
|
||||
|
||||
m_window.reset(::SDL_CreateWindow(title().c_str(), windowWidth(), windowHeight(), 0), ::SDL_DestroyWindow);
|
||||
Wrapper::maybeThrowException(m_window.get(), "Unable to create window");
|
||||
@@ -59,14 +71,24 @@ void Game::raisePOWER() noexcept {
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_vsync) {
|
||||
SDL_LogInfo(SDL_LOG_CATEGORY_RENDER, "Setting callback rate hint");
|
||||
const auto success = SDL_SetHint(SDL_HINT_MAIN_CALLBACK_RATE, std::format("{}", fps()).c_str());
|
||||
Wrapper::maybeThrowException(success, "Unable to set event loop callback rate hint");
|
||||
}
|
||||
|
||||
m_pixelFormat = SDL_GetPixelFormatDetails(m_pixelType);
|
||||
Wrapper::maybeThrowException(m_pixelFormat, "Unable to obtain pixel format details");
|
||||
|
||||
configureBackground();
|
||||
createBitmapTexture();
|
||||
}
|
||||
|
||||
m_performanceFrequency = ::SDL_GetPerformanceFrequency();
|
||||
m_targetFrameTime = 1.0 / fps();
|
||||
void Game::terminate() {
|
||||
m_bitmapTexture.reset();
|
||||
m_renderer.reset();
|
||||
m_window.reset();
|
||||
m_wrapper.lowerPOWER();
|
||||
}
|
||||
|
||||
void Game::configureBackground() const {
|
||||
@@ -79,54 +101,48 @@ void Game::createBitmapTexture() {
|
||||
Wrapper::maybeThrowException(m_bitmapTexture.get(), "Unable to create bitmap texture");
|
||||
}
|
||||
|
||||
void Game::runLoop() {
|
||||
while (powered()) {
|
||||
update();
|
||||
draw();
|
||||
maybeSynchronise();
|
||||
}
|
||||
SDL_AppResult Game::runFrame() {
|
||||
update();
|
||||
draw();
|
||||
return SDL_APP_CONTINUE;
|
||||
}
|
||||
|
||||
void Game::update() {
|
||||
m_frameStartTime = ::SDL_GetPerformanceCounter();
|
||||
handleEvents();
|
||||
runVerticalBlank();
|
||||
runRasterLines();
|
||||
}
|
||||
|
||||
void Game::handleEvents() {
|
||||
::SDL_Event e;
|
||||
while (::SDL_PollEvent(&e)) {
|
||||
switch (e.type) {
|
||||
case SDL_EVENT_QUIT:
|
||||
lowerPOWER();
|
||||
break;
|
||||
case SDL_EVENT_KEY_DOWN:
|
||||
handleKeyDown(e.key.key);
|
||||
break;
|
||||
case SDL_EVENT_KEY_UP:
|
||||
handleKeyUp(e.key.key);
|
||||
break;
|
||||
case SDL_EVENT_JOYSTICK_BUTTON_DOWN:
|
||||
handleJoyButtonDown(e.jbutton);
|
||||
break;
|
||||
case SDL_EVENT_JOYSTICK_BUTTON_UP:
|
||||
handleJoyButtonUp(e.jbutton);
|
||||
break;
|
||||
case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
|
||||
handleGamepadButtonDown(e.gbutton);
|
||||
break;
|
||||
case SDL_EVENT_GAMEPAD_BUTTON_UP:
|
||||
handleGamepadButtonUp(e.gbutton);
|
||||
break;
|
||||
case SDL_EVENT_GAMEPAD_ADDED:
|
||||
addGamepad(e);
|
||||
break;
|
||||
case SDL_EVENT_GAMEPAD_REMOVED:
|
||||
removeGamepad(e);
|
||||
break;
|
||||
}
|
||||
SDL_AppResult Game::handleEvent(SDL_Event& e) {
|
||||
switch (e.type) {
|
||||
case SDL_EVENT_QUIT:
|
||||
return SDL_APP_SUCCESS;
|
||||
break;
|
||||
case SDL_EVENT_KEY_DOWN:
|
||||
handleKeyDown(e.key.key);
|
||||
break;
|
||||
case SDL_EVENT_KEY_UP:
|
||||
handleKeyUp(e.key.key);
|
||||
break;
|
||||
case SDL_EVENT_JOYSTICK_BUTTON_DOWN:
|
||||
handleJoyButtonDown(e.jbutton);
|
||||
break;
|
||||
case SDL_EVENT_JOYSTICK_BUTTON_UP:
|
||||
handleJoyButtonUp(e.jbutton);
|
||||
break;
|
||||
case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
|
||||
handleGamepadButtonDown(e.gbutton);
|
||||
break;
|
||||
case SDL_EVENT_GAMEPAD_BUTTON_UP:
|
||||
handleGamepadButtonUp(e.gbutton);
|
||||
break;
|
||||
case SDL_EVENT_GAMEPAD_ADDED:
|
||||
addGamepad(e);
|
||||
break;
|
||||
case SDL_EVENT_GAMEPAD_REMOVED:
|
||||
removeGamepad(e);
|
||||
break;
|
||||
}
|
||||
return SDL_APP_CONTINUE;
|
||||
}
|
||||
|
||||
void Game::draw() {
|
||||
@@ -135,37 +151,6 @@ void Game::draw() {
|
||||
displayTexture();
|
||||
}
|
||||
|
||||
bool Game::maybeSynchronise() {
|
||||
const bool synchronising = !m_vsync;
|
||||
if (synchronising)
|
||||
synchronise();
|
||||
return synchronising;
|
||||
}
|
||||
|
||||
void Game::synchronise() {
|
||||
|
||||
m_frameEndTime = ::SDL_GetPerformanceCounter();
|
||||
|
||||
const auto frameTime = m_frameEndTime - m_frameStartTime; // In performance frequency
|
||||
::SDL_LogDebug(SDL_LOG_CATEGORY_RENDER, "Frame time (ticks): %ld", frameTime);
|
||||
|
||||
const auto elapsedFrameTime = double(frameTime) / m_performanceFrequency; // In seconds
|
||||
::SDL_LogDebug(SDL_LOG_CATEGORY_RENDER, "Frame time (seconds): %f", elapsedFrameTime);
|
||||
|
||||
const auto gap = m_targetFrameTime - elapsedFrameTime; // in seconds
|
||||
::SDL_LogDebug(SDL_LOG_CATEGORY_RENDER, "Timing gap (seconds): %f", gap);
|
||||
|
||||
if (gap > 0) {
|
||||
const auto delay = Uint32(gap * 1000.0);
|
||||
::SDL_LogDebug(SDL_LOG_CATEGORY_RENDER, "Delay (ticks): %d", delay);
|
||||
SDL_Delay(delay);
|
||||
}
|
||||
|
||||
if (gap < 0) {
|
||||
::SDL_LogWarn(SDL_LOG_CATEGORY_RENDER, "Running slowly");
|
||||
}
|
||||
}
|
||||
|
||||
void Game::removeGamepad(SDL_Event& e) {
|
||||
const auto which = e.gdevice.which;
|
||||
const auto found = m_gameControllers.find(which);
|
||||
|
||||
+22
-8
@@ -3,13 +3,27 @@
|
||||
|
||||
using namespace Gaming;
|
||||
|
||||
Wrapper::Wrapper(bool verbose /* = false */) {
|
||||
const auto success = ::SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_GAMEPAD | SDL_INIT_HAPTIC);
|
||||
maybeThrowException(success, "Unable to initialise SDL library");
|
||||
const auto priority = verbose ? SDL_LogPriority::SDL_LOG_PRIORITY_VERBOSE : SDL_LogPriority::SDL_LOG_PRIORITY_ERROR;
|
||||
::SDL_SetLogPriorities(priority);
|
||||
Wrapper::Wrapper(SDL_LogPriority logging /* = SDL_LOG_PRIORITY_WARN */)
|
||||
: m_logging(logging) {}
|
||||
|
||||
void Wrapper::raisePOWER() noexcept {
|
||||
base::raisePOWER();
|
||||
initialise();
|
||||
}
|
||||
|
||||
Wrapper::~Wrapper() {
|
||||
::SDL_Quit();
|
||||
}
|
||||
void Wrapper::lowerPOWER() noexcept {
|
||||
terminate();
|
||||
base::lowerPOWER();
|
||||
}
|
||||
|
||||
void Wrapper::initialise() {
|
||||
const auto success = ::SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_GAMEPAD | SDL_INIT_HAPTIC);
|
||||
maybeThrowException(success, "Unable to initialise SDL library");
|
||||
::SDL_SetLogPriorities(m_logging);
|
||||
::SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "SDL library initialised");
|
||||
}
|
||||
|
||||
void Wrapper::terminate() {
|
||||
::SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "SDL library terminating");
|
||||
::SDL_Quit();
|
||||
}
|
||||
Reference in new issue
Block a user