This is a basic game loop written in C++ using SDL. All feedback is appreciated especially feedback related to design and extensibility.
Main.cpp:
#include "Game.h"
int main(int argc, char* argv[])
{
Game game;
game.Start();
return 0;
}
Game.h:
#pragma once
#include <SDL.h>
class Game
{
private:
SDL_Window* m_Window;
SDL_Renderer* m_Renderer;
bool m_Running;
float m_DeltaTime;
public:
void Start();
void Stop();
private:
void GameLoop();
void HandleEvents();
void Update(float deltaTime);
void Render();
};
Timer.h:
#pragma once
class Timer
{
private:
int m_StartTime;
int m_PausedTime;
bool m_Started;
bool m_Paused;
public:
Timer()
: m_StartTime(0), m_PausedTime(0), m_Started(false), m_Paused(false) {}
void Start();
void Stop();
void Pause();
void Unpause();
int GetTicks();
inline bool IsStarted() const { return m_Started; }
inline bool IsPaused() const { return m_Paused; }
};
Game.cpp:
#include "Game.h"
#include <iostream>
#include <SDL_image.h>
#include <SDL_ttf.h>
#include "Timer.h"
void Game::Start()
{
m_Running = true;
if (SDL_Init(SDL_INIT_EVERYTHING) != 0)
{
std::cout << "ERROR: Failed to initialise SDL!" << std::endl;
Stop();
}
if (IMG_Init(IMG_INIT_PNG) < 0)
{
std::cout << "ERROR: Failed to initialise SDL_image!" << std::endl;
Stop();
}
if (TTF_Init() == -1)
{
std::cout << "ERROR: Failed to initialise SDL_ttf!" << std::endl;
Stop();
}
m_Window = SDL_CreateWindow("RPG", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 800, 600, 0);
if (!m_Window)
{
std::cout << "ERROR: Failed to create SDL_Window!" << std::endl;
Stop();
}
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
m_Renderer = SDL_CreateRenderer(m_Window, -1, SDL_RENDERER_PRESENTVSYNC);
if (!m_Renderer)
{
std::cout << "ERROR: Failed to create SDL_Renderer!" << std::endl;
Stop();
}
SDL_SetRenderDrawColor(m_Renderer, 0, 0, 0, 255);
GameLoop();
}
void Game::Stop()
{
SDL_DestroyWindow(m_Window);
SDL_DestroyRenderer(m_Renderer);
m_Window = nullptr;
m_Renderer = nullptr;
IMG_Quit();
SDL_Quit();
m_Running = false;
}
void Game::GameLoop()
{
Timer deltaTimer;
deltaTimer.Start();
while (m_Running)
{
HandleEvents();
Update(m_DeltaTime);
m_DeltaTime = static_cast<float>(deltaTimer.GetTicks()) / 1000.0f;
deltaTimer.Start();
Render();
}
Stop();
}
void Game::HandleEvents()
{
SDL_Event event;
SDL_PollEvent(&event);
switch (event.type)
{
case SDL_QUIT:
m_Running = false;
break;
}
}
void Game::Update(float deltaTime)
{
}
void Game::Render()
{
SDL_RenderClear(m_Renderer);
static TTF_Font* font = TTF_OpenFont("assets/fonts/caliban.ttf", 12);
static SDL_Color colour = { 255, 255, 255, 255 };
SDL_Surface* textSurface = TTF_RenderText_Blended(font, std::string("FPS: " + std::to_string(1.0f / m_DeltaTime)).c_str(), colour);
SDL_Texture* textTexture = SDL_CreateTextureFromSurface(m_Renderer, textSurface);
SDL_Rect textBounds = { 5, 5, textSurface->w, textSurface->h };
SDL_RenderCopy(m_Renderer, textTexture, nullptr, &textBounds);
SDL_FreeSurface(textSurface);
SDL_DestroyTexture(textTexture);
SDL_RenderPresent(m_Renderer);
}
Timer.cpp:
#include "Timer.h"
#include <SDL.h>
void Timer::Start()
{
m_Started = true;
m_Paused = false;
m_StartTime = SDL_GetTicks();
m_PausedTime = 0;
}
void Timer::Stop()
{
m_Started = false;
m_Paused = false;
m_StartTime = 0;
m_PausedTime = 0;
}
void Timer::Pause()
{
if (m_Started && !m_Paused)
{
m_Paused = true;
m_PausedTime = SDL_GetTicks() - m_StartTime;
m_StartTime = 0;
}
}
void Timer::Unpause()
{
if (m_Started && m_Paused)
{
m_Paused = false;
m_StartTime = SDL_GetTicks() - m_PausedTime;
m_PausedTime = 0;
}
}
int Timer::GetTicks()
{
if (m_Started)
return m_Paused ? m_PausedTime : SDL_GetTicks() - m_StartTime;
return 0;
}