38
\$\begingroup\$

I'm trying to wrap my head around this very Hello World-y problem in game development. I've created a TicTacToe game in XNA so I guess the next step would be a Breakout clone.

Keep in mind that I have no knowledge on game programming or even what maths I should apply to where. That's why I'm asking this question.


To the question: How can I determine where the ball should bounce when it hits the paddle at the bottom of the screen?

I'd imagine it would be something like:

  1. Capture speed and angle from incoming ball.
  2. Detect where it touched the bar (far left, far right, center) and according to that give it a higher speed if it touched the outer areas.
  3. This is where I'm stuck. Hehe.

Any insights? I realize this is not a straightforward answer type question, but I'm sure it's one everyone faces at some point.

I'm reading the book Linear Algebra that was recommended on this website, but I still have no idea if I should apply it here.

\$\endgroup\$
1
  • \$\begingroup\$ Write pong before breakout, you could then export the ball, wall and paddle classes and extend them such that they work with the different types of bricks and powerups. Plus, I would consider pong simpler than breakout. \$\endgroup\$
    – Incognito
    Commented Oct 5, 2010 at 19:53

11 Answers 11

38
\$\begingroup\$

Here's the relevant logic I used on the pong on my homepage: (please go play it before reading, so that you know the effect I'm achieving with the following code)

Essentially, when the ball collides with the paddle, its direction is completely disregarded; it is given a new direction according to how far from the center of the paddle it collided. If the ball hits the paddle right in the center, it is sent away exactly horizontal; if it hits right on the edge, it flies off at an extreme angle (75 degrees). And it always travels at a constant speed.

var relativeIntersectY = (paddle1Y+(PADDLEHEIGHT/2)) - intersectY;

Take the middle Y value of the paddle, and subtract the Y intersection of the ball. If the paddle is 10 pixels high, this number will be between -5 and 5. I call this the "relative intersect" because it is in "paddle space" now, the ball's intersection relative to the middle of the paddle.

var normalizedRelativeIntersectionY = (relativeIntersectY/(PADDLEHEIGHT/2));
var bounceAngle = normalizedRelativeIntersectionY * MAXBOUNCEANGLE;

Take the relative intersection and divide it by half the paddle height. Now our -5 to 5 number is a decimal from -1 to 1; it's normalized. Then multiply it by the maximum angle by which you want the ball to bounce. I set it to 5*Pi/12 radians (75 degrees).

ballVx = BALLSPEED*Math.cos(bounceAngle);
ballVy = BALLSPEED*-Math.sin(bounceAngle);

Finally, calculate new ball velocities, using simple trigonometry.

This might not quite be the effect you're going for, or you might want to also determine a speed by multiplying the normalized relative intersection by a max speed; this would make the ball go faster if it hits near the edge of a paddle, or slower if it hits near the center.


I'd possibly like some code on what a vector would look like or how I could save the variable of the vector the balls has (speed and direction).

A vector contains both speed and direction, implicitly. I store my vector as a "vx" and "vy"; that is, the speed in the x direction and the speed in the y direction. If you haven't taken an introductory course in physics this might seem somewhat foreign to you.

The reason I do this is because it reduces the per-frame calculations necessary; every frame, you just do x += vx * time; and y += vy * time; where time is the time since last frame, in milliseconds (therefore the velocities are in pixels per millisecond).


Regarding implementing the ability to curve the ball:

First of all, you'd need to know the paddle's velocity at the time the ball hits; which means you'd need to keep track of the paddle's history, so that you can know one or more of the paddle's past positions so that you can compare them to its current position to see if it moved. (change in position / change in time = velocity; so you need 2 or more positions, and the times of those positions)

You now also need to track an angular velocity of the ball, which practically represents the curve along which it is traveling, but is equivalent to the real-world spin of the ball. Similar to how you would interpolate the bounce angle from the relative position of the ball on collision with the paddle, you would also need to interpolate this angular velocity (or spin) from the velocity of the paddle on collision. Rather than simply setting the spin like you do with the bounce angle, you might want to add or subtract to the ball's existing spin, because that tends to work well in games (the player can notice the ball is spinning, and cause it to spin even more wildly, or counter the spin in an attempt to make it travel straight).

Note, however, that while this is the most common sense and probably easiest way to implement it, the actual physics of a bounce doesn't rely solely on the velocity of the object it hits; an object with no angular velocity (no spin) which hits a surface at an angle, will have a spin imparted upon it. This might lead to a better game mechanic, so you may want to look into this, but I'm not certain of the physics behind it so I'm not going to try to explain it.

\$\endgroup\$
8
  • \$\begingroup\$ That effect is what I'm going for; the faster speed as it hits the edges of the bar. Thanks a bunch for taking the time to write this out. I'm having some trouble understanding a few things though; for example, in the first snipper, what is 'intersectY'? Also, 'paddle1Y' is the height of the bar correct? \$\endgroup\$
    – Sergio
    Commented Oct 2, 2010 at 22:57
  • \$\begingroup\$ intersectY is the position of the ball where it intersects the paddle. I do an overcomplicated calculation which I honestly don't even understand right now, but essentially it is the Y value of the ball at the time it collides. paddle1Y is the Y value of the paddle, from the top of the screen; PADDLEHEIGHT is the height of the paddle ("bar"). \$\endgroup\$
    – Ricket
    Commented Oct 2, 2010 at 22:59
  • \$\begingroup\$ What would you have to add in order to allow "curve" balls? For example, when the ball is about to hit the paddle you move the paddle in order to make the ball curve. Something like this: en.wikipedia.org/wiki/Curve_ball \$\endgroup\$
    – Zolomon
    Commented Oct 7, 2010 at 17:59
  • \$\begingroup\$ See the edit, and let me know what you think (if you need more info about something, don't get something, etc.) \$\endgroup\$
    – Ricket
    Commented Oct 7, 2010 at 23:26
  • \$\begingroup\$ Thanks! Superb answer, I've always wondered how to do achieve that effect! \$\endgroup\$
    – Zolomon
    Commented Oct 9, 2010 at 7:31
9
\$\begingroup\$

It has been a while since I've done this, but I think I've got this right.

Given a perfect collision, the angle of reflection is equal to the angle of incidence.

You know the normal of your paddle (presuming a flat surface): N You know your original position of your ball (at the beginning of your timestep): P You know your new position of the ball (at the end of the timestep): P' You know your point of collision: C Assuming that you calculated that the segment P -> P' passes through your paddle, your new reflected postion (P'') would be:

P' + 2 * (N * (P' dot -N) )

The N * (P' dot -N) subexpression calculates the depth along the normal of collision the ball traveled. The minus sign is to correct for the fact we're checking for depth opposite the direction of the normal.

The P' + 2 * the part of the subexpression moves the ball back above the plane of collision by 2 times the depth of the collision.

If you want a less than perfect collision, change the factor 2 to be (1 + (1-k)) where k is your coefficient of friction. A perfect collision has a k value of 0, causing the reflection angle to be exactly that of the incoming angle. A k value of 1 causes a collision where the ball would stay on the surface of the plane of collision.

Your new velocity vector, V'', direction would be P'' - C. Normalize it and multiply by your incoming velocity and your resultant velocity magnitude would be the same, but in the new direction. You can monkey with that velocity by multiplying it by a coefficient, l, that would either increase (l > 1) or decrease (l < 1) the resulting velocity.

To summarize:

P'' = P' + (1-k) * (N*(P dot -N)) V'' = l * V * ((P'' - C) / |P''-C|)

Where k and l are coefficients of your choosing.

\$\endgroup\$
6
\$\begingroup\$

The reflection can either be done "right" or done "easy."

The "right" way is to compute vectors perpendicular to the walls. In 2D that's pretty easy and you could probably just hard code them. Then, the reflection step essentially leaves the "parallel" component of the motion intact and reverses the "perpendicular" component. There's probably detailed information on the web for this, maybe at MathWorld even.

The "easy" way is to just negate the X or Y motion when you hit a wall. If you hit the side walls, you'd negate X. If you hit the top you negate Y. If you want to speed the ball up, just increase whatever you want; you can speed it up in its current direction by multiplying both the X and Y speeds or you can speed up only on one axis.

\$\endgroup\$
2
  • \$\begingroup\$ Aren't the "easy" way and the "right" way described above essentially the same?? \$\endgroup\$
    – Tom
    Commented Jun 22, 2014 at 11:01
  • 1
    \$\begingroup\$ They are exactly the same if the walls are along the major axes. If the walls are not all along the X, Y, and Z axes then no, the two are completely different. \$\endgroup\$ Commented Jun 23, 2014 at 17:26
5
\$\begingroup\$

I'm making an arkanoid-ish game myself too and I think the solution on how the ball should behave when hitting the paddle is quite simpler and faster than getting into the sin/cos approach... it works fine for the purposes of a game like this. Here's what I do:

  • Of course, since the ball speed increases in time I interpolate the before/after x,y steps to keep accurate collision detection, looping through all "stepX" and "stepY" which are calculated dividing each speed component by the modulus of the vector formed by the current and future ball positions.

  • If a collision against the paddle occurs I divide the Y speed by 20. This "20" is the most convenient value I found to get my resulting maximum angle when the ball hits on the sides of the paddle, but you can change it to whatever your needs are, just play with some values and choose the better for you. By dividing, let's say a speed of 5, which is my initial game speed by this number (20), I get a "rebound factor" of 0.25. This calculation keeps my angles quite proportional when the speed increases in time up to my maximum speed value which, for example, could be 15 (in that case: 15/20 = 0.75). Considering that my paddle x, y coords are midhandled (x and y represent the center of the paddle), I then multiply this result by the difference between the ball position and the paddle position. The greater the difference, the greatear the resulting angle. Besides, using a midhandled padlle, you get the correct sign for the x increment depending on the side the ball hits without having to worry about calculating the center. In pseudo-code:

For n = 0 to modulus ...

if collision_detected then speedX = -(speedY / 20 ) * (paddleX - ballX); speedY = -speedY;
exit; end if

...

x = x + stepX; y = y + stepY;

end for

Remeber, always try to keep things SIMPLE. I hope it helps!

\$\endgroup\$
4
\$\begingroup\$

The paddle in Breakout, when it follows the style you're describing is usually modelled as a curved surface. The angle of incidence changes based on where on the paddle it hits. On the dead center the tangent line to the curve is absolutely horizontal, and the ball reflects as expected. As you move off the center, the tangent to the curve becomes increasingly angled, and the ball reflects differently as a result.

The key point is that the angle of reflection, not the ball speed, is what changes. The ball speed generally just increases slowly over time.

\$\endgroup\$
2
  • 1
    \$\begingroup\$ You say as a curved surface and that sounds logical in my head but once I try to think of it in terms of code things get fuzzy fast. How could I even declare that in code as a variable or something. \$\endgroup\$
    – Sergio
    Commented Oct 2, 2010 at 22:46
  • \$\begingroup\$ Something like angle = 1 - 2 * (ball.x - paddle.left) / paddle.width will give you a number between 1 and -1; this (times some value tweaked for your game mechanics) is the slope of the tangent line at the point the ball collided. Reflect off that line rather than the strictly horizontal one. \$\endgroup\$
    – user744
    Commented Oct 2, 2010 at 23:00
4
\$\begingroup\$

Nolan Bushnell gave a keynote at SIEGE this past weekend and talked about a similar issue with the original pong. You don't have to do alot of complicated calculations. If you hit on towards the left portion of hte panel, send the ball left. Do the same for the right side.

To start off with you can make the angle for the left and right sides 45 degrees. Once you finish the game you could if you want go back and make this more complicated but make this as simple as you can to begin with.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ I saw that keynote too, his point was that this was a design decision not a math decision: "angle of incidence = angle of reflection" would be correct but make for weak gameplay. Additionally in the original Pong and Breakout, speed was a function of how many ball/paddle collisions there were (so it speeds up over time). They also reduced paddle size after a certain number of hits, too. I would avoid allowing the ball to go straight up though, then you could leave the paddle there indefinitely. \$\endgroup\$ Commented Oct 5, 2010 at 15:18
4
\$\begingroup\$

Breakout is a classic beginners work for starting to dive into the world of physics based game programming. Basically the ball has a bounce motion when it hits on the wall. As somebody above suggested the angle of incidence is equal to the angle of reflection. But when you consider the ball hitting the paddle. The logic is divided into 3 sections. 1.) The ball hitting the center portion of the paddle. 2.) The ball hitting the leftward portion of the paddle. 3.) The ball hitting the rightward position of the paddle.

When you consider the center portion: You need not differ the bounce effect of that which is applied when hitting the ball. The ball just gets deflected normally.But, when either direction is hit, the case is different.

When the ball is hit on the leftward side, ie consider the ball coming from the leftward side of the screen and you are coming with the paddle from the right side. Then when you hit the ball with the leftward portion, the ball should reflect to the direction it came from .ie.leftwards with the same angle it came from. Same is the case vice versa. In the rightward portion also the same thing applies.

This movement of the ball towards the leftward or rightward direction when it is being hit makes it more believable.

Hope you got the idea,at least logic wise.Thank you

\$\endgroup\$
3
\$\begingroup\$

Imagine you calculate the distance between the center of the paddle and the point where the ball Y hit and call it d. Let's suppose d has a positive value when the ball hit above the center of the paddle. You can now add d * -0.1 to the Y velocity of your ball and it will start changing direction. Here is an example in javascript that can be easily translated into c#!

var canvas = document.querySelector('canvas');
var resize = function () {
  canvas.width = innerWidth;
  canvas.height = innerHeight;
};
resize();
var ctx = canvas.getContext('2d');
var ball = {
  size: 3,
  x: 1,
  y: canvas.height/2,
  vx: 2,
  vy: 0
};
var paddle = {
  height: 40,
  width: 3,
  x: canvas.width/2,
  y: canvas.height/2
};
addEventListener('mousemove', function (e) {
  paddle.y = e.clientY - (paddle.height/2);
});
var loop = function () {
  resize();
  ball.x += ball.vx;
  ball.y += ball.vy;
  if (ball.x > canvas.width || ball.x < 0) ball.vx *= -1; // horiz wall hit
  if (ball.y > canvas.height || ball.y < 0) ball.vy *= -1; // vert wall hit
  if (ball.x >= paddle.x && ball.x <= paddle.x + paddle.width && ball.y >= paddle.y && ball.y <= paddle.y + paddle.height) {
    // paddle hit
    var paddleCenter = paddle.y + (paddle.height/2);
    var d = paddleCenter - ball.y;
    ball.vy += d * -0.1; // here's the trick
    ball.vx *= -1;
  }
  ctx.fillRect(ball.x,ball.y,ball.size,ball.size);
  ctx.fillRect(paddle.x,paddle.y,paddle.width,paddle.height);
  requestAnimationFrame(loop);
};
loop();
body {overflow: hidden; margin: 0}
canvas {width: 100vw; height: 100vh}
<canvas></canvas>

\$\endgroup\$
1
\$\begingroup\$

Hi I have been recently tried to make a ball game and figured out a solution for this. So what I did: The paddle is moving while we are playing the game. My coordinate system is left as it is, the top-left point of the canvas is 0,0. The paddle is moving in this coordinate system. The x axis points from 0 to canvas width, and y axis is pointing on 0 to canvas Height. I created a paddle with fix size 100 width and 20 height. And then I draw an imagined circle around it. When the ball hits the paddle I compute paddle's Centerpoint

double paddleCenter=Squash.paddle.getXpos()+Squash.paddle.getPaddleWidth()/2;

Then I subtract the center from the ball's current position this way the coordinate system will be at the center of the paddle, ballCenter is the point where the ball hit the paddle (-(paddlewidth+r)..0..(paddlewidth+r)) this is nothing but rescaling the hitting point on the paddle

double x0 = ballCenterX-paddleCenter;

compute the circle's intersection point with the help of the ball's hitting point (x0) this is a recomputation , we ask for the y coordinate on the circle with the x0 already known coordinate and there was needed a flip for the y axis

double y0 = -Math.sqrt(paddleRadius*paddleRadius-x0*x0);

compute the derivate of the normal equation of the circle which is defined around the paddle with radis paddleRadius f(x,y)=x^2+y^2-r^2

double normalX=2*x0;
double normalY=2*y0;

normalize the N vector, to get a unit vector for the surface normal

double normalizer=Math.sqrt(normalX*normalX + normalY*normalY);
normalX=normalX/normalizer;
normalY=normalY/normalizer;

now we have the normalized (unit) surface normals for the paddle. Compute the new direction with these surface normals, this will be computed with the help of the reflection vector formula: new_direction=old_direction-2*dot(N,old_direction)*N, but instead with the surface normal pointing always upwards, the normal's will be changing from point to point where the ball hits the paddle

double eta=2; //this is the constant which gives now perfect reflection but with different normal vectors, for now this set to 2, to give perfect reflection
double dotprod=vX*normalX+vY*normalY;
vX=vX-eta*dotprod*normalX;//compute the reflection and get the new direction on the x direction
vY=-vY;//y direction is remain the same (but inverted), as we just want to have a change in the x direction

I've published my solution to this problem. For more details and for the complete game you can see my github repository:

https://github.com/zoli333/BricksGame

written in java with eclipse. There is another solution for this commented out in Ball.java, where the rescaling not happen I don't move the coordinate coordinate system to the paddle's center point, instead I compute all of these above from the topleft 0,0 coordinate system relative to the paddle's center point. This works also.

\$\endgroup\$
0
1
\$\begingroup\$

based on @Ricket's answer, I solved it basically with Pythagorean theorem. You want the size of the final vector to be 1, so you can multiply it to have your final ball speed. Following Python code:

At first, u take the intersect of the paddle-(bar) and the ball. So it's the X vector.

relativeIntersectX = (self.bar_x + (self.bar_width/2)) - self.ball_x

Then you normalize it. So it is value between -1 and 1.

normalizedIntersect = (relativeIntersectX/(self.bar_width/2))

So, the ball_vx (x-velocity) is basically the vector itself. Velocity is any positive number based on, how fast you want the ball.

self.ballvx = -normalizedIntersect*self.velocity

The ball_vy is just expressed from the formula vx^2 + vy^2 = 1.

self.ballvy = -math.sqrt(1-normalizedIntersect**2)*self.velocity

You might want to care about special cases, so you just go something like this.

if normalizedIntersect > 0.95:
     normalizedIntersect = 0.95
elif normalizedIntersect < -0.95:
     normalizedIntersect = -0.95
\$\endgroup\$
0
\$\begingroup\$

this will help http://www-classes.usc.edu/engr/ee-s/477p/s00/pong.html you simply invert x and y depending on where it hits the screen. if its going up, and hits the top, it inverts the y velocity while keeping the x velocity

\$\endgroup\$

You must log in to answer this question.