Setting Up the Game and Drawing
README.MD File
Your starting repo has a README.md file which contains placeholders for your name, email address, section, and platform. Edit this file to contain your information, and push this change to Github.
Code Setup
The starting code has a Main.cpp with a main function that does nothing and empty Game.h/Game.cpp files.
Create a new Game
class in the Game.h/Game.cpp files. Create the following public functions (they will be empty or stubs for now):
- A constructor
Initialize
– No parameters and returnsbool
Shutdown
– No parameters and returnsvoid
RunLoop
– No parameters and returnsvoid
We use separate Initialize
/Shutdown
functions rather than a constructor and destructor to have more control over the initialization and shutdown order of game systems.
Game::Initialize
This function initializes the game. It returns true
if the game successfully initializes and false
otherwise.
You will be using a lot of SDL functions in this lab, so include "SDL2/SDL.h"
in Game.h.
For SDL functions, we will point you to examples in the documentation. You should look at the example, and adapt it as needed for our usage (this means you won’t necessarily directly copy/paste code from the documentation).
- Initialize SDL using
SDL_Init
: https://wiki.libsdl.org/SDL_Init. We need the audio and video subsystems. - Create a window using
SDL_CreateWindow
: https://wiki.libsdl.org/SDL_CreateWindow. Make the window 1024 pixels wide and 768 pixels high. This function returns aSDL_Window*
. Save this in a member variable inGame
. - Create a renderer using
SDL_CreateRenderer
: https://wiki.libsdl.org/SDL_CreateRenderer. We want to use the accelerated renderer and turn on VSYNC. To do this, pass inSDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC
as the third parameter to the function. This function returns anSDL_Renderer*
. Save this in a member variable inGame
.
If the window is too big to fit on your screen, what you can do is instead pass in half the width/height to SDL_CreateWindow
(so 512x384) but then use SDL_RenderSetLogicalSize and set the logical size to the normal 1024x768. That way your window will be half the size but the coordinates of everything will still be the full size.
Game::RunLoop
This function runs the game loop.
This is where you will implement the “game loop” as discussed in lecture. Recall that each iteration of the game loop has three phases: process inputs, update game, generate outputs. Make member functions for each of these three phases (ProcessInput
, UpdateGame
, GenerateOutput
) – make them empty for now.
Now implement an infinite loop in RunLoop
, calling the functions you just created for the three phases (in the correct order).
In SDL, you may receive events that the game must respond to. So, in your ProcessInput
function, use SDL_PollEvent
to poll for available events: https://wiki.libsdl.org/SDL_PollEvent. Note that there could be more than one event per frame, which is why the example uses while (SDL_PollEvent…)
. You should mimic this in your ProcessInput
function. For now, we won’t do anything with the events we get from SDL_PollEvent
(this will change soon).
We will add code to UpdateGame
and GenerateOutput
in a bit.
Game::Shutdown
This function shuts down SDL systems (and anything else needed).
This function simply needs to:
- Destroy the renderer – https://wiki.libsdl.org/SDL_DestroyRenderer
- Destroy the window – https://wiki.libsdl.org/SDL_DestroyWindow
- Quit SDL – https://wiki.libsdl.org/SDL_Quit
To test your Game
class, create an instance of Game
in Main.cpp. Then call Initialize
. If Initialize
returns true
, call RunLoop
(you don’t want to run if you failed to initialize). Then call Shutdown (you should call Shutdown regardless if initialize succeeds or fails).
When you run your game, you should see an empty window of the correct size. The inside of the window will be black, white, or grey.
There is currently no way to quit the game, other than stopping it in the debugger.
SDL_QUIT
Let’s add support for quitting. There are multiple ways to quit (close the window, use the quit shortcut key, or press ESC). Change your game loop so that the condition is a bool member variable. This allows you to stop the loop by setting the bool to false
.
Next, update the loop in ProcessInput
to check if any of the events are of type SDL_QUIT
.
The SDL_Event
documentation describes how to test the type of an SDL event: https://wiki.libsdl.org/SDL_Event#reading.
In our case, instead of SDL_MOUSEMOTION
, we are testing for an SDL_QUIT
event: https://wiki.libsdl.org/SDL_EventType#SDL_QUIT. If you detect an event of type SDL_QUIT
, set your game loop bool to false
(so the loop exits).
Now when you run the game, it should quit if you click the “X” button on the window.
Simple Keyboard Input
While we could also use events for keys, it’s often easier to grab the state of the entire keyboard and test the status of individual keys.
So, after the entire SDL_PollEvent
loop in ProcessInput
, call SDL_GetKeyboardState
: https://wiki.libsdl.org/SDL_GetKeyboardState. Then, check the state for whether the ESC key is pressed. If so, set your game loop bool to false. (This list of scan codes is here: https://wiki.libsdl.org/SDL_Scancode).
Now when you run the game, it should also quit if you press the ESC key.
Drawing
SDL rendering uses a back buffer (which we’ll cover later in the semester). For now, just know that every frame, the game code must clear the back buffer, draw the scene, and “present” the back buffer.
In GenerateOutput
, do the following:
- Set the render draw color to blue
(0, 0, 255, 255)
usingSDL_SetRenderDrawColor
: https://wiki.libsdl.org/SDL_SetRenderDrawColor - Clear the backbuffer using
SDL_RenderClear
: https://wiki.libsdl.org/SDL_RenderClear - Draw your game objects (which we will do in a second)
- Present using
SDL_RenderPresent
: https://wiki.libsdl.org/SDL_RenderPresent
Now your window should be filled in blue:
Walls
Now let’s draw game objects. Remember that all game objects should be drawn after the SDL_RenderClear
call but before the SDL_RenderPresent
call.
First, add three white walls on the top, right, and bottom of the screen. To do this, set the color to white (all 255s) using the same SDL_SetRenderDrawColor
function. Then draw the walls as rectangles using SDL_RenderFillRect
: https://wiki.libsdl.org/SDL_RenderFillRect.
Remember that in the SDL coordinate system, (0, 0) is the top left corner of the window.
Hint: Make a constant for the thickness of the walls, so that it’s easy to adjust and avoids using magic numbers throughout your code.
You should now have walls that look something like this:
Paddle
On the left part of the screen, add a paddle (this is what the player will use to hit the ball). Instead of hard coding the position of the paddle like the wall, create a SDL_Point
member variable for the paddle’s position. (Of course, you need to initialize the variable).
When you draw the paddle, draw it centered around its position. This will make it easier to draw the paddle in the correct spot once you start updating the position later in this lab. Hint: Make a constant for the height of the paddle.
You should now have a paddle on the screen (you can use whichever color you want, I stuck with white):
Ball
Since SDL doesn’t have a draw circle function, we’ll just use a square for the ball (this is how it’s implemented in the original Pong). As with the paddle, create an SDL_Point
member variable for the ball position. Initialize the position of the ball to the center of the window.
You should now have a ball (though it doesn’t move):
This is the end of part 1.
Commit and push your code. Verify that you can view your code on the GitHub website!
Once you’ve pushed this code, you’re ready to move on to part 2.