Breakout physics
Malinus

As the next step on the "learning game-dev ladder" I decided to make a breakout clone. But before I start, I could really use some few advices about the physics. Should I use vectors, which will give the ball a lot of diffrent angels? Or is this a waste of time, and I should stick to a few fixed angels (something like: 10, 45 and 70 degrees, like in DX-ball).

LennyLen

Using vectors gives you more options. You can either use a true vector, with an angle and a velocity, or just store an x and y velocity.

gnolam
LennyLen said:

You can either use a true vector, with an angle and a velocity, or just store an x and y velocity.

A vector expressed in polar (angle and length) coordinates is no more true than a vector expressed in Cartesian (grid) coordinates. :P

And yes, use vectors.

Malinus
gnolam said:

And yes, use vectors.

Soo, it´s time to dust off my math books :P.

So the ball should bounce off at the same angel from every other surface then the paddle. Should I divide the paddle into diffrent zones where every zone represents a specific angel? Or how is it normally done? Or am I just wrong about everything?

Evert
Malinus said:

So the ball should bounce off at the same angel from every other surface then the paddle. Should I divide the paddle into diffrent zones where every zone represents a specific angel? Or how is it normally done? Or am I just wrong about everything?

Invert the component perpendicular to the surface of a the panel at the point of collision.
If you want to do something slightly more interesting, you could add variations on that instead, but that is the gist of it.

Malinus
Evert said:

Invert the component perpendicular to the surface of a the panel at the point of collision.
If you want to do something slightly more interesting, you could add variations on that instead, but that is the gist of it.

Yup that part i understand (just like light). But what when the ball hits the paddle?

OnlineCop
Malinus said:

Should I divide the paddle into diffrent zones where every zone represents a specific angel?

Yes. It makes the game a LOT more fun. Having a bland "always 45-degree" (or whatever angle) bounce makes the ball "landing" location very predictable (even with the tiles themselves bouncing the ball back).

Start your angle off in a range between 30 and 60 degrees in one of the four quadrants (so one of: 30..60, 120..150, 210..240, 300..330).

Your 'x' coordinate is calculated with cos(angle) and your 'y' is sin(angle). Even while the angle changes, the total velocity of the ball remains the same, so even if it shoots off to the side from hitting the corner of the paddle, it will still be traveling at the same speed.

More often than not, the moving ball (because of its velocity) will travel further than a wall's, brick's, or paddle's [closest] outside edge. To deal with that, and make it bounce correctly, calculate the time where the ball just touches the edge of that object, then backtrack the ball to that position. Reverse the ball's perpendicular direction (so it bounces off the object), then move the ball forward (in that new direction) by the amount you "backtracked" earlier. So even at high speeds, your ball will calculate the correct position to bounce at.

Michael Faerber

You could also just give the ball a different direction when the paddle moves upon collision.

For example, you move the paddle to the right and the ball is approaching, it will be reflected to the right.

Malinus
OnlineCop said:

Yes. It makes the game a LOT more fun. Having a bland "always 45-degree" (or whatever angle) bounce makes the ball "landing" location very predictable (even with the tiles themselves bouncing the ball back).

Start your angle off in a range between 30 and 60 degrees in one of the four quadrants (so one of: 30..60, 120..150, 210..240, 300..330).

Your 'x' coordinate is calculated with cos(angle) and your 'y' is sin(angle). Even while the angle changes, the total velocity of the ball remains the same, so even if it shoots off to the side from hitting the corner of the paddle, it will still be traveling at the same speed.

More often than not, the moving ball (because of its velocity) will travel further than a wall's, brick's, or paddle's [closest] outside edge. To deal with that, and make it bounce correctly, calculate the time where the ball just touches the edge of that object, then backtrack the ball to that position. Reverse the ball's perpendicular direction (so it bounces off the object), then move the ball forward (in that new direction) by the amount you "backtracked" earlier. So even at high speeds, your ball will calculate the correct position to bounce at.

Now this was helpfull.
So it´s basicly just

#SelectExpand
1x+=cos(angel / 180 * PI); 2y+=sin(angel / 180 * PI);

And then when it collides with the paddle I just change angle (according to where on the paddle it hits).

Reversing the ball of objects should be something like:
angel=((-cos(angel / 180 * PI))*180)/PI;

But in the part about backtracking you really lost me. I got the point but i really don´t know how it should be done. Or more precisely: how to calculate the time where the ball just touches the edge of the object? Especially when i think the ball´s velocity will be controlled by a timer.

gnolam

No. Use Cartesian vectors. Then, as Evert said, you can just invert the component perpendicular to the surface hit.

// Initial velocity
float xv = cos(some_angle);
float yv = sin(some_angle);

void Update()
{
    x += xv;
    y += yv;
}

void ReflectOffVerticalSurface()
{
    xv = -xv;
}
// Reflection off a horizontal surface is left as an exercise to the reader ;)

Malinus
gnolam said:

No. Use Cartesian vectors. Then, as Evert said, you can just invert the component perpendicular to the surface hit.

But since the sin & con use radians i still need to convert the angel to radians, right? Or am I just wrong about everything?:P

All the back backtracking talk OnlineCop did, made me a little dizzy. Won´t it work with just something like:

#SelectExpand
1if(x >= (SCREEN_W - ball->w) ){ 2xv = -xv; 3}

etc. etc.
Or isn´t it accurate enough at high velocity?

But my real question now is, how do I add velocity? I tried just adding a xspeed and yspeed to the x,y and invert it when needed. But it still seam to affect the angel as well.

LennyLen

For future reference this is an angel:

{"name":"angel4c.gif","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/4\/1\/419deb088cca25005eb568582c1f1dd3.gif","w":600,"h":419,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/4\/1\/419deb088cca25005eb568582c1f1dd3"}angel4c.gif

This is an angle:

{"name":"800px-Angle_measure.svg.png","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/2\/d\/2da9ead9838239217018f36443b2cbd2.png","w":800,"h":563,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/2\/d\/2da9ead9838239217018f36443b2cbd2"}800px-Angle_measure.svg.png

Malinus said:

All the back backtracking talk OnlineCop did, made me a little dizzy. Won´t it work with just something like...Or isn´t it accurate enough at high velocity?

That will work, but if the ball goes sufficiently outside the wall, you may have a few frames where it isn't visible.

Malinus

Well i should have paid more attention at my english class´ :P. But how to add velocity without affecting the angle?

EDIT: and at my math class as well :P

gnolam
Malinus said:

But my real question now is, how do I add velocity? I tried just adding a xspeed and yspeed to the x,y and invert it when needed. But it still seam to affect the angel as well.

Multiply both velocity components by the same value.

verthex

You take each component of the vectors you wanna add and then you find the magnitude just as

gnolam said:

Multiply both velocity components by the same value.

heres a tutorial on vectors ;D

OnlineCop
Malinus said:
x+=cos(angel / 180 * PI);
y+=sin(angel / 180 * PI);

Malinus said:

But my real question now is, how do I add velocity?

Your equation assumes that your angle is stored as a degree, and not a radian. If you started out with the angle as a radian, you can simplify your math (it speeds up your calculation, because you don't have to re-calculate "angle / 180 * PI" every time). Instead, set your initial angle to be in radians, and you'll be set:

   static const DEG2RAD = (double)M_PI / (double)180.0;

   // quadrant 0 is top-right, 1 is top-left, 2 is bottom-left, 3 is bottom-right
   unsigned char quadrant = rand % 4;

   // rand_twosided() returns a random value between 30..60 degrees
   double angle = rand_twosided(45.0, 1.0/3.0) * DEG2RAD + M_PI/2.0 * quadrant;

To figure out the velocity, pass in the DELTA time (from your last logic update):

void update_ball(double delta_time)
{
   x += (velocity * delta_time) * cos(angle);
   y += (velocity * delta_time) * sin(angle);
   // ... other stuff ...
}

I'm in class now, so when I have more time, I'll try to reply about how to find the "backtrack amount". ;D

Malinus

After reading your explanation, looking at my old math books (ahh not THAT old), and reading some tutorials online I think I got the hang of it now. So thanks for the explanations guys.

And OnlineCop if you get time I would really appreciate it. But don´t let it distract you from class, or you will end like me :P. Hehe I´m just happy I haven´t started a 3D project yet ;D.

gnolam

Argh. Polar coordinates are not the tool for the job here. They just complicate things.

Also,

OnlineCop said:

x += (velocity * delta_time) * cos(angle);

You mean speed. Velocity is a vector quantity, speed is a scalar.

OnlineCop
gnolam said:

You mean speed. Velocity is a vector quantity, speed is a scalar.

You're probably right. I meant speed. Saying "velocity" just sounds like you're so much smarter than your average "speed" user.

I'm still looking for some good examples of backtracking explained better than I can do (I've tried writing some code snippets and examples, but they fail).

You can always try this wiki for some help; I haven't looked through it extensively enough to see whether it discusses this, but it's worth a shot.

Malinus

So after looking at the internet and reading about the backtracking algorithm, which is almost the same as the brute-force algorithm, I assume you are not referring to that algorithm. So instead I tried with making a function that looks something like:

int trace(){

int tracing=0;
for(int a=0;a<100;a++){
   x += xv*speed;
   y += yv*speed;
   tracing++;

   if(collision)
   return tracing;
    }

return 0;
}

And run it every logic, to make it simulate that the logics of ball moving happend 100 times. And return after how many logics the collision will happen.

But I still can´t see any advantage, because I still use bounding box collision detection, for collisions :s... So it´s just a lot of extra work for the CPU.

If I have to be honest I think I´m pretty much off.

OnlineCop

To be honest, I've been too busy to respond. Sorry.

I've usually used this as a good starting point, since it helps with several different shapes that collide with each other. Some of the code (near the bottom) also use timer calculations to determine the "right" time, when the collisions first occur. It may be helpful.

EDIT: So may this PDF.

Malinus
OnlineCop said:

To be honest, I've been too busy to respond. Sorry.

Wow, dont worry about that. Im just really happy for the help.

OnlineCop said:

I've usually used this[www.gamedev.net] as a good starting point, since it helps with several different shapes that collide with each other. Some of the code (near the bottom) also use timer calculations to determine the "right" time, when the collisions first occur. It may be helpful.

EDIT: So may this[www.peroxide.dk] PDF.

Well even though Im thankfull for the reading, Im not really sure how much effort I should put into making a ball consisting of 10 pixels collide perfect. But I guess I should still try, becouse even though a simple bounding box collision detection will be more then enough for a simple game like breakout, I will be able to use the stuff I learn in further projects.

OnlineCop

A quick-and-dirty that you can do is simply backtracking completely, and reverse direction from that point (basically, the last location BEFORE your ball moved from the current update() call.

So let's say that your ball goes too far horizontally. Since you had calculated the "current" position like this:

   x += (delta_time * velocity) * cos(angle);

then to backtrack, simply undo that operation (one of two ways):

   x -= (delta_time * velocity) * cos(angle);

...or...

   old_x = x;
   x += (delta_time * velocity) * cos(angle);

   if ( /* collision test */ == true)
   {
      angle = M_PI - angle;
      x = old_x + (delta_time * velocity) * cos(angle);
   }

This way, it's not terribly accurate, but the math is a lot simpler, and at high framerates, you'll never even know that it was "imprecise calculations" (you'll see it if your framerate drops and your ball suddenly ricochets off of apparently "nothing").

One other problem with Breakout clones is that you often try to do your collision detection against ALL of the bricks on the screen, every frame. The best thing to do for that is to optimize your collision check, since that's probably one of the bigger issues you'll deal with.

Imagine testing against every tile when you only have about 20 tiles on-screen. Not a big deal, even with a "check against every single tile"-unoptimized collision check. But get to a level that's nothing but tiles (100-200 tiles), and that same collision-check algorithm could make your game come to a screeching halt. But that's a different thread... ::)

Schyfis
Quote:

int tracing=0;
for(int a=0;a<100;a++){
   ...
   tracing++;
   ...
}

Just a heads-up that a and tracing will always be the same number. You can get rid of tracing and return a.

Malinus
Schyfis said:

Quote:

int tracing=0;
for(int a=0;a<100;a++){
...
tracing++;
...
}

Just a heads-up that a and tracing will always be the same number. You can get rid of tracing and return a.

I must say I´m more stupid then i thought....

OnlineCop said:

then to backtrack, simply undo that operation (one of two ways):

x -= (delta_time * velocity) * cos(angle);

You can´t imagine how helpfull it was. I finally (/about time!!) understand. How could i possibly be so slow? I rally need to start my study now... hehe

OnlineCop said:

One other problem with Breakout clones is that you often try to do your collision detection against ALL of the bricks on the screen, every frame. The best thing to do for that is to optimize your collision check, since that's probably one of the bigger issues you'll deal with.

Imagine testing against every tile when you only have about 20 tiles on-screen. Not a big deal, even with a "check against every single tile"-unoptimized collision check. But get to a level that's nothing but tiles (100-200 tiles), and that same collision-check algorithm could make your game come to a screeching halt. But that's a different thread...

I actually allready considered that. I think I will only check collision with the titles that have less then 4 neighbours, even though it may not be the best optimization (heh), I think it will be enough....

Evert
Malinus said:

Yup that part i understand (just like light). But what when the ball hits the paddle?

Same.
To make things more interesting, you can add an impulse to the ball in the direction the paddle is moving at the time, or allow the paddle to be tilted.

Malinus
Evert said:

Malinus said:
Yup that part i understand (just like light). But what when the ball hits the paddle?

Same.
To make things more interesting, you can add an impulse to the ball in the direction the paddle is moving at the time, or allow the paddle to be tilted.

Well I think I will do as they did in the old good arkanoid (or newer DX-ball). Just divide the paddle into 6 zones. 3 left zones and 3 right zones. With the little twist that every of the 6 angles will have a little random interval, so no "stuck-ball" occurs.

I´m glad that games don´t have to follow the rules of real world physics ;D

OnlineCop

Some versions of Breakout provide some sort of enhancement for the player's paddle (or for the game itself), like "slower ball", "stick on paddle until mouse-click", "shoot lasers to also pop tiles", etc.

You could do the same kind of thing: turn on gravity, make the paddle "bounce" the ball back (like bouncers in pinball machines), change from a 6-angle paddle (like you were just talking about) to a totally flat paddle, etc.

But that's just for your TODO list down the road, once everything else gets up and going.

Malinus

Thanks for the ideas OnlineCop.

Now that the "engine" of the game is working I found a other problem... When having a fps about 60, and a 800x600 resolution, you have to set the speed to about 5 before the ball moves fast. Setting speed to 5 will make the ball look like it jumps. When only updating the ball once pr. frame how can I avoid this problem?

The only way I can see to work around this problem is setting the fps to 300 and then having the speed on 0.5-1.5...

OnlineCop

How is your logic being updated, according to time? Do you pass in the delta time, the current number of "ticks", or are you simply calling your update() function without using any sort of "time measurement"?

Also, logic updates and screen updates rarely need to be set at the same speed. You may only want to do logic updates a few times (10 or 20 times for your 60-fps setup), and then draw every single frame (drawing is actually quite CPU-intensive, so that may not be so great), or fiddle with some of your settings.

I have one game where you can moderate your FPS. Below 20 FPS, the game really starts to stutter noticeably. Stepping up from below 20 FPS to around 30 is when you can tell that the "jumping" gets smoothed to almost non-existent. Between 30 and 60 FPS, there's not major difference, and above that (up to 200 FPS, where I cap it), there's no change at all (though it eats up more CPU).

Working with an actual "delta_time" being passed into my update() function was the best thing for me. At 200 FPS, my delta time was in the order of "1/200th" of a second (well, due to processing time, maybe "1/198th").

Check out how I have the code (at least the updating system) in the attachment on this thread. While I didn't go with the Allegro timers (I used the BZflag's code), the way that the game gets updated is with delta times, and you can change the FPS with the '-/+' keys.

Malinus
OnlineCop said:

How is your logic being updated, according to time? Do you pass in the delta time, the current number of "ticks", or are you simply calling your update() function without using any sort of "time measurement"?

I´am simply using the standart mainloop model that looks something like this:
Wiki mainloop.

OnlineCop

The trouble with the design presented on the wiki is, as mentioned earlier, that you don't get any sort of delta to compare your timing against. It essentially assumes that all updates are the same, regardless of framerate. And from experience, I have personally seen most programs jump between a single tick, to multiple ticks, each time the logic gets updated. That's due to other programs hogging the CPU (good old Firefox, IE 8, Safari, Opera, and any other tabbed browser that likes to refresh its tabs on a regular interval), which throws off the exact "tick" count in odd places.

The wiki shows this kind of construct:

#SelectExpand
1volatile int ticks; 2void timer(void) {ticks++;} 3 4... 5 6install_int(timer, 1000.0 / FPS); 7 8... 9 10while (1) { 11 if (game_ticks < ticks) { 12 poll_input(); 13 handle_game_tick(); 14 game_ticks++; 15 need_redraw = true; 16 } 17 else if (need_redraw) { 18 render_last_frame(); 19 need_redraw = false; 20 } 21 22 rest(1); 23}

However, I'd suggest modifying that a little.

(Warning: untested)

#SelectExpand
10int old_ticks = ticks; 11while (1) { 12 double delta_time = (ticks - old_ticks) / (1000.0 / FPS); 13 old_ticks = ticks; 14 15 if (game_ticks < ticks) { 16 poll_input(); 17 18 handle_game_tick(delta_time); 19 20 game_ticks++; 21 need_redraw = true; 22 } 23 else if (need_redraw) { 24 render_last_frame(); 25 need_redraw = false; 26 } 27 28 rest(1); 29}

With this, since the timer was set initially to "(1000.0 / FPS), then to get the delta time, you take the difference between the current and old ticks, and divide by that value. So if your FPS is set to 60, then the allegro timer will update your "ticks" once every (1000/60)th of a second (or roughly every 1/16th of a second). If the difference of (ticks - old_ticks) is "0", then your logic is simple: the ball's velocity gets moved "0" pixels. If the difference is "1", then the delta_time is around "0.06" seconds, meaning that your ball will move (velocity * 0.06 * [cos(angle) | sin(angle)]) pixels (depending on x- or y-direction).

Does that work in your program?

SiegeLord

Firstly, that's delta timing which is highly flawed. Secondly, you are implementing it using allegro timers which are not nearly precise enough to support it properly, even if there was a reason to use it.

Malinus

What do you suggest then?

OnlineCop

Personally, I stole the timing routine used in BZ Flag's sources and used that. They've already made it compile on Mac, Windows, and Linux, and it uses Mutexes (if needed), Windows' GetTickCount() (or whatever it is), and so on. 8-) The only change I made to it was to get rid of about 3-4 lines of code that was specific only to their game, and then change their header from "#include <windows.h>" to #include <winalleg.h>. (I'm not tooting my own horn here, but in this post, I've attached a game that has this already working properly. You can rip the code out of there as you'd like. Just be sure to give them credit if you leave it in your game.)

Malinus

So what you guys are saying, is that it is impossible to make timing for a breakout game, using the allegro4 timers only? Or at least without forcing 100% CPU because of having to update logics 300/s...

OnlineCop

Only that timing precision isn't wonderful when using Allegro 4. It gets close, sure, but since the library doesn't take an aggressive approach to getting the current "computer ticks since epoch", it's at the mercy of other background processes also competing for CPU time.

To get really good precision on the architecture you're targeting, you basically need to ask the device/OS/hardware to provide you with its total number of "ticks" that have occurred. Since hardware itself updates this, even if you have a computer hogging 100% CPU time, it's still getting updated consistently.

But if you don't need "super-accurate" timing, the allegro timers will be just fine.

gnolam
Malinus said:

So what you guys are saying, is that it is impossible to make timing for a breakout game, using the allegro4 timers only? Or at least without forcing 100% CPU because of having to update logics 300/s...

300 updates/s is way too much. First off, the timers are too coarse (10 ms by default under Windows) to properly handle update frequencies that high. But more importantly, there is absolutely no point of running updates at a higher frequency than your display - which at best goes up to around 100 Hz. 50 Hz should be more than enough for your needs.

Malinus
gnolam said:

Malinus said:
So what you guys are saying, is that it is impossible to make timing for a breakout game, using the allegro4 timers only? Or at least without forcing 100% CPU because of having to update logics 300/s...

300 updates/s is way too much. First off, the timers are too coarse (10 ms by default under Windows) to properly handle update frequencies that high. But more importantly, there is absolutely no point of running updates at a higher frequency than your display - which at best goes up to around 100 Hz. 50 Hz should be more than enough for your needs.

I know, but when using 60Hz you will need a x- and yspeed that are higher then 2 if you want to have you ball go fast (at 800x600). This will of course lead to the ball "jumping", which looks very bad. So the only solution I can see is to force a very high FPS, which is possible in a simple game, but of course VERY inefficient... How can I get a smooth ball and having 60bps?

SiegeLord

You do know that if your monitor only refreshes at 60Hz and your ball moves faster than 1px per 1/60 seconds you are going to get jumping no matter how high your FPS goes?

Malinus
SiegeLord said:

You do know that if your monitor only refreshes at 60Hz and your ball moves faster than 1px per 1/60 seconds you are going to get jumping no matter how high your FPS goes?

Well it somehow still seams more smooth..

But my question still remains..

OnlineCop

You mean this question?

Malinus said:

So what you guys are saying, is that it is impossible to make timing for a breakout game, using the allegro4 timers only?

Breakout clones have been done many times in the forums. Some better than others, admittedly. Search through the last few years worth of posts and you should find some good pointers and tips on how, when others who have done it, everything was set up to allow for correct timing and updating (so it didn't look choppy/jumpy).

Malinus
OnlineCop said:

Breakout clones have been done many times in the forums. Some better than others, admittedly. Search through the last few years worth of posts and you should find some good pointers and tips on how, when others who have done it, everything was set up to allow for correct timing and updating (so it didn't look choppy/jumpy).

Well it´s not that I havn´t been looking through other clones source codes, or looking at forums etc. etc.... But nowhere I was able to find a solution to this rather simple problem. I really can´t see how it can be possible to make ball go smooth AND fast (over 1pixel pr. update ). So the max speed of a ball can´t be more then 60 pixels/s ("1" it´s not a real definition - just a guiding number it propably wont look jumping with 80pixels/s either but at 200 it propably will). So all I ask for, is if someone will be willing to give me some guiding lines how to do a PROPER timing for a game with a ball as the main object.

OnlineCop

With what you've currently coded up, are you still seeing jerky movement? If so, could you post your code? We may be able to spot where the jerkiness comes from, and post better feedback once we know how to best respond based on your needs.

Thread #601650. Printed from Allegro.cc