We're going to jump straight in and make a Pong clone in SDL2 and C++. I've already created three files: main.cpp, pong.hpp, and pong.cpp. Main.cpp only has one job: create an instance of a Pong
class and call the method game_loop()
.
main.cpp:
#include "pong.hpp"
int main()
{
Pong pong;
pong.game_loop();
}
The Pong
class that resides in pong.cpp handles our window, renderer, window events and eventually more as we progress through this article.
pong.hpp:
#pragma once
#include <SDL2/SDL.h>
class Pong
{
public:
Pong();
~Pong() = default;
void game_loop();
void update(double delta_time);
void draw();
private:
SDL_Window *m_game_window;
SDL_Event m_game_window_event;
SDL_Renderer *m_game_window_renderer;
};
pong.cpp:
#include "pong.hpp"
Pong::Pong()
{
SDL_CreateWindowAndRenderer(680, 480, SDL_WINDOW_RESIZABLE,
&m_game_window, &m_game_window_renderer);
}
void Pong::game_loop()
{
bool keep_running = true;
while(keep_running)
{
while(SDL_PollEvent(&m_game_window_event) > 0)
{
switch(m_game_window_event.type)
{
case SDL_QUIT:
keep_running = false;
}
}
update(1.0/60.0);
draw();
}
}
void Pong::update(double delta_time)
{
}
void Pong::draw()
{
SDL_RenderClear(m_game_window_renderer);
SDL_RenderPresent(m_game_window_renderer);
}
In the constructor, we initialize both the window and renderer using SDL_CreateWindowAndRenderer()
. It's a shortcut to creating a window and then creating the renderer from the window. We also allow resizing of the window in case the user is not satisfied with the default dimensions. This has an implication that we must talk about. If we allow resizing of the window, this means our objects in the game will change dimension/shape. We need an independent resolution from the window dimensions. With SDL2 being a fairly low level library, this is easy to accomplish. In the constructor, add the function SDL_RenderSetLogicalSize()
that takes a pointer to an SDL_Renderer
and two arguments for the width and height.
SDL_RenderSetLogicalSize(m_game_window_renderer, 400, 400);
The next order of business is to create the paddles. Like always, we'll create a class to manage the position, updates and drawing to the screen.
paddle.hpp:
#pragma once
#include <SDL2/SDL.h>
#include "paddle.hpp"
class Pong
{
public:
Pong();
~Pong() = default;
void game_loop();
void update(double delta_time);
void draw();
private:
SDL_Window *m_game_window;
SDL_Event m_game_window_event;
SDL_Renderer *m_game_window_renderer;
Paddle m_left_paddle;
Paddle m_right_paddle;
};
paddle.cpp:
#include "paddle.hpp"
Paddle::Paddle(int x, int y)
{
m_position.x = x;
m_position.y = y;
m_position.w = 50;
m_position.h = 100;
}
void Paddle::handle_input(SDL_Event const &event)
{
}
void Paddle::update(double delta_time)
{
}
void Paddle::draw()
{
}
We also add the Paddle
classes to the private section of the Pong
class and initialize them in the constructor initialization list:
pong.hpp:
#include "paddle.hpp"
...
Paddle m_left_paddle;
Paddle m_right_paddle;
pong.cpp:
Pong::Pong(): m_left_paddle(0, (400 / 2) - 50), m_right_paddle(400 - 50, (400 / 2) - 50)
{
...
}
In the game loop of Pong
, any events that are not handled by Pong
itself should be sent to the other objects in the game like the paddles.
while(SDL_PollEvent(&m_game_window_event) > 0)
{
switch(m_game_window_event.type)
{
...
}
m_left_paddle.handle_input(m_game_window_event);
m_right_paddle.handle_input(m_game_window_event);
}
What's next?
In the next part, we'll get the left and right paddles moving up and down the screen.
Like always, the source code for this series can be found at my Github repository:
Top comments (1)
Hey, nice tutorial, thanks for sharing it.
The content of the paddle.hpp file here is wrong, I just thought I'd suggest an edit.
Cheers^^