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.
ball.hpp:
#pragma once
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include <random>
class Ball
{
public:
Ball() = default;
~Ball();
void init(SDL_Renderer *renderer, int x, int y);
void update(double delta_time);
void draw(SDL_Renderer *renderer);
private:
int m_velocity;
double m_x;
SDL_Texture *m_image;
SDL_Rect m_position;
};
Ball::init():
void Ball::init(SDL_Renderer *renderer, int x, int y)
{
IMG_Init(IMG_INIT_PNG);
SDL_Surface *image = IMG_Load("ball.png");
m_image = SDL_CreateTextureFromSurface(renderer, image);
SDL_FreeSurface(image);
IMG_Quit();
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;
}
}
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.
Ball::update():
void Ball::update(double delta_time)
{
m_x = m_x + (m_velocity * delta_time);
m_position.x = m_x;
}
Nothing new.
Ball::draw():
void Ball::draw(SDL_Renderer *renderer)
{
SDL_RenderCopy(renderer, m_image, nullptr, &m_position);
}
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)
{
...
m_ball.update(delta_time);
}
void Pong::draw()
{
m_ball.draw(m_game_window_renderer);
}
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: https://github.com/Noah11012/sdl2-tutorial-code
Top comments (1)
These have been a fun read - you might consider using a series tag to link them together!