DEV Community


Posted on • Updated on

Using SDL2: Pong 3

For this post, we'll get the ball on the screen. First thing I did was to create a ball class called, well, Ball. It follows the same structure as the previous classes: an update and draw methods. One method that makes it stand out from the rest is the init() method. This initializes the object and places it at the position we choose.

Why not use the constructor?

In the Pong class, I added yet another object called m_ball. Because SDL2 doesn't provide the feature to draw circles, I went with an image and decided to use SDL2_image again.

Using the constructor initializer list is impossible because the renderer doesn't get created until later, after when m_ball is already default initialized.


#pragma once

#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>

#include <random>

class Ball
    Ball() = default;
    void init(SDL_Renderer *renderer, int x, int y);
    void update(double delta_time);
    void draw(SDL_Renderer *renderer);
    int m_velocity;
    double m_x;
    SDL_Texture *m_image;
    SDL_Rect m_position;
Enter fullscreen mode Exit fullscreen mode


void Ball::init(SDL_Renderer *renderer, int x, int y)

    SDL_Surface *image = IMG_Load("ball.png");

    m_image = SDL_CreateTextureFromSurface(renderer, image);


    m_position.x = x;
    m_position.y = y;

    m_x = m_position.x;

    SDL_QueryTexture(m_image, nullptr, nullptr, &m_position.w, &m_position.h);

    std::random_device dev;
    std::mt19937 rand_gen(dev());
    std::uniform_int_distribution<> dist(0, 1);

    if(dist(rand_gen) == 0)
        m_velocity = -3;
    } else {
        m_velocity = 3;
Enter fullscreen mode Exit fullscreen mode

Quite a bit is happening in this function. First, we initialize SDL2_image's PNG loader, open an image and save it to an SDL_Surface. Next, we convert it into an SDL_Texture and free the SDL_Surface. Lastly, we quit SDL2_image.

We'll introduce a new function called SDL_QueryTexture(). It takes a pointer to an SDL_Texture for the attributes we want to query. The rest are output parameters for the data we would like. The first one is a pointer to an Unit32. We can see an SDL_Texture's format by using this output parameter. For the next one, we'll wave our hands over and concentrate on the last two. The output parameters for the width and height.

To make this game a little more exciting, we'll randomize the direction the ball goes. If it is 0 then it will move to the left. When a value of one, it goes to the right.

We use some shiny C++ 11 features to get uniform distributions. This is something std::rand() cannot give us. I won't go into much detail but the gist of what I'm doing is creating a random device and then a Mersenne Twister engine. Finally, create a range from 0 to 1 using std::uniform_int_distribution.

Depending on the value, we set the m_velocity variable to the correct value.


void Ball::update(double delta_time)
    m_x = m_x + (m_velocity * delta_time);
    m_position.x = m_x;
Enter fullscreen mode Exit fullscreen mode

Nothing new.


void Ball::draw(SDL_Renderer *renderer)
    SDL_RenderCopy(renderer, m_image, nullptr, &m_position);
Enter fullscreen mode Exit fullscreen mode

Nothing new here also.

Just like every other class before it, we call the update() method in Pong's update method and the draw() method in Pong's draw method.

void Pong::update(double delta_time)
Enter fullscreen mode Exit fullscreen mode
void Pong::draw()
Enter fullscreen mode Exit fullscreen mode

Compile and run.

What's next

Currently, the ball only travels in a straight horizontal line. In the next post, this will be fixed.

Github repository:

Top comments (1)

deciduously profile image
Ben Lovy • Edited

These have been a fun read - you might consider using a series tag to link them together!