mirror of
https://github.com/MoleskiCoder/EightBit.git
synced 2024-12-22 09:30:32 +00:00
Tidy the Gaming library a little:
*) Add an SDLWrapper class to control the lifetime of SDL_Init/SDL_Quit *) Pass FPS as a float, rather than int *) Allow the key and button handlers to show whether an event was handled or not *) Add a full screen render option (F12) *) Use smart pointers in the GameController class Signed-off-by: Adrian Conlon <Adrian.conlon@gmail.com>
This commit is contained in:
parent
1577455a67
commit
89fae1cb6f
@ -11,22 +11,13 @@
|
||||
#include <Device.h>
|
||||
|
||||
#include "GameController.h"
|
||||
#include "SDLWrapper.h"
|
||||
|
||||
class Configuration;
|
||||
|
||||
namespace Gaming {
|
||||
class Game : public EightBit::Device {
|
||||
public:
|
||||
|
||||
static void throwSDLException(std::string failure) {
|
||||
throw std::runtime_error(failure + ::SDL_GetError());
|
||||
}
|
||||
|
||||
static void verifySDLCall(int returned, std::string failure) {
|
||||
if (returned < 0)
|
||||
throwSDLException(failure);
|
||||
}
|
||||
|
||||
Game();
|
||||
virtual ~Game();
|
||||
|
||||
@ -34,7 +25,7 @@ namespace Gaming {
|
||||
virtual void raisePOWER() override;
|
||||
|
||||
protected:
|
||||
virtual int fps() const = 0;
|
||||
virtual float fps() const = 0;
|
||||
virtual bool useVsync() const = 0;
|
||||
|
||||
virtual int windowWidth() const noexcept { return displayWidth() * displayScale(); }
|
||||
@ -59,6 +50,17 @@ namespace Gaming {
|
||||
|
||||
virtual const uint32_t* pixels() const = 0;
|
||||
|
||||
virtual bool handleKeyDown(SDL_Keycode key);
|
||||
virtual bool handleKeyUp(SDL_Keycode key);
|
||||
|
||||
virtual bool handleJoyButtonDown(SDL_JoyButtonEvent event);
|
||||
virtual bool handleJoyButtonUp(SDL_JoyButtonEvent event);
|
||||
|
||||
virtual bool handleControllerButtonDown(SDL_ControllerButtonEvent event);
|
||||
virtual bool handleControllerButtonUp(SDL_ControllerButtonEvent event);
|
||||
|
||||
void toggleFullscreen();
|
||||
|
||||
std::shared_ptr<GameController> gameController(const int which) const {
|
||||
const auto i = m_gameControllers.find(which);
|
||||
if (i == m_gameControllers.cend())
|
||||
@ -81,6 +83,8 @@ namespace Gaming {
|
||||
std::shared_ptr<SDL_PixelFormat> pixelFormat() const noexcept { return m_pixelFormat; }
|
||||
|
||||
private:
|
||||
SDLWrapper m_wrapper;
|
||||
|
||||
std::shared_ptr<SDL_Window> m_window;
|
||||
std::shared_ptr<SDL_Renderer> m_renderer;
|
||||
std::shared_ptr<SDL_Texture> m_bitmapTexture;
|
||||
@ -97,14 +101,5 @@ namespace Gaming {
|
||||
|
||||
void configureBackground() const;
|
||||
void createBitmapTexture();
|
||||
|
||||
virtual void handleKeyDown(SDL_Keycode key) {}
|
||||
virtual void handleKeyUp(SDL_Keycode key) {}
|
||||
|
||||
virtual void handleJoyButtonDown(SDL_JoyButtonEvent event) {}
|
||||
virtual void handleJoyButtonUp(SDL_JoyButtonEvent event) {}
|
||||
|
||||
virtual void handleControllerButtonDown(SDL_ControllerButtonEvent event) {}
|
||||
virtual void handleControllerButtonUp(SDL_ControllerButtonEvent event) {}
|
||||
};
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
namespace Gaming {
|
||||
@ -17,17 +19,17 @@ namespace Gaming {
|
||||
}
|
||||
|
||||
auto getJoystickId() const noexcept {
|
||||
return buildJoystickId(m_gameController);
|
||||
return buildJoystickId(m_gameController.get());
|
||||
}
|
||||
|
||||
private:
|
||||
int m_index;
|
||||
SDL_GameController* m_gameController = nullptr;
|
||||
std::shared_ptr<SDL_GameController> m_gameController;
|
||||
|
||||
void open();
|
||||
void close() noexcept;
|
||||
|
||||
SDL_Haptic* m_hapticController = nullptr;
|
||||
std::shared_ptr<SDL_Haptic> m_hapticController;
|
||||
bool m_hapticRumbleSupported = false;
|
||||
|
||||
void openHapticController();
|
||||
|
18
Gaming/inc/SDLWrapper.h
Normal file
18
Gaming/inc/SDLWrapper.h
Normal file
@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
namespace Gaming {
|
||||
class SDLWrapper final {
|
||||
public:
|
||||
SDLWrapper();
|
||||
~SDLWrapper();
|
||||
|
||||
static void throwSDLException(std::string failure) {
|
||||
throw std::runtime_error(failure + ::SDL_GetError());
|
||||
}
|
||||
|
||||
static void verifySDLCall(int returned, std::string failure) {
|
||||
if (returned < 0)
|
||||
throwSDLException(failure);
|
||||
}
|
||||
};
|
||||
}
|
@ -3,13 +3,9 @@
|
||||
|
||||
namespace Gaming {
|
||||
|
||||
Game::Game() {
|
||||
verifySDLCall(::SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_GAMECONTROLLER | SDL_INIT_HAPTIC), "Failed to initialise SDL: ");
|
||||
}
|
||||
Game::Game() {}
|
||||
|
||||
Game::~Game() {
|
||||
::SDL_Quit();
|
||||
}
|
||||
Game::~Game() {}
|
||||
|
||||
void Game::raisePOWER() {
|
||||
|
||||
@ -21,29 +17,29 @@ void Game::raisePOWER() {
|
||||
windowWidth(), windowHeight(),
|
||||
SDL_WINDOW_SHOWN), ::SDL_DestroyWindow);
|
||||
if (m_window == nullptr)
|
||||
throwSDLException("Unable to create window: ");
|
||||
SDLWrapper::throwSDLException("Unable to create window: ");
|
||||
|
||||
::SDL_DisplayMode mode;
|
||||
verifySDLCall(::SDL_GetWindowDisplayMode(m_window.get(), &mode), "Unable to obtain window information");
|
||||
SDLWrapper::verifySDLCall(::SDL_GetWindowDisplayMode(m_window.get(), &mode), "Unable to obtain window information");
|
||||
|
||||
Uint32 rendererFlags = 0;
|
||||
m_vsync = useVsync();
|
||||
if (m_vsync) {
|
||||
const auto required = fps();
|
||||
if (required == mode.refresh_rate) {
|
||||
if (std::abs(required - mode.refresh_rate) < 0.001) {
|
||||
rendererFlags |= SDL_RENDERER_PRESENTVSYNC;
|
||||
::SDL_Log("Attempting to use SDL_RENDERER_PRESENTVSYNC");
|
||||
} else {
|
||||
m_vsync = false;
|
||||
::SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Display refresh rate is incompatible with required rate (%d)", required);
|
||||
::SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Display refresh rate is incompatible with required rate (%f)", required);
|
||||
}
|
||||
}
|
||||
m_renderer.reset(::SDL_CreateRenderer(m_window.get(), -1, rendererFlags), ::SDL_DestroyRenderer);
|
||||
if (m_renderer == nullptr)
|
||||
throwSDLException("Unable to create renderer: ");
|
||||
SDLWrapper::throwSDLException("Unable to create renderer: ");
|
||||
|
||||
::SDL_RendererInfo info;
|
||||
verifySDLCall(::SDL_GetRendererInfo(m_renderer.get(), &info), "Unable to obtain renderer information");
|
||||
SDLWrapper::verifySDLCall(::SDL_GetRendererInfo(m_renderer.get(), &info), "Unable to obtain renderer information");
|
||||
|
||||
if (m_vsync) {
|
||||
if ((info.flags & SDL_RENDERER_PRESENTVSYNC) == 0) {
|
||||
@ -54,20 +50,20 @@ void Game::raisePOWER() {
|
||||
|
||||
m_pixelFormat.reset(::SDL_AllocFormat(m_pixelType), ::SDL_FreeFormat);
|
||||
if (m_pixelFormat == nullptr)
|
||||
throwSDLException("Unable to allocate pixel format: ");
|
||||
SDLWrapper::throwSDLException("Unable to allocate pixel format: ");
|
||||
|
||||
configureBackground();
|
||||
createBitmapTexture();
|
||||
}
|
||||
|
||||
void Game::configureBackground() const {
|
||||
verifySDLCall(::SDL_SetRenderDrawColor(m_renderer.get(), 0x00, 0x00, 0x00, SDL_ALPHA_OPAQUE), "Unable to set render draw colour");
|
||||
SDLWrapper::verifySDLCall(::SDL_SetRenderDrawColor(m_renderer.get(), 0x00, 0x00, 0x00, SDL_ALPHA_OPAQUE), "Unable to set render draw colour");
|
||||
}
|
||||
|
||||
void Game::createBitmapTexture() {
|
||||
m_bitmapTexture.reset(::SDL_CreateTexture(m_renderer.get(), m_pixelType, SDL_TEXTUREACCESS_STREAMING, rasterWidth(), rasterHeight()), ::SDL_DestroyTexture);
|
||||
if (m_bitmapTexture == nullptr)
|
||||
throwSDLException("Unable to create bitmap texture");
|
||||
SDLWrapper::throwSDLException("Unable to create bitmap texture");
|
||||
}
|
||||
|
||||
void Game::runLoop() {
|
||||
@ -120,7 +116,7 @@ void Game::runLoop() {
|
||||
|
||||
if (!m_vsync) {
|
||||
const auto elapsedTicks = ::SDL_GetTicks() - m_startTicks;
|
||||
const auto neededTicks = (m_frames / (float)fps()) * 1000.0;
|
||||
const auto neededTicks = (m_frames / fps()) * 1000.0;
|
||||
const auto sleepNeeded = (int)(neededTicks - elapsedTicks);
|
||||
if (sleepNeeded > 0) {
|
||||
::SDL_Delay(sleepNeeded);
|
||||
@ -173,11 +169,11 @@ std::shared_ptr<GameController> Game::chooseController(const int who) const {
|
||||
}
|
||||
|
||||
void Game::updateTexture() {
|
||||
verifySDLCall(::SDL_UpdateTexture(m_bitmapTexture.get(), nullptr, pixels(), displayWidth() * sizeof(Uint32)), "Unable to update texture: ");
|
||||
SDLWrapper::verifySDLCall(::SDL_UpdateTexture(m_bitmapTexture.get(), nullptr, pixels(), displayWidth() * sizeof(Uint32)), "Unable to update texture: ");
|
||||
}
|
||||
|
||||
void Game::copyTexture() {
|
||||
verifySDLCall(
|
||||
SDLWrapper::verifySDLCall(
|
||||
::SDL_RenderCopy(m_renderer.get(), m_bitmapTexture.get(), nullptr, nullptr),
|
||||
"Unable to copy texture to renderer");
|
||||
}
|
||||
@ -186,4 +182,48 @@ void Game::displayTexture() {
|
||||
::SDL_RenderPresent(m_renderer.get());
|
||||
}
|
||||
|
||||
void Game::toggleFullscreen() {
|
||||
auto wasFullscreen = ::SDL_GetWindowFlags(m_window.get()) & SDL_WINDOW_FULLSCREEN_DESKTOP;
|
||||
SDLWrapper::verifySDLCall(::SDL_SetWindowFullscreen(m_window.get(), wasFullscreen ? 0 : SDL_WINDOW_FULLSCREEN_DESKTOP), "Failed to modify the window full screen setting");
|
||||
::SDL_ShowCursor(wasFullscreen ? 1 : 0);
|
||||
}
|
||||
|
||||
bool Game::handleKeyDown(SDL_Keycode key) {
|
||||
switch (key) {
|
||||
case SDLK_F12:
|
||||
// Don't let it get poked.
|
||||
return true;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool Game::handleKeyUp(SDL_Keycode key) {
|
||||
switch (key) {
|
||||
case SDLK_F12:
|
||||
toggleFullscreen();
|
||||
return true;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool Game::handleJoyButtonDown(SDL_JoyButtonEvent event) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Game::handleJoyButtonUp(SDL_JoyButtonEvent event) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Game::handleControllerButtonDown(SDL_ControllerButtonEvent event) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Game::handleControllerButtonUp(SDL_ControllerButtonEvent event) {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
@ -5,7 +5,7 @@
|
||||
namespace Gaming {
|
||||
|
||||
GameController::GameController(int index)
|
||||
: m_index(index) {
|
||||
: m_index(index) {
|
||||
open();
|
||||
}
|
||||
|
||||
@ -16,58 +16,46 @@ namespace Gaming {
|
||||
void GameController::open() {
|
||||
SDL_assert(::SDL_NumJoysticks() > 0);
|
||||
if (::SDL_IsGameController(m_index)) {
|
||||
m_gameController = ::SDL_GameControllerOpen(m_index);
|
||||
if (m_gameController == nullptr) {
|
||||
Game::throwSDLException("Unable to open game controller: ");
|
||||
}
|
||||
m_gameController.reset(::SDL_GameControllerOpen(m_index), ::SDL_GameControllerClose);
|
||||
if (m_gameController == nullptr)
|
||||
SDLWrapper::throwSDLException("Unable to open game controller: ");
|
||||
openHapticController();
|
||||
auto name = ::SDL_GameControllerName(m_gameController);
|
||||
auto name = ::SDL_GameControllerName(m_gameController.get());
|
||||
::SDL_Log("Game controller name: %s", name);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
::SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Joystick is not a game controller!!");
|
||||
}
|
||||
}
|
||||
|
||||
void GameController::openHapticController() {
|
||||
m_hapticController = ::SDL_HapticOpen(m_index);
|
||||
if (m_hapticController == nullptr) {
|
||||
Game::throwSDLException("Unable to open haptic controller: ");
|
||||
}
|
||||
Game::verifySDLCall(::SDL_HapticRumbleInit(m_hapticController), "Unable to initialise haptic controller: ");
|
||||
m_hapticRumbleSupported = ::SDL_HapticRumbleSupported(m_hapticController) != SDL_FALSE;
|
||||
m_hapticController.reset(::SDL_HapticOpen(m_index), ::SDL_HapticClose);
|
||||
if (m_hapticController == nullptr)
|
||||
SDLWrapper::throwSDLException("Unable to open haptic controller: ");
|
||||
SDLWrapper::verifySDLCall(::SDL_HapticRumbleInit(m_hapticController.get()), "Unable to initialise haptic controller: ");
|
||||
m_hapticRumbleSupported = ::SDL_HapticRumbleSupported(m_hapticController.get()) != SDL_FALSE;
|
||||
}
|
||||
|
||||
void GameController::closeHapticController() noexcept {
|
||||
if (m_hapticController != nullptr) {
|
||||
::SDL_HapticClose(m_hapticController);
|
||||
m_hapticController = nullptr;
|
||||
}
|
||||
m_hapticController.reset();
|
||||
m_hapticRumbleSupported = false;
|
||||
}
|
||||
|
||||
void GameController::close() noexcept {
|
||||
if (m_gameController != nullptr) {
|
||||
::SDL_GameControllerClose(m_gameController);
|
||||
m_gameController = nullptr;
|
||||
}
|
||||
m_gameController.reset();
|
||||
closeHapticController();
|
||||
}
|
||||
|
||||
void GameController::startRumble() noexcept {
|
||||
if (m_hapticRumbleSupported) {
|
||||
if (::SDL_HapticRumblePlay(m_hapticController, 1.0, 1000) < 0) {
|
||||
if (::SDL_HapticRumblePlay(m_hapticController.get(), 1.0, 1000) < 0)
|
||||
::SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Unable to start haptic rumble: %s", ::SDL_GetError());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GameController::stopRumble() noexcept {
|
||||
if (m_hapticRumbleSupported) {
|
||||
if (::SDL_HapticRumbleStop(m_hapticController) < 0) {
|
||||
if (::SDL_HapticRumbleStop(m_hapticController.get()) < 0)
|
||||
::SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Unable to stop haptic rumble: %s", ::SDL_GetError());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -166,10 +166,12 @@
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="SDLWrapper.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\inc\Game.h" />
|
||||
<ClInclude Include="..\inc\GameController.h" />
|
||||
<ClInclude Include="..\inc\SDLWrapper.h" />
|
||||
<ClInclude Include="pch.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
|
@ -20,6 +20,9 @@
|
||||
<ClCompile Include="pch.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="SDLWrapper.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\inc\Game.h">
|
||||
@ -31,5 +34,8 @@
|
||||
<ClInclude Include="pch.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\inc\SDLWrapper.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
12
Gaming/src/SDLWrapper.cpp
Normal file
12
Gaming/src/SDLWrapper.cpp
Normal file
@ -0,0 +1,12 @@
|
||||
#include "pch.h"
|
||||
#include "SDLWrapper.h"
|
||||
|
||||
using namespace Gaming;
|
||||
|
||||
SDLWrapper::SDLWrapper() {
|
||||
verifySDLCall(::SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_GAMECONTROLLER | SDL_INIT_HAPTIC), "Failed to initialise SDL: ");
|
||||
}
|
||||
|
||||
SDLWrapper::~SDLWrapper() {
|
||||
::SDL_Quit();
|
||||
}
|
Loading…
Reference in New Issue
Block a user