Wednesday, May 18, 2016

Ball and paddle physics!

One thing that really disappointed me with the write-in game from Pico-8 Zine was the constant angle at which the ball travels. It was good as an introduction to programming goes, because it was so simple, but it was really boring from a gameplay standpoint. If I wanted this game to be something more than just a proof of concept, I had to find a way to improve this mechanic.

This was not an easy task. Finding and implementing the solution took, on and off, couple of days of brainstorming followed by trial and error in Lua. But, with some old school pen&paper math, and a little help of google I managed to get the ball to bounce at different angles, depending which part of the paddle it hits. Now the game requires some strategy on top of reflexes.


***

THE PROCESS

The problem here was to figure out how to change the angle at which the ball travels after hitting the paddle. It was an issue mainly because everything in this game was created using X and Y coordinates, and even if it didn't, as far as I know (and I might be very wrong), Pico-8 doesn't offer a function to move a graphic at an angle. Also, I didn't want to jump into messing with angles just yet. So, I had to find a way to translate angles to coordinates.

Fortunately I remembered that vectors are a thing, so that was my starting point. The game as I had it at that point used value of 3 for both ballxdir and ballydir, which made the ball travel at a 45 degree angle. If I changed one of the values (in my case, X), the angle would change. Sadly this meant that the overall speed would change too, because Y stayed constant. So depending on X, the ball would go either very fast or very slow. Not good.


KEEPING THE SPEED CONSTANT

I managed to solve this by just drawing out the problem on graph paper and looking for some patterns. Right now it is so obvious to me that the speed I was looking for was the hypotenuse of a right triangle with sides equal to  ballxdir and ballydir values. But, at the time, it felt like a great discovery worthy of celebration. From that it was pretty easy to calculate speed and then make the ballydir change to keep that speed, thanks to this Greek guy named Pythagoras, his fancy a2+b2=c2 theorem and the PICO-8 manual which gave me the SQRT function.

ballspeed=sqrt(ballxdir*ballxdir+ballydir*ballydir)
--this is where the
--code to make the ball
--change horizontal direction
--depending on where it hit 
--the paddle would go
ballydir=sqrt(ballspeed*ballspeed-ballxdir*ballxdir)*(-1)
 --multiply by -1 to make 
 --the ball travel up

Now I was able to change the the value of horizontal travel for the ball and keep the speed from going haywire. Now I just needed a good way to generate the horizontal travel value (ballxdir) depending on where the ball hit the paddle. If it hits it dead center, the ball should travel almost straight upwards (exact straight is boring) and the further from the center the ball hits, it should bounce of at a smaller angle (larger ballxdir).

MAKING ANGLED BOUNCES

This is where I once again took out my pen and paper and started drawing. I have split the paddle down the center (so between 12th and 13th pixel, as the paddle is 24 pixels wide). I wanted everything to the left of that dividing line to bounce the ball left (so negative X) and everything on right, right (positive X) The first idea was make the ballxdir equal to how many pixels away from the center of the paddle the ball hit. So 1 at 1 pixel, 2 at 2 and so on. That made the ball go like crazy very quick. I tried to fix it by limiting that value with formula: ballxdir=padw/6-6. That gave me values between -6 and 6, but it was still too much (also generated some bugs with the Pythagorean theorem). Narrowing it down to even 4 with IF functions made big chunks of the sides of the paddle always bounce back at -4 or 4, which was functional, but not much fun. I was getting somewhere, but it wasn't it. So I followed the programming advice I have read multiple times - go and find your answers.

I googled "breakout physics pythagorean theorem" and stumbled upon this gamedev stackexchange topic. It is originally for Java, but the code is explained very well, so I was able to transplant it into my game. Instead of using pixels (a mindset I pushed myself into by thinking of low rez games), the advice there tells you to "calculate the position of the ball relative to the center of the paddle, and express this as a number between -1 and +1". Then, multiply that by current speed so it will give out an horizontal movement that is lesser than the overall speed (so it won't mess up the  pythagorean theorem), but makes the value varied enough to give a crazy huge amount of angles! This worked perfectly for me! I ended up with this code:

 function hitball()
 --check if ball is within paddle
  if ballx>padx-2 and
  ballx<=padx+padw+1 and
  bally>pady-6 and
  ballydir>0 and
  bally>padx+1 then 
  --pitagoran therum keeps
  --overall speed constant
   ballspeed=sqrt(ballxdir*ballxdir+ballydir*ballydir)
   --change ball angle
   --based on where the ball
   --hit the paddle
   padcenterx=padx+padw/2 
   hitx=(ballcenterx-padcenterx)/(padw/2) 
   ballxdir=ballspeed*hitx 
  --speed up the ball  
   ballspeed+=0.05
  --compute ball y direction 
  --to keep speed constant 
   ballydir=sqrt(ballspeed*ballspeed-ballxdir*ballxdir)*(-1)
    sfx(1)
    score+=10
  end 

Now that I am finally happy with the core gameplay mechanic, I can move to introducing some other things into the game...like graphics or enemies. While the former won't be too much of an issue (I already experimented with the ball sprite), the latter will be something completely new to me. I guess I will check out more tutorials and get into source code of some of the Pico-8 games.

Oh, and while we are on the topic of Pico-8 games, I want to give a shout out to Josh Millard from Pico-8 BBS, whose game Arkanoi-8 pushed me to implement better ball physics on my own (in a big part because I don't really understand his advanced code, so I couldn't copy it ). Thanks Josh!

No comments:

Post a Comment