Advertisement

sdl pong/breakout

Started by March 28, 2024 12:09 AM
7 comments, last by AliAbdulKareem 7 months, 3 weeks ago

I have made some progress on my pong and breakout game thanks to joej I have got the paddles to move. My question is how do I get a ball sprite to move around the screen. I have already got the ball to be drawn on the screen.

Let me know if you need my code.

Usually you just draw the ball, let's say and the center of the screen. With a random velocity direction let's say like: (0.7 in X direction, 0.3 in Y direction).
and you keep moving in that direction till you either:
a) hit a wall, depending on which side you have hit, you flip one of the velocity directions, either (X,Y), if you hit a corner, you flip both direction.
b) hit a player, in this case, there are two cases:
first, if you hit the player rectangle from the front, (the long side) then you flip only the X direction.
second, if you hit the player from either top or below (the short side) then you flip both X and Y.

That should be a good starting point.

one thing I should mention, is that you probably want to normalize your random speed such that it is always the same in magnitude.

None

Advertisement

To avoid spaghetti code, i propose to use a struct for the ball. You have already used such structs - the rect data structure for the sdl images. You should do the same now for your ball, writing your own struct:

struct Ball
{
	float posx;
	float posy;
	float velx;
	float vely;
	
	void Integrate (float timestep)
	{
		posx += velx * timestep;
		posy += vely * tiemstep;
	}
};

Ball ball; // make one instance as a global variable for now

void main ()
{
	// initialize the values to some initial state  
	ball.posx = 200.0f;
	ball.posy = 100.0f;
	ball.velx = 0.7f;
	ball.vely = 0.3f;
	
	// load images, make window and all thet stuff...
	
	while (true) // game loop
	{
		// move the ball, the given timestep and the velocity values define speed
		ball.Integrate (1.f / 60.f); 
		
		// to render the ball, probably fill some SDL rect data structure with the ball coordinates
		
		float size = 10; 
		image_rect.left = ball.posx - size * 0.5f;
		image_rect.width = image_rect.left + size;
		// same for right and height, 
		// then draw the rect to show the ball
	}
}

AliAbdulKareem said:
one thing I should mention, is that you probably want to normalize your random speed such that it is always the same in magnitude.

That's not needed. Since we'll only reflect velocity, it's magnitude won't change.

We could do it anyway, but this would be the time to introduce a liner math library such as glm to have a vec2.

Vectors would also help with collision detection and response.
So after the ball is moving like it should, i would consider to add such library, or keep going with separate x and y values for everything, requiring twice the code but postponing to learn about basic linear math.

Thinking of it, it's probably too early to consider using a math library.

But i insist on using the struct for the ball.
The reason is: Once you are used to data structures, managing many objects (bricks in breakout, or enemies in space invaders) becomes much easier. And that's your primary programming problem.
So even if Pong is simple enough to go without data structures, you really should take the opportunity to learn first steps here.

But the math stuff is not frightening either.
Personally i lack vec2 in my math lib, so i've written one myself and it's really not much code:

	struct Vec2
	{
		float x, y;

		Vec2 () {};

		Vec2 (float x, float y) : x(x), y(y) {};


		Point2D operator + (const Vec2 &p) const 
		{
			return Vec2 (x + p.x, y + p.y);
		}
		
		Point2D operator - (const Vec2 &p) const
		{
			return Vec2 (x - p.x, y - p.y);
		}
		
		Vec2 operator * (const float v) const
		{
			return Vec2 (x * v, y * v);
		}
		
		float Dot (const Vec2 &p) const
		{
			return x * p.x + y * p.y;
		}
		
		float Perp (const Vec2 &p) const // dot product with perpendicular p
		{
			return x * p.y - y * p.x;
		}
		
		Vec2 Perp () const
		{
			return Vec2 (-y, x);
		}

		float& operator [] (int i) // get x or y using given index of 0 or 1
		{
			return *(&x + i);
		}

		inline float operator [] (int i) const
		{
			return *(&x + i);
		}

	};

If you would include this as a header file (or put it above the Ball struct), you could rewrite the Ball like this:

struct Ball
{
	Vec2 pos;
	Vec2 vel;
	
	void Integrate (float timestep)
	{
		//pos += vel * timestep; // oops, i did not implement the += operator, so a proper Vec2 library would be a bit larger than my tiny one. We should also add rotation functions to avoid a need for matrices
		
		pos = pos + vel * timestep;
	}
};

So it's half the code to write, and it's also much easier to read the code once we're used to the concept, which happens quickly.

At one point you surely want to do this, but maybe not yet.

JoeJ said:
That's not needed. Since we'll only reflect velocity, it's magnitude won't change.

Oh, I was thinking about every time he launches the ball at a random velocity, which is why I mentioned normalization, but probably should have made my explanations clearer.

I would add my voice to avoid using a math library and avoid using vectors even at this point, just get it to work for now.

btw, what is your reasoning behind using Perp() ? like does it happen a lot you want something perpendicular?

None

AliAbdulKareem said:
btw, what is your reasoning behind using Perp() ? like does it happen a lot you want something perpendicular?

I have used this to implement a support polygon for balancing ragdoll. The perpendicular stuff is used to find if a point is inside or outside the polygon, and to find the closest point on the polygon. The same things would become necessary to handle collision and velocity reflection against walls which are not axis aligned.

In general you always need perp in 2D when you would use the cross product in 3D, i would say. So it should be there.

But Length() and Normaize() should be added ofc.
Though, not everybody does this. In the Physics Engine i'm using, i need to write this to get the length of a vector:

float mag = sqrt(v.Dot(v).AsFloat());

which is really cumbersome and annoying. ; )

AliAbdulKareem said:
I would add my voice to avoid using a math library and avoid using vectors even at this point, just get it to work for now.

Yes, but also no.
Phil runs against a wall because he uses just global variables and spaghetti code for everything. He could not understand my simple example code of a Space Invaders game because i have used a struct for enemies in some array or std::vector.
So he must learn about using data structures to get over the wall. Otherwise he'll remain stuck.

And those vec2, vec3 and matrices we use are the very best example to show the incredible advantage of using small structs or classes. It's the perfect exercise, but sadly the math concept gets in the way eventually. And i want to teach data structures, not yet math.

So i agree it's best to go without it first, but then eventually update the working Pong game to use Vec2 instead x,y variables, once it works.
Or eventually do Space Invaders first, still focusing primarily on a data structure to handle many enemy sprites.
But when it comes to Breakout, using Vec2 for math can reduce complexity a lot and i recommend it at this point.

(Btw, don't confuse vec2 or vec3 with std::vector. Completely different things which have nothing in common.)

Advertisement

I got the ball to move around the screen. Now I have to get the ball to bounce off the breakout bricks. I am going to use AABB collision detection algorithms

congrats! keep going. + start by just detecting the edges of the screen instead of a full AABB.

None

This topic is closed to new replies.

Advertisement