Debugging Breakout paddle bounces
Published on
Over the summer, I started working through the 20 games challenge. While working on my Breakout clone, I struggled with choosing the physics parameters for how the paddle should affect the ball's speed and angle. That's the kind of thing that's easier with a debugger!
My childhood memory of Breakout is actually itself a clone (Brick on the iPod 5G). But I think I remember that the paddle would bounce the ball differently based on how the two collided. Specifically, the behavior I remember (and wanted to implement) is that hitting the ball near the edge of the paddle would make the ball move faster and at a more horizontal angle.
In focusing on making any game at all rather than a good one, I ended up with this mess of unexplained constants:
let offset = (collision.x - paddle.x) / Paddle::SIZE.x;
let tilt = (0.).lerp(-PI / 4., offset);
let angle = (v.to_angle() + tilt).clamp(PI / 6., 5. * PI / 6.);
let mag = v.length() * (1. + offset.abs() / 2.);
v = Vec2::from_angle(angle).rotate(Vec2::X * mag);
(But the game is published and playable and recognizable as Breakout, so I am proud of that!)
Anyway, worse than the code being bad is that someone told me the bounces felt "wrong". Fixing that would require developing some actual intution about the whole set of possible collision responses. This is the perfect place for a visualizer!
(If that doesn't work for some reason, try ./paddle/)
If you're interested, here's the commit that implements the visualizer. I made it with lots of help from Charles!
That shows the ball's exit velocity based on the "entrance velocity" and the collision point along the paddle.
Some interesting choices present in the visualizer:
- Assume that the ball is going to hit the paddle in exactly one frame. That way all distances shown can also be interpreted as velocities.
- The ball is always moving toward the paddle's origin point. This means that the ball's position also specifies its velocity, so there's no extra input mechansim needed to set the velocity vector.
- The paddle can only move half its width away from the origin point. This makes it easy to get the perfect corner collision. (I think this is an application of infinite movement target size from user interface design.)
We saw immediately that my PI/6 angle limits were too far from horizontal.
Fixing that took a bit of trial and error (and recompiling), but the feedback was much easier to get than with the game itself.
We were also able to have some good game design discussions by putting the visualizer into specific scenarios and pointing at the results.
Also, I haven't fixed it yet, but I think I know why the bounces feel bad! My code causes the ball to tilt slightly toward the side of the paddle that it hit, which lets you send the ball straight upwards if you catch the ball just right enough times. But I think players expect hitting either paddle edge to always make the tilt more severe. That doesn't make any physical sense (not that my code does either), but video games get to tilt away from realism when that makes things more fun!