What makes up a game
Outside the design and gameplay of a game, the underlying mechanics are essentially the interaction of various subsystems such as graphics, game logic, and user input. The graphics subsystem should not know how the game logic is implemented or vice versa. We can think of the structure of a game as follows:
Once the game is initialized, it then goes into a loop of checking for user input, updating any values based on the game physics, before rendering to the screen. Once the user chooses to exit, the loop is broken and the game moves onto cleaning everything up and exiting. This is the basic scaffold for a game and it is what will be used in this book.
We will be building a reusable framework that will take all of the legwork out of creating a game in SDL 2.0. When it comes to boilerplate code and setup code, we really only want to write it once and then reuse it within new projects. The same can be done with drawing code, event handling, map loading, game states, and anything else that all games may require. We will start by breaking up the Hello SDL 2.0 example into separate parts. This will help us to start thinking about how code can be broken into reusable standalone chunks rather than packing everything into one large file.
Breaking up the Hello SDL code
We can break up the Hello SDL into separate functions:
bool g_bRunning = false; // this will create a loop
Follow these steps to break the Hello SDL
code:
Create an
init
function after the two global variables that takes any necessary values as parameters and passes them to theSDL_CreateWindow
function:bool init(const char* title, int xpos, int ypos, int height, int width, int flags) { // initialize SDL if(SDL_Init(SDL_INIT_EVERYTHING) >= 0) { // if succeeded create our window g_pWindow = SDL_CreateWindow(title, xpos, ypos, height, width, flags); // if the window creation succeeded create our renderer if(g_pWindow != 0) { g_pRenderer = SDL_CreateRenderer(g_pWindow, -1, 0); } } else { return false; // sdl could not initialize } return true; } void render() { // set to black SDL_SetRenderDrawColor(g_pRenderer, 0, 0, 0, 255); // clear the window to black SDL_RenderClear(g_pRenderer); // show the window SDL_RenderPresent(g_pRenderer); }
Our main function can now use these functions to initialize SDL:
int main(int argc, char* argv[]) { if(init("Chapter 1: Setting up SDL", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 640, 480, SDL_WINDOW_SHOWN)) { g_bRunning = true; } else { return 1; // something's wrong } while(g_bRunning) { render(); } // clean up SDL SDL_Quit(); return 0; }
As you can see, we have broken the code up into separate parts: one function does the initialization for us and the other does the rendering code. We've added a way to keep the program running in the form of a while
loop that runs continuously, rendering our window.
Let's take it a step further and try to identify which separate parts a full game might have and how our main loop might look. Referring to the first screenshot, we can see that the functions we will need are initialize
, get input
, do physics
, render
, and exit
. We will generalize these functions slightly and rename them to init()
, handleEvents()
, update()
, render()
, and clean()
. Let's put these functions into main.cpp
:
void init(){} void render(){} void update(){} void handleEvents(){} void clean(){} bool g_bRunning = true; int main() { init(); while(g_bRunning) { handleEvents(); update(); render(); } clean(); }
What does this code do?
This code does not do much at the moment, but it shows the bare bones of a game and how a main loop might be broken apart. We declare some functions that can be used to run our game: first, the init()
function, which will initialize SDL and create our window, and second, we declare the core loop functions of render
, update
, and handle events
. We also declare a clean
function, which will clean up code at the end of our game. We want this loop to continue running so we have a Boolean value that is set to true
, so that we can continuously call our core loop functions.