Author Topic: Math help with puck/paddle bounce  (Read 5917 times)

0 Members and 1 Guest are viewing this topic.

Offline STxAxTIC

  • Library Staff
  • Forum Resident
  • Posts: 1091
  • he lives
    • View Profile
Re: Math help with puck/paddle bounce
« Reply #15 on: June 04, 2020, 08:48:10 pm »
Right, now that we're pulling on this thread, the "hidden circle" can be of any radius with a lower limit of twice the paddle height. The upper limit is infinity, in which case the paddle acts the way it looks.

Here is all you have to do, it solves the whole problem:

Decide on a radius of curvature as sketched above.
Create a function that translates a point of contact on the paddle to a point on the imagined circle. This will literally be an arc-trig function.
The normal vector to the paddle, which is usually horizontal, becomes (cos a, sin a), where a is the angle from horizontal.
Reflect the ball velocity about that vector.
Done.

Oh and this is my 777th post.

EDIT: I'm on call and may have to leave at any time but if there is no solution in 24 hours I'm going to do it myself.
« Last Edit: June 04, 2020, 08:57:01 pm by STxAxTIC »
You're not done when it works, you're done when it's right.

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: Math help with puck/paddle bounce
« Reply #16 on: June 04, 2020, 08:51:35 pm »
OH wait, you can vary the angle of return according to how far from center of the paddle the ball is when it hits the paddle. Compare difference between ball y and center of paddle y, the greater the distance the more angle is towards the upper or lower paddle half.
« Last Edit: June 04, 2020, 09:02:07 pm by bplus »

Offline codeguy

  • Forum Regular
  • Posts: 174
    • View Profile
Re: Math help with puck/paddle bounce
« Reply #17 on: June 04, 2020, 08:55:03 pm »
pro tip: sin(2theta) will be your friend and the reflected angle will be the same as the incident angle relative to the tangent line along the circle or ellipse.
« Last Edit: June 04, 2020, 08:56:54 pm by codeguy »

Offline TerryRitchie

  • Seasoned Forum Regular
  • Posts: 495
  • Semper Fidelis
    • View Profile
Re: Math help with puck/paddle bounce
« Reply #18 on: June 04, 2020, 10:14:09 pm »
So to be clear - the paddle needs to look like a paddle and move like a paddle, but reflect the ball as if curved. (This is a one-liner solution with vectors but it seems like bplus has something close to cooked already.)

Yes, exactly. Sorry for the delay, wife came home and had to leave the computer for a while.
In order to understand recursion, one must first understand recursion.

Offline TerryRitchie

  • Seasoned Forum Regular
  • Posts: 495
  • Semper Fidelis
    • View Profile
Re: Math help with puck/paddle bounce
« Reply #19 on: June 04, 2020, 10:15:54 pm »
A more subtle arc like from an ellipse?

I have a Breakout Game that uses a "loaf-of-bread" paddle, LOL. I have to check the math I used for that.

Yes, in my drawing I envisioned the ellipse to go from upper left to lower left passing through the center of the paddle. Not sure if that is accurate but what I envisioned.
In order to understand recursion, one must first understand recursion.

Offline _vince

  • Seasoned Forum Regular
  • Posts: 422
    • View Profile
Re: Math help with puck/paddle bounce
« Reply #20 on: June 05, 2020, 11:42:09 am »
Another tip, don't forget to convert to radians when doing trig in QB64, radians = degrees*pi/180 will be critical

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: Math help with puck/paddle bounce
« Reply #21 on: June 05, 2020, 01:24:06 pm »
Air Hockey
https://www.qb64.org/forum/index.php?topic=2326.0

Uses round paddles and the paddles are not restricted to up and down but anywhere on the players side of the table or center line which is more like a regular game of pong.

The math for handling collision is simple circle intersect circle ie the circle centers are less in distance than the sum of their radii.

The math a little more than simple wall collision but not as bad as ellipse collision will be, which is likely to get terrible and I don't think newbies to QB64 will appreciate unless they are math or physics majors in college. Maybe I could figure out the rebound from an ellipse paddle if I really, really, had too, but yuck!

So I have to ask Terry, are you sure you want to go that far?

In meantime, I will try to explain math for circle versus circle collision and rebound:

Here is code from AirHockey to handle contact with paddle,
it's probably over simplified for STx standards, but does the job fairly well:
Code: QB64: [Select]
  1.     IF SQR((px - psx) ^ 2 + (py - psy) ^ 2) < (pr + pr2) THEN 'puck has penetrated player's striker = collision plus!
  2.         pa = _ATAN2(py - psy, px - psx)  'calc the new direction for puck to go
  3.         'boost puck away
  4.         px = px + .5 * speed * COS(pa) 'calc new position for puck, back it out of paddle
  5.         py = py + .5 * speed * SIN(pa)
  6.         snd 2200, 4
  7.     END IF
  8.  

Not too scary is it?

Here is collision detection line: IF SQR((px - psx) ^ 2 + (py - psy) ^ 2) < (pr + pr2) THEN
px, py the puck location
psx, psy the player striker location

This line determines the angle of the puck center to the player striker center:
pa = _ATAN2(py - psy, px - psx)
Important: this also resets pa, the puck angle, which is the direction in radians the puck is traveling. With round paddles the angle the puck hits the striker is the angle the puck rebounds! simple beauty math-wise.

This resets the puck location (anywhere in code eg starting drawPuck sub):
        px = px + .5 * speed * COS(pa)
        py = py + .5 * speed * SIN(pa)

The x component of the speed and heading of the puck,
My dx or Terry's xv = .5 * speed * COS(pa) or new location for x component = old x + the speed times COS of the puck angle
My dy or Terry's yv = .5 * speed * SIN(pa)  or new location for y component = old y + the speed times SIN of the puck angle


PS where I used an ellipse paddle, the loaf of bread thing in Breakout game, I used random rebounding, which is not bad way to go specially if the randomness is increased the more off center the ball strikes the paddle. You might get ellipse like rebounding without allot of math, IF you can make the random rebound increase in the direction of the reflection.

Update #1: The .5 in xv = .5 * speed * COS(pa)
.5 is the average distance the ball will have sunk into the paddle ie somewhere between (0 and  1) * COS(pa) likewise for the y component of the change in position. (Not having studied my code for awhile, I had to figure out again WTH the .5 was for?! LOL)
« Last Edit: June 05, 2020, 01:54:05 pm by bplus »

Offline STxAxTIC

  • Library Staff
  • Forum Resident
  • Posts: 1091
  • he lives
    • View Profile
Re: Math help with puck/paddle bounce
« Reply #22 on: June 05, 2020, 02:30:42 pm »
Ellipses are not needed. See my post 777 otherwise I'm stuck on the road a while
You're not done when it works, you're done when it's right.

Offline TempodiBasic

  • Forum Resident
  • Posts: 1792
    • View Profile
Re: Math help with puck/paddle bounce
« Reply #23 on: June 05, 2020, 07:41:06 pm »
@TerryRitchie
can be this useful?
  [ You are not allowed to view this attachment ]  
....elastic collision in physic
Programming isn't difficult, only it's  consuming time and coffee

Offline STxAxTIC

  • Library Staff
  • Forum Resident
  • Posts: 1091
  • he lives
    • View Profile
Re: Math help with puck/paddle bounce
« Reply #24 on: June 05, 2020, 08:31:12 pm »
Okay I've cracked the main problem carrying out the steps I laid out above. I did this by writing the following code in the MovePuck sub:

Code: QB64: [Select]
  1.             '''
  2.             ' Velocity vector using ordinary orientation.
  3.             vx = Puck.xv
  4.             vy = Puck.yv
  5.  
  6.             ' Find place on paddle where puck is hitting as fraction from midpoint. Result in interval [-1,1]
  7.             hy = 2 * (Puck.y - P1Paddle.y) / (P1Paddle.rect.y1 - P1Paddle.rect.y2)
  8.  
  9.             ' Convert fractional height to an angle. Result in interval [-pi/2,pi/2]
  10.             ang = _ASIN(hy)
  11.  
  12.             ' Create unit vectors based on imaginary circle.
  13.             rux = COS(ang)
  14.             ruy = SIN(ang)
  15.             tux = -SIN(ang)
  16.             tuy = COS(ang)
  17.  
  18.             ' Re-calculate velocity vector in new basis.
  19.             wr = vx * rux + vy * ruy
  20.             wt = vx * tux + vy * tuy
  21.  
  22.             ' Reflect the velocity about the normal vector
  23.             wt = -wt
  24.  
  25.             ' Re-calculate veloctiy in old basis.
  26.             vx = wr * COS(ang) - wt * SIN(ang)
  27.             vy = wr * SIN(ang) + wt * COS(ang)
  28.  
  29.             ' Restore original variables
  30.             Puck.xv = vx
  31.             Puck.yv = vy
  32.             '''


For maximal clarity, this is the exact implementation Terry was trying to draw in the top post. The paddle is still a rectangle, but reflects the ball exactly as if it were a circle. I modified the main code to get rid of the random y-velocity and to toss the ball at player 1, as this code is only implemented for player 1.

Near the edges, the ball reflects wildly. Of course, this is what you get for a circle. As I was saying above, this effect can be made less drastic by making the invisible circle bigger.

Anyway, here's the whole thing:

Code: QB64: [Select]
  1. 'OPTION _EXPLICIT
  2.  
  3. '-------------------------------------------------------------------------------------------------------------
  4. '** Declare constants
  5. '-------------------------------------------------------------------------------------------------------------
  6.  
  7. CONST FALSE = 0, TRUE = NOT FALSE '               truth detectors
  8. CONST BRIGHTWHITE = _RGB32(255, 255, 255) '       define colors
  9. CONST WHITE = _RGB32(192, 192, 192)
  10. CONST GRAY = _RGB32(128, 128, 128) '
  11. CONST SCREENWIDTH = 768 '                         screen width
  12. CONST SCREENHEIGHT = 576 '                        screen height
  13. CONST PUCKSIZE = 10 '                             puck square size
  14. CONST PADDLEWIDTH = 10 '                          paddle width
  15. CONST PADDLEHEIGHT = 60 '                         paddle height
  16. CONST PADDLESPEED = 2 '                           paddle Y speed
  17. CONST PADDLEOFFSET = 20 '                         paddle offset from side of screen
  18. CONST P1MISSED = PUCKSIZE \ 2 '                   puck on left side edge of screen
  19. CONST P2MISSED = SCREENWIDTH - 1 - PUCKSIZE \ 2 ' puck on right side edge of screen
  20. CONST PADDLEYMIN = PADDLEHEIGHT \ 2 + 30 '                                     min Y coordinate for paddles
  21. CONST PADDLEYMAX = SCREENHEIGHT - 30 - PADDLEHEIGHT \ 2 '                      max Y coordinate for paddles
  22. CONST PUCKXMIN = PADDLEOFFSET + PADDLEWIDTH + PUCKSIZE \ 2 '                   min X coordinate for puck
  23. CONST PUCKXMAX = SCREENWIDTH - 1 - PADDLEOFFSET - PADDLEWIDTH - PUCKSIZE \ 2 ' max X coordinate for puck
  24. CONST PUCKYMIN = 30 + PUCKSIZE \ 2 '                                           min Y coordinate for puck
  25. CONST PUCKYMAX = SCREENHEIGHT - 30 - PUCKSIZE \ 2 '                            max Y coordinate for puck
  26. CONST FPS = 300 '                                                              game frames per second
  27.  
  28. '-------------------------------------------------------------------------------------------------------------
  29. '** Declare TYPE definitions
  30. '-------------------------------------------------------------------------------------------------------------
  31.  
  32. TYPE RECTTYPE '        rectangle definition for collision detection
  33.     x1 AS INTEGER '    upper left X coordinate
  34.     y1 AS INTEGER '    upper left Y coordinate
  35.     x2 AS INTEGER '    lower right X coordinate
  36.     y2 AS INTEGER '    lower right Y coordinate
  37.  
  38. TYPE MOVINGOBJECT '    moving object definition
  39.     x AS SINGLE '      center X coordinate
  40.     y AS SINGLE '      center Y coordinate
  41.     xv AS SINGLE '     X vector component
  42.     yv AS SINGLE '     Y vector component
  43.     rect AS RECTTYPE ' rectangle coordinates for collision detection
  44.  
  45. '-------------------------------------------------------------------------------------------------------------
  46. '** Declare global variables
  47. '-------------------------------------------------------------------------------------------------------------
  48.  
  49. DIM PlayField AS LONG '        playing field image
  50. DIM PlayFieldCopy AS LONG '    playing field image copy (without scores)
  51. DIM P1Paddle AS MOVINGOBJECT ' player 1 paddle
  52. DIM P2Paddle AS MOVINGOBJECT ' player 2 paddle
  53. DIM Puck AS MOVINGOBJECT '     puck properties
  54. DIM Computer AS INTEGER '      TRUE if computer playing
  55. DIM Score(9) AS STRING * 15 '  encoded font data
  56. DIM Pscore(1) AS INTEGER '     player scores (0=player1, 1=player2
  57. DIM Missed AS INTEGER '        TRUE when a player misses the puck
  58. DIM GameOver AS INTEGER '      TRUE when a player reaches a score of 9
  59.  
  60. '-------------------------------------------------------------------------------------------------------------
  61. '** Begin main program
  62. '-------------------------------------------------------------------------------------------------------------
  63.  
  64. SCREEN _NEWIMAGE(SCREENWIDTH, SCREENHEIGHT, 32) ' enter graphics screen
  65. _TITLE "QB64 PONG!" '                             give window a title
  66. _DELAY .5 '                                       slight delay
  67. _SCREENMOVE _MIDDLE '                             move window to center of desktop
  68. Initialize '                                      initialize game settings
  69. DO '                                              begin main program loop
  70.     NewGame '                                     start a new game
  71.     DO '                                          begin game loop
  72.         PlayGame '                                play the game
  73.     LOOP UNTIL GameOver OR _KEYDOWN(27) '         leave when game over or player exits
  74. LOOP UNTIL _KEYDOWN(27) '                         leave when player exits
  75. Cleanup '                                         clean player's computer memory and exit to operating system
  76.  
  77. '-------------------------------------------------------------------------------------------------------------
  78. '** end main program
  79. '-------------------------------------------------------------------------------------------------------------
  80.  
  81. '-------------------------------------------------------------------------------------------------------------
  82. '** Declare subroutines and functions
  83. '-------------------------------------------------------------------------------------------------------------
  84.  
  85. '-------------------------------------------------------------------------------------------------------------
  86. SUB ResetRound ()
  87.     '---------------------------------------------------------------------------------------------------------
  88.  
  89.     SHARED Missed AS INTEGER '    need access to puck miss indicator
  90.     SHARED Puck AS MOVINGOBJECT ' need access to puck
  91.  
  92.     IF SGN(Puck.xv) = 1 THEN '                          puck was heading right?
  93.         Puck.x = SCREENWIDTH * .25 '                    yes, serve from player 1 side
  94.         Puck.xv = .5 '                                  puck will travel right
  95.     ELSE '                                              no, puck was travleing left
  96.         Puck.x = SCREENWIDTH * .75 '                    serve from player 2 side
  97.         Puck.xv = -.5 '                                 puck will travel left
  98.     END IF
  99.     Puck.y = INT(RND(1) * (SCREENHEIGHT - 100)) + 50 '  random puck Y coordinate
  100.     Puck.yv = 0 '(RND(1) - RND(1)) / 2 '                   random puck Y vector component
  101.     Missed = FALSE '                                    reset puck missed indicator
  102.  
  103.  
  104. '-------------------------------------------------------------------------------------------------------------
  105. SUB PlayGame ()
  106.     '---------------------------------------------------------------------------------------------------------
  107.  
  108.     SHARED PlayField AS LONG '    need access to playing field image
  109.     SHARED Missed AS INTEGER '    need access to puck miss indicator
  110.     SHARED Puck AS MOVINGOBJECT ' need access to puck
  111.     SHARED Pscore() AS INTEGER '  need access to player scores
  112.     SHARED GameOver AS INTEGER '  need access to game over indicator
  113.  
  114.     ResetRound '                                             set up next game round
  115.     DO '                                                     begin round loop
  116.         _LIMIT FPS '                                         limit game speed
  117.         _PUTIMAGE , PlayField '                              reset playing field
  118.         MovePaddles '                                        update player paddles
  119.         MovePuck '                                           update puck
  120.         _DISPLAY '                                           update screen with changes
  121.     LOOP UNTIL Missed OR _KEYDOWN(27) '                      leave when puck missed or a player presses ESC
  122.     IF SGN(Puck.xv) = -1 THEN '                              was puck heading left?
  123.         Pscore(1) = Pscore(1) + 1 '                          yes, increase player 2 score
  124.     ELSE '                                                   no, puck was heading right
  125.         Pscore(0) = Pscore(0) + 1 '                          increase player 1 score
  126.     END IF
  127.     IF Pscore(0) = 9 OR Pscore(1) = 9 THEN GameOver = TRUE ' set game over indicator when player reaches 9
  128.     UpdateScores '                                           update player scores
  129.  
  130.  
  131. '-------------------------------------------------------------------------------------------------------------
  132. SUB UpdateScores ()
  133.     '---------------------------------------------------------------------------------------------------------
  134.  
  135.     SHARED PlayField AS LONG '     need access to playing field image
  136.     SHARED PlayFieldCopy AS LONG ' need access to playing field image copy
  137.     SHARED Pscore() AS INTEGER '   need access to player scores
  138.     DIM Clr AS _UNSIGNED LONG '    font color
  139.  
  140.     _PUTIMAGE , PlayFieldCopy, PlayField '                      restore playing field
  141.     _DEST PlayField '                                           make playing field image the destination
  142.     IF Pscore(0) = 9 THEN Clr = BRIGHTWHITE ELSE Clr = WHITE '  set color of font
  143.     DrawScore SCREENWIDTH \ 2 - 100, 50, Pscore(0), Clr '       draw player 1's score
  144.     IF Pscore(1) = 9 THEN Clr = BRIGHTWHITE ELSE Clr = WHITE '  set color of font
  145.     DrawScore SCREENWIDTH \ 2 + 55, 50, Pscore(1), Clr '        draw player 2's score
  146.     _DEST 0 '                                                   set destination back to screen
  147.  
  148.  
  149. '-------------------------------------------------------------------------------------------------------------
  150. SUB DrawScore (dx AS INTEGER, dy AS INTEGER, n AS INTEGER, clr AS _UNSIGNED LONG)
  151.     '---------------------------------------------------------------------------------------------------------
  152.  
  153.     SHARED Score() AS STRING * 15 ' need access to encoded font data
  154.     DIM x AS INTEGER '              font column counter
  155.     DIM y AS INTEGER '              font row counter
  156.     DIM p AS INTEGER '              string position counter
  157.  
  158.     p = 0 '                                       reset string position counter
  159.     FOR y = 0 TO 4 '                              cycle through 5 rows
  160.         FOR x = 0 TO 2 '                          cycle through 3 columns
  161.             p = p + 1 '                           increment string position counter
  162.             IF MID$(Score(n), p, 1) <> " " THEN ' character in this position?
  163.                 LINE (dx + x * 15, dy + y * 15)-(dx + x * 15 + 15, dy + y * 15 + 15), clr, BF ' yes, draw cube
  164.             END IF
  165.     NEXT x, y '                                   leave when all rows and columns processed
  166.  
  167.  
  168. '-------------------------------------------------------------------------------------------------------------
  169. SUB MovePuck ()
  170.     '---------------------------------------------------------------------------------------------------------
  171.  
  172.     SHARED P1Paddle AS MOVINGOBJECT ' need access to player 1 paddle
  173.     SHARED P2Paddle AS MOVINGOBJECT ' need access to player 2 paddle
  174.     SHARED Puck AS MOVINGOBJECT '     need access to puck
  175.     SHARED Missed AS INTEGER '        need access to puck miss indicator
  176.     SHARED Pscore() AS INTEGER '      need access to player scores
  177.  
  178.     Puck.x = Puck.x + Puck.xv '            add X vector component to X coordinate
  179.     Puck.y = Puck.y + Puck.yv '            add Y vector component to Y coordinate
  180.     Puck.rect.x1 = Puck.x - PUCKSIZE \ 2 ' calculate puck rectangle coordinates
  181.     Puck.rect.y1 = Puck.y - PUCKSIZE \ 2
  182.     Puck.rect.x2 = Puck.x + PUCKSIZE \ 2
  183.     Puck.rect.y2 = Puck.y + PUCKSIZE \ 2
  184.     IF Puck.x < PUCKXMIN OR Puck.x > PUCKXMAX THEN '                             is puck within a paddle area?
  185.         IF SGN(Puck.xv) = -1 AND RectCollide(Puck.rect, P1Paddle.rect) THEN '    going left and hit paddle?
  186.  
  187.             '''
  188.             ' Velocity vector using ordinary orientation.
  189.             vx = Puck.xv
  190.             vy = Puck.yv
  191.  
  192.             ' Find place on paddle where puck is hitting as fraction from midpoint. Result in interval [-1,1]
  193.             hy = 2 * (Puck.y - P1Paddle.y) / (P1Paddle.rect.y1 - P1Paddle.rect.y2)
  194.  
  195.             ' Convert fractional height to an angle. Result in interval [-pi/2,pi/2]
  196.             ang = _ASIN(hy)
  197.  
  198.             ' Create unit vectors based on imaginary circle.
  199.             rux = COS(ang)
  200.             ruy = SIN(ang)
  201.             tux = -SIN(ang)
  202.             tuy = COS(ang)
  203.  
  204.             ' Re-calculate velocity vector in new basis.
  205.             wr = vx * rux + vy * ruy
  206.             wt = vx * tux + vy * tuy
  207.  
  208.             ' Reflect the velocity about the normal vector
  209.             wt = -wt
  210.  
  211.             ' Re-calculate veloctiy in old basis.
  212.             vx = wr * COS(ang) - wt * SIN(ang)
  213.             vy = wr * SIN(ang) + wt * COS(ang)
  214.  
  215.             ' Restore original variables
  216.             Puck.xv = vx
  217.             Puck.yv = vy
  218.             '''
  219.  
  220.             Puck.xv = -Puck.xv + .025 '                                          yes, reverse X vector
  221.             Puck.x = PUCKXMIN '                                                  place puck to right of paddle
  222.  
  223.             '** change in Y vector component here
  224.  
  225.             SOUND 440, 1 '                                                       audio report
  226.         ELSEIF SGN(Puck.xv) = 1 AND RectCollide(Puck.rect, P2Paddle.rect) THEN ' going right and hit paddle?
  227.             Puck.xv = -Puck.xv - .025 '                                          yes, reverse X vector
  228.             Puck.x = PUCKXMAX '                                                  place puck to left of paddle
  229.  
  230.             '** change in Y vector component here
  231.  
  232.             SOUND 440, 1 '                                                       audio report
  233.         END IF
  234.     END IF
  235.     IF Puck.y < PUCKYMIN OR Puck.y > PUCKYMAX THEN '                             is puck hitting a side line?
  236.         IF SGN(Puck.yv) = -1 THEN Puck.y = PUCKYMIN ELSE Puck.y = PUCKYMAX '     yes, place puck above/below
  237.         Puck.yv = -Puck.yv '                                                     reverse Y vector
  238.         SOUND 880, 1 '                                                           audio report
  239.     END IF
  240.     IF Puck.x < P1MISSED OR Puck.x > P2MISSED THEN '                             did puck hit edge of screen?
  241.         Missed = TRUE '                                                          yes, set missed indicator
  242.         SOUND 220, 1 '                                                           audio report
  243.     END IF
  244.     LINE (Puck.rect.x1, Puck.rect.y1)-(Puck.rect.x2, Puck.rect.y2), BRIGHTWHITE, BF ' draw puck
  245.  
  246.  
  247. '-------------------------------------------------------------------------------------------------------------
  248. SUB MovePaddles ()
  249.     '---------------------------------------------------------------------------------------------------------
  250.  
  251.     SHARED P1Paddle AS MOVINGOBJECT ' need access to player 1 paddle
  252.     SHARED P2Paddle AS MOVINGOBJECT ' need access to player 2 paddle
  253.     SHARED Puck AS MOVINGOBJECT '     need access to puck
  254.     SHARED Computer AS INTEGER '      need access to computer opponent
  255.  
  256.     IF _KEYDOWN(87) OR _KEYDOWN(119) THEN '                       player 1 pressing W or w keys?
  257.         P1Paddle.y = P1Paddle.y - PADDLESPEED '                   yes, move paddle up
  258.         IF P1Paddle.y < PADDLEYMIN THEN P1Paddle.y = PADDLEYMIN ' keep paddle in playing field
  259.     ELSEIF _KEYDOWN(83) OR _KEYDOWN(115) THEN '                   player 1 pressing S or s keys?
  260.         P1Paddle.y = P1Paddle.y + PADDLESPEED '                   yes, move paddle down
  261.         IF P1Paddle.y > PADDLEYMAX THEN P1Paddle.y = PADDLEYMAX ' keep paddle in playing field
  262.     END IF
  263.     IF Computer THEN '                                            is player 1 playing computer?
  264.         IF SGN(Puck.xv) = 1 THEN '                                yes, is puck heading for computer?
  265.             IF Puck.y - P2Paddle.y < 0 THEN '                     yes, is puck higher than center of paddle?
  266.                 P2Paddle.y = P2Paddle.y - .5 - RND(1) / 3 '       yes, move computer paddle up
  267.                 IF P2Paddle.y < PADDLEYMIN THEN P2Paddle.y = PADDLEYMIN ' keep paddle in playing field
  268.             ELSE '                                                no, puck is below center of paddle
  269.                 P2Paddle.y = P2Paddle.y + .5 + RND(1) / 3 '       move computer paddle down
  270.                 IF P2Paddle.y > PADDLEYMAX THEN P2Paddle.y = PADDLEYMAX ' keep paddle in playing field
  271.             END IF
  272.         END IF
  273.     ELSEIF _KEYDOWN(18432) THEN '                                 player 2 pressing up arrow key?
  274.         P2Paddle.y = P2Paddle.y - PADDLESPEED '                   yes, move paddle up
  275.         IF P2Paddle.y < PADDLEYMIN THEN P2Paddle.y = PADDLEYMIN ' keep paddle in playing field
  276.     ELSEIF _KEYDOWN(20480) THEN '                                 player 2 pressing down arrow key?
  277.         P2Paddle.y = P2Paddle.y + PADDLESPEED '                   yes, move paddle down
  278.         IF P2Paddle.y > PADDLEYMAX THEN P2Paddle.y = PADDLEYMAX ' keep paddle in playing field
  279.     END IF
  280.     P1Paddle.rect.y1 = P1Paddle.y - PADDLEHEIGHT \ 2 '            calc player 1 paddle rectangle
  281.     P1Paddle.rect.y2 = P1Paddle.y + PADDLEHEIGHT \ 2
  282.     P2Paddle.rect.y1 = P2Paddle.y - PADDLEHEIGHT \ 2 '            calc player 2 paddle rectangle
  283.     P2Paddle.rect.y2 = P2Paddle.y + PADDLEHEIGHT \ 2
  284.     LINE (P1Paddle.rect.x1, P1Paddle.rect.y1)-(P1Paddle.rect.x2, P1Paddle.rect.y2), BRIGHTWHITE, BF ' draw
  285.     LINE (P2Paddle.rect.x1, P2Paddle.rect.y1)-(P2Paddle.rect.x2, P2Paddle.rect.y2), BRIGHTWHITE, BF ' paddles
  286.  
  287.  
  288. '-------------------------------------------------------------------------------------------------------------
  289. SUB NewGame ()
  290.     '---------------------------------------------------------------------------------------------------------
  291.  
  292.     SHARED P1Paddle AS MOVINGOBJECT ' need access toplayer 1 paddle
  293.     SHARED P2Paddle AS MOVINGOBJECT ' need access to player 2 paddle
  294.     SHARED Puck AS MOVINGOBJECT '     need access to puck
  295.     SHARED PlayField AS LONG '        need access to playing field image
  296.     SHARED Pscore() AS INTEGER '      need access to player scores
  297.     SHARED Missed AS INTEGER '        need access to puck miss indicator
  298.     SHARED GameOver AS INTEGER '      need access to game over indicator
  299.     SHARED Computer AS INTEGER '      need access to computer opponent
  300.     DIM Blink AS INTEGER '            blink bit
  301.     DIM NumPlayers AS INTEGER '       number of players selected
  302.     DIM Frames AS INTEGER '           frame counter
  303.  
  304.     P1Paddle.y = SCREENHEIGHT \ 2 '                                                reset paddle locations
  305.     P2Paddle.y = P1Paddle.y
  306.     Puck.xv = -1 '.5 '                                                                 reset puck X speed
  307.     Puck.y = SCREENHEIGHT \ 2
  308.     Missed = FALSE '                                                               reset missed indicator
  309.     GameOver = FALSE '                                                             reset game over indicator
  310.     Computer = FALSE '                                                             player 2 is human
  311.     SOUND 440, 1 '                                                                 new game audio
  312.     SOUND 880, 1
  313.     DO '                                                                           begin setup loop
  314.         _LIMIT FPS '                                                               limit frame speed
  315.         _PUTIMAGE , PlayField '                                                    restore playing field
  316.         MovePaddles '                                                              allow paddle movement
  317.         Frames = Frames + 1 '                                                      increment frame counter
  318.         IF Frames = FPS THEN '                                                     max frames reached?
  319.             Frames = 0 '                                                           yes, reset frame counter
  320.             Blink = 1 - Blink '                                                    toggle blink bit
  321.         END IF
  322.         LOCATE (SCREENHEIGHT \ 16), ((SCREENWIDTH \ 8) - 51) \ 2 + 1 '             position cursor on screen
  323.         SELECT CASE Blink '                                                        which blinker bit?
  324.             CASE 0 '                                                               blinker bit 0
  325.                 IF NumPlayers = 0 THEN '                                           number of players chosen?
  326.                     PRINT "    SELECT (1) OR (2) PLAYERS NOW : ESC TO EXIT    "; ' no, display player select
  327.                 ELSE '                                                             yes
  328.                     PRINT "(1) OR (2) PLAYERS : SPACEBAR TO PLAY : ESC TO EXIT"; ' display all instructions
  329.                 END IF
  330.             CASE 1 '                                                               blinker bit 1
  331.                 PRINT SPACE$(51); '                                                remove instructions
  332.         END SELECT
  333.         LOCATE (SCREENHEIGHT \ 16) - 2, ((SCREENWIDTH \ 16) - 27) \ 2 + 1 '        position cursor on screen
  334.         PRINT "PLAYER 1: W = UP / S = DOWN" '                                      display keys to player 1
  335.         LOCATE (SCREENHEIGHT \ 16) - 2, ((SCREENWIDTH \ 8) - 31) \ 2 + (SCREENWIDTH \ 32) + 1 'position cursor
  336.         SELECT CASE NumPlayers '                                                   how many players selected?
  337.             CASE 0: PRINT " SELECT (1) OR (2) PLAYERS NOW " '                      ask number of players
  338.             CASE 1: PRINT " PLAYER 2: THE CPU HAS CONTROL " '                      inform computer playing
  339.             CASE 2: PRINT "PLAYER 2: UP ARROW / DOWN ARROW" '                      display keys player 2
  340.         END SELECT
  341.         IF NumPlayers <> 1 THEN '                                                  has one player been chosen?
  342.             IF _KEYDOWN(49) THEN '                                                 no, "1" key been pressed?
  343.                 NumPlayers = 1 '                                                   yes, set players to 1
  344.                 Computer = TRUE '                                                  remember AI now has control
  345.                 SOUND 440, 1 '                                                     play affirmation beep
  346.                 SOUND 880, 1 '                                                     play affirmation beep
  347.             END IF
  348.         END IF
  349.         IF NumPlayers <> 2 THEN '                                                  has two player been chosen?
  350.             IF _KEYDOWN(50) THEN '                                                 no, "2" key been pressed?
  351.                 NumPlayers = 2 '                                                   yes, set players to 2
  352.                 Computer = FALSE '                                                 human is playing human
  353.                 SOUND 440, 1 '                                                     play affirmation beep
  354.                 SOUND 880, 1 '                                                     play affirmation beep
  355.             END IF
  356.         END IF
  357.         _DISPLAY '                                                                 update screen with changes
  358.     LOOP UNTIL (_KEYDOWN(32) AND NumPlayers) OR _KEYDOWN(27) '                     leave when players select
  359.     Pscore(0) = 0 '                                                                reset scores
  360.     Pscore(1) = 0
  361.     UpdateScores '                                                                 update on screen scores
  362.  
  363.  
  364. '-------------------------------------------------------------------------------------------------------------
  365. SUB Initialize ()
  366.     '---------------------------------------------------------------------------------------------------------
  367.  
  368.     SHARED PlayField AS LONG '        need access to playing field image
  369.     SHARED PlayFieldCopy AS LONG '    need access to playing field image copy
  370.     SHARED P1Paddle AS MOVINGOBJECT ' need access to player 1 paddle
  371.     SHARED P2Paddle AS MOVINGOBJECT ' need access to player 2 paddle
  372.     SHARED Puck AS MOVINGOBJECT '     need access to puck
  373.     SHARED Score() AS STRING * 15 '   need access to encoded font data
  374.     DIM y AS INTEGER '                center line counter
  375.  
  376.     RANDOMIZE TIMER '                                               seed random number generator
  377.  
  378.     '** do as many precalculations as possible for values that will not change
  379.  
  380.     P1Paddle.x = PADDLEOFFSET + PADDLEWIDTH \ 2 '                   calculate player 1 paddle X coordinate
  381.     P2Paddle.x = SCREENWIDTH - 1 - PADDLEOFFSET - PADDLEWIDTH \ 2 ' calculate player 2 paddle X coordinate
  382.     P1Paddle.rect.x1 = P1Paddle.x - PADDLEWIDTH \ 2 '               calculate player 1 paddle rectangle
  383.     P1Paddle.rect.x2 = P1Paddle.x + PADDLEWIDTH \ 2
  384.     P2Paddle.rect.x1 = P2Paddle.x - PADDLEWIDTH \ 2 '               calculate player 2 paddle rectangle
  385.     P2Paddle.rect.x2 = P2Paddle.x + PADDLEWIDTH \ 2
  386.  
  387.     '** draw default playing field
  388.  
  389.     PlayField = _NEWIMAGE(SCREENWIDTH, SCREENHEIGHT, 32) '          create image
  390.     _DEST PlayField '                                               make it destination
  391.     CLS '                                                           clear image
  392.     LINE (0, 19)-(SCREENWIDTH - 1, 29), BRIGHTWHITE, BF '           draw top side line
  393.     LINE (0, SCREENHEIGHT - 30)-(SCREENWIDTH - 1, SCREENHEIGHT - 20), BRIGHTWHITE, BF ' draw bottom side line
  394.     FOR y = 40 TO SCREENHEIGHT - 52 STEP 30 '                       cycle top to bottom
  395.         LINE ((SCREENWIDTH \ 2) - 5, y)-((SCREENWIDTH \ 2) + 5, y + 15), GRAY, BF '     draw center lines
  396.     NEXT y
  397.     _DEST 0 '                                                       destination back to screen
  398.     PlayFieldCopy = _COPYIMAGE(PlayField) '                         save a copy of the playing field image
  399.     Score(0) = "0000 00 00 0000" '                                  create numeric font data
  400.     Score(1) = "  1  1  1  1  1"
  401.     Score(2) = "222  22222  222"
  402.     Score(3) = "333  3333  3333"
  403.     Score(4) = "4 44 4444  4  4"
  404.     Score(5) = "5555  555  5555"
  405.     Score(6) = "6666  6666 6666"
  406.     Score(7) = "777  7  7  7  7"
  407.     Score(8) = "8888 88888 8888"
  408.     Score(9) = "9999 9999  9999"
  409.  
  410.  
  411. '-------------------------------------------------------------------------------------------------------------
  412. FUNCTION RectCollide (Rect1 AS RECTTYPE, Rect2 AS RECTTYPE)
  413.     '---------------------------------------------------------------------------------------------------------
  414.  
  415.     RectCollide = FALSE '                       assume no collision
  416.     IF Rect1.x2 >= Rect2.x1 THEN '              rect 1 lower right X >= rect 2 upper left  X ?
  417.         IF Rect1.x1 <= Rect2.x2 THEN '          rect 1 upper left  X <= rect 2 lower right x ?
  418.             IF Rect1.y2 >= Rect2.y1 THEN '      rect 1 lower right Y >= rect 2 upper left  Y ?
  419.                 IF Rect1.y1 <= Rect2.y2 THEN '  rect 1 upper left  Y <= rect 2 lower right Y ?
  420.                     RectCollide = TRUE '        if all 4 IFs TRUE then a collision is occurring
  421.                 END IF
  422.             END IF
  423.         END IF
  424.     END IF
  425.  
  426.  
  427. '-------------------------------------------------------------------------------------------------------------
  428. SUB Cleanup ()
  429.     '---------------------------------------------------------------------------------------------------------
  430.  
  431.     SHARED PlayField AS LONG '     need access to playing field image
  432.     SHARED PlayFieldCopy AS LONG ' need access to playing field image copy
  433.  
  434.     _FREEIMAGE PlayField '         free images from memory
  435.     _FREEIMAGE PlayFieldCopy
  436.     SYSTEM '                       return to operating system
  437.  
  438.  
  439. '-------------------------------------------------------------------------------------------------------------
  440. FUNCTION Yvector (PaddleY AS INTEGER, PuckY AS INTEGER, PuckYV)
  441.     '---------------------------------------------------------------------------------------------------------
  442.  
  443.  
  444.     '** math here (ugh)
  445.  
  446.  
  447.  
  448.  
You're not done when it works, you're done when it's right.

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: Math help with puck/paddle bounce
« Reply #25 on: June 05, 2020, 10:35:13 pm »
Here is plain old randomness:
Code: QB64: [Select]
  1.  
  2. '-------------------------------------------------------------------------------------------------------------
  3. '** Declare constants
  4. '-------------------------------------------------------------------------------------------------------------
  5.  
  6. CONST FALSE = 0, TRUE = NOT FALSE '               truth detectors
  7. CONST BRIGHTWHITE = _RGB32(255, 255, 255) '       define colors
  8. CONST WHITE = _RGB32(192, 192, 192)
  9. CONST GRAY = _RGB32(128, 128, 128) '
  10. CONST SCREENWIDTH = 768 '                         screen width
  11. CONST SCREENHEIGHT = 576 '                        screen height
  12. CONST PUCKSIZE = 10 '                             puck square size
  13. CONST PADDLEWIDTH = 10 '                          paddle width
  14. CONST PADDLEHEIGHT = 60 '                         paddle height
  15. CONST PADDLESPEED = 2 '                           paddle Y speed
  16. CONST PADDLEOFFSET = 20 '                         paddle offset from side of screen
  17. CONST P1MISSED = PUCKSIZE \ 2 '                   puck on left side edge of screen
  18. CONST P2MISSED = SCREENWIDTH - 1 - PUCKSIZE \ 2 ' puck on right side edge of screen
  19. CONST PADDLEYMIN = PADDLEHEIGHT \ 2 + 30 '                                     min Y coordinate for paddles
  20. CONST PADDLEYMAX = SCREENHEIGHT - 30 - PADDLEHEIGHT \ 2 '                      max Y coordinate for paddles
  21. CONST PUCKXMIN = PADDLEOFFSET + PADDLEWIDTH + PUCKSIZE \ 2 '                   min X coordinate for puck
  22. CONST PUCKXMAX = SCREENWIDTH - 1 - PADDLEOFFSET - PADDLEWIDTH - PUCKSIZE \ 2 ' max X coordinate for puck
  23. CONST PUCKYMIN = 30 + PUCKSIZE \ 2 '                                           min Y coordinate for puck
  24. CONST PUCKYMAX = SCREENHEIGHT - 30 - PUCKSIZE \ 2 '                            max Y coordinate for puck
  25. CONST FPS = 300 '                                                              game frames per second
  26.  
  27. '-------------------------------------------------------------------------------------------------------------
  28. '** Declare TYPE definitions
  29. '-------------------------------------------------------------------------------------------------------------
  30.  
  31. TYPE RECTTYPE '        rectangle definition for collision detection
  32.     x1 AS INTEGER '    upper left X coordinate
  33.     y1 AS INTEGER '    upper left Y coordinate
  34.     x2 AS INTEGER '    lower right X coordinate
  35.     y2 AS INTEGER '    lower right Y coordinate
  36.  
  37. TYPE MOVINGOBJECT '    moving object definition
  38.     x AS SINGLE '      center X coordinate
  39.     y AS SINGLE '      center Y coordinate
  40.     xv AS SINGLE '     X vector component
  41.     yv AS SINGLE '     Y vector component
  42.     rect AS RECTTYPE ' rectangle coordinates for collision detection
  43.  
  44. '-------------------------------------------------------------------------------------------------------------
  45. '** Declare global variables
  46. '-------------------------------------------------------------------------------------------------------------
  47.  
  48. DIM PlayField AS LONG '        playing field image
  49. DIM PlayFieldCopy AS LONG '    playing field image copy (without scores)
  50. DIM P1Paddle AS MOVINGOBJECT ' player 1 paddle
  51. DIM P2Paddle AS MOVINGOBJECT ' player 2 paddle
  52. DIM Puck AS MOVINGOBJECT '     puck properties
  53. DIM Computer AS INTEGER '      TRUE if computer playing
  54. DIM Score(9) AS STRING * 15 '  encoded font data
  55. DIM Pscore(1) AS INTEGER '     player scores (0=player1, 1=player2
  56. DIM Missed AS INTEGER '        TRUE when a player misses the puck
  57. DIM GameOver AS INTEGER '      TRUE when a player reaches a score of 9
  58.  
  59. '-------------------------------------------------------------------------------------------------------------
  60. '** Begin main program
  61. '-------------------------------------------------------------------------------------------------------------
  62.  
  63. SCREEN _NEWIMAGE(SCREENWIDTH, SCREENHEIGHT, 32) ' enter graphics screen
  64. _TITLE "QB64 PONG!" '                             give window a title
  65. _DELAY .5 '                                       slight delay
  66. _SCREENMOVE _MIDDLE '                             move window to center of desktop
  67. Initialize '                                      initialize game settings
  68. DO '                                              begin main program loop
  69.     NewGame '                                     start a new game
  70.     DO '                                          begin game loop
  71.         PlayGame '                                play the game
  72.     LOOP UNTIL GameOver OR _KEYDOWN(27) '         leave when game over or player exits
  73. LOOP UNTIL _KEYDOWN(27) '                         leave when player exits
  74. Cleanup '                                         clean player's computer memory and exit to operating system
  75.  
  76. '-------------------------------------------------------------------------------------------------------------
  77. '** end main program
  78. '-------------------------------------------------------------------------------------------------------------
  79.  
  80. '-------------------------------------------------------------------------------------------------------------
  81. '** Declare subroutines and functions
  82. '-------------------------------------------------------------------------------------------------------------
  83.  
  84. '-------------------------------------------------------------------------------------------------------------
  85. SUB ResetRound ()
  86.     '---------------------------------------------------------------------------------------------------------
  87.  
  88.     SHARED Missed AS INTEGER '    need access to puck miss indicator
  89.     SHARED Puck AS MOVINGOBJECT ' need access to puck
  90.  
  91.     IF SGN(Puck.xv) = 1 THEN '                          puck was heading right?
  92.         Puck.x = SCREENWIDTH * .25 '                    yes, serve from player 1 side
  93.         Puck.xv = .5 '                                  puck will travel right
  94.     ELSE '                                              no, puck was travleing left
  95.         Puck.x = SCREENWIDTH * .75 '                    serve from player 2 side
  96.         Puck.xv = -.5 '                                 puck will travel left
  97.     END IF
  98.     Puck.y = INT(RND(1) * (SCREENHEIGHT - 100)) + 50 '  random puck Y coordinate
  99.     Puck.yv = (RND(1) - RND(1)) / 2 '                   random puck Y vector component
  100.     Missed = FALSE '                                    reset puck missed indicator
  101.  
  102.  
  103. '-------------------------------------------------------------------------------------------------------------
  104. SUB PlayGame ()
  105.     '---------------------------------------------------------------------------------------------------------
  106.  
  107.     SHARED PlayField AS LONG '    need access to playing field image
  108.     SHARED Missed AS INTEGER '    need access to puck miss indicator
  109.     SHARED Puck AS MOVINGOBJECT ' need access to puck
  110.     SHARED Pscore() AS INTEGER '  need access to player scores
  111.     SHARED GameOver AS INTEGER '  need access to game over indicator
  112.  
  113.     ResetRound '                                             set up next game round
  114.     DO '                                                     begin round loop
  115.         _LIMIT FPS '                                         limit game speed
  116.         _PUTIMAGE , PlayField '                              reset playing field
  117.         MovePaddles '                                        update player paddles
  118.         MovePuck '                                           update puck
  119.         _DISPLAY '                                           update screen with changes
  120.     LOOP UNTIL Missed OR _KEYDOWN(27) '                      leave when puck missed or a player presses ESC
  121.     IF SGN(Puck.xv) = -1 THEN '                              was puck heading left?
  122.         Pscore(1) = Pscore(1) + 1 '                          yes, increase player 2 score
  123.     ELSE '                                                   no, puck was heading right
  124.         Pscore(0) = Pscore(0) + 1 '                          increase player 1 score
  125.     END IF
  126.     IF Pscore(0) = 9 OR Pscore(1) = 9 THEN GameOver = TRUE ' set game over indicator when player reaches 9
  127.     UpdateScores '                                           update player scores
  128.  
  129.  
  130. '-------------------------------------------------------------------------------------------------------------
  131. SUB UpdateScores ()
  132.     '---------------------------------------------------------------------------------------------------------
  133.  
  134.     SHARED PlayField AS LONG '     need access to playing field image
  135.     SHARED PlayFieldCopy AS LONG ' need access to playing field image copy
  136.     SHARED Pscore() AS INTEGER '   need access to player scores
  137.     DIM Clr AS _UNSIGNED LONG '    font color
  138.  
  139.     _PUTIMAGE , PlayFieldCopy, PlayField '                      restore playing field
  140.     _DEST PlayField '                                           make playing field image the destination
  141.     IF Pscore(0) = 9 THEN Clr = BRIGHTWHITE ELSE Clr = WHITE '  set color of font
  142.     DrawScore SCREENWIDTH \ 2 - 100, 50, Pscore(0), Clr '       draw player 1's score
  143.     IF Pscore(1) = 9 THEN Clr = BRIGHTWHITE ELSE Clr = WHITE '  set color of font
  144.     DrawScore SCREENWIDTH \ 2 + 55, 50, Pscore(1), Clr '        draw player 2's score
  145.     _DEST 0 '                                                   set destination back to screen
  146.  
  147.  
  148. '-------------------------------------------------------------------------------------------------------------
  149. SUB DrawScore (dx AS INTEGER, dy AS INTEGER, n AS INTEGER, clr AS _UNSIGNED LONG)
  150.     '---------------------------------------------------------------------------------------------------------
  151.  
  152.     SHARED Score() AS STRING * 15 ' need access to encoded font data
  153.     DIM x AS INTEGER '              font column counter
  154.     DIM y AS INTEGER '              font row counter
  155.     DIM p AS INTEGER '              string position counter
  156.  
  157.     p = 0 '                                       reset string position counter
  158.     FOR y = 0 TO 4 '                              cycle through 5 rows
  159.         FOR x = 0 TO 2 '                          cycle through 3 columns
  160.             p = p + 1 '                           increment string position counter
  161.             IF MID$(Score(n), p, 1) <> " " THEN ' character in this position?
  162.                 LINE (dx + x * 15, dy + y * 15)-(dx + x * 15 + 15, dy + y * 15 + 15), clr, BF ' yes, draw cube
  163.             END IF
  164.     NEXT x, y '                                   leave when all rows and columns processed
  165.  
  166.  
  167. '-------------------------------------------------------------------------------------------------------------
  168. SUB MovePuck ()
  169.     '---------------------------------------------------------------------------------------------------------
  170.  
  171.     SHARED P1Paddle AS MOVINGOBJECT ' need access to player 1 paddle
  172.     SHARED P2Paddle AS MOVINGOBJECT ' need access to player 2 paddle
  173.     SHARED Puck AS MOVINGOBJECT '     need access to puck
  174.     SHARED Missed AS INTEGER '        need access to puck miss indicator
  175.     SHARED Pscore() AS INTEGER '      need access to player scores
  176.  
  177.     Puck.x = Puck.x + Puck.xv '            add X vector component to X coordinate
  178.     Puck.y = Puck.y + Puck.yv '            add Y vector component to Y coordinate
  179.     Puck.rect.x1 = Puck.x - PUCKSIZE \ 2 ' calculate puck rectangle coordinates
  180.     Puck.rect.y1 = Puck.y - PUCKSIZE \ 2
  181.     Puck.rect.x2 = Puck.x + PUCKSIZE \ 2
  182.     Puck.rect.y2 = Puck.y + PUCKSIZE \ 2
  183.     IF Puck.x < PUCKXMIN OR Puck.x > PUCKXMAX THEN '                             is puck within a paddle area?
  184.         IF SGN(Puck.xv) = -1 AND RectCollide(Puck.rect, P1Paddle.rect) THEN '    going left and hit paddle?
  185.             Puck.xv = -Puck.xv + .025 '                                          yes, reverse X vector
  186.             Puck.x = PUCKXMIN '                                                  place puck to right of paddle
  187.             Puck.yv = RND * 3 - 1.5
  188.             '** change in Y vector component here
  189.  
  190.             SOUND 440, 1 '                                                       audio report
  191.         ELSEIF SGN(Puck.xv) = 1 AND RectCollide(Puck.rect, P2Paddle.rect) THEN ' going right and hit paddle?
  192.             Puck.xv = -Puck.xv - .025 '                                          yes, reverse X vector
  193.             Puck.x = PUCKXMAX '                                                  place puck to left of paddle
  194.             Puck.yv = RND * 3 - 1.5
  195.             '** change in Y vector component here
  196.  
  197.             SOUND 440, 1 '                                                       audio report
  198.         END IF
  199.     END IF
  200.     IF Puck.y < PUCKYMIN OR Puck.y > PUCKYMAX THEN '                             is puck hitting a side line?
  201.         IF SGN(Puck.yv) = -1 THEN Puck.y = PUCKYMIN ELSE Puck.y = PUCKYMAX '     yes, place puck above/below
  202.         Puck.yv = -Puck.yv '                                                     reverse Y vector
  203.         SOUND 880, 1 '                                                           audio report
  204.     END IF
  205.     IF Puck.x < P1MISSED OR Puck.x > P2MISSED THEN '                             did puck hit edge of screen?
  206.         Missed = TRUE '                                                          yes, set missed indicator
  207.         SOUND 220, 1 '                                                           audio report
  208.     END IF
  209.     LINE (Puck.rect.x1, Puck.rect.y1)-(Puck.rect.x2, Puck.rect.y2), BRIGHTWHITE, BF ' draw puck
  210.  
  211.  
  212. '-------------------------------------------------------------------------------------------------------------
  213. SUB MovePaddles ()
  214.     '---------------------------------------------------------------------------------------------------------
  215.  
  216.     SHARED P1Paddle AS MOVINGOBJECT ' need access to player 1 paddle
  217.     SHARED P2Paddle AS MOVINGOBJECT ' need access to player 2 paddle
  218.     SHARED Puck AS MOVINGOBJECT '     need access to puck
  219.     SHARED Computer AS INTEGER '      need access to computer opponent
  220.  
  221.     IF _KEYDOWN(87) OR _KEYDOWN(119) THEN '                       player 1 pressing W or w keys?
  222.         P1Paddle.y = P1Paddle.y - PADDLESPEED '                   yes, move paddle up
  223.         IF P1Paddle.y < PADDLEYMIN THEN P1Paddle.y = PADDLEYMIN ' keep paddle in playing field
  224.     ELSEIF _KEYDOWN(83) OR _KEYDOWN(115) THEN '                   player 1 pressing S or s keys?
  225.         P1Paddle.y = P1Paddle.y + PADDLESPEED '                   yes, move paddle down
  226.         IF P1Paddle.y > PADDLEYMAX THEN P1Paddle.y = PADDLEYMAX ' keep paddle in playing field
  227.     END IF
  228.     IF Computer THEN '                                            is player 1 playing computer?
  229.         IF SGN(Puck.xv) = 1 THEN '                                yes, is puck heading for computer?
  230.             IF Puck.y - P2Paddle.y < 0 THEN '                     yes, is puck higher than center of paddle?
  231.                 P2Paddle.y = P2Paddle.y - .5 - RND(1) / 3 '       yes, move computer paddle up
  232.                 IF P2Paddle.y < PADDLEYMIN THEN P2Paddle.y = PADDLEYMIN ' keep paddle in playing field
  233.             ELSE '                                                no, puck is below center of paddle
  234.                 P2Paddle.y = P2Paddle.y + .5 + RND(1) / 3 '       move computer paddle down
  235.                 IF P2Paddle.y > PADDLEYMAX THEN P2Paddle.y = PADDLEYMAX ' keep paddle in playing field
  236.             END IF
  237.         END IF
  238.     ELSEIF _KEYDOWN(18432) THEN '                                 player 2 pressing up arrow key?
  239.         P2Paddle.y = P2Paddle.y - PADDLESPEED '                   yes, move paddle up
  240.         IF P2Paddle.y < PADDLEYMIN THEN P2Paddle.y = PADDLEYMIN ' keep paddle in playing field
  241.     ELSEIF _KEYDOWN(20480) THEN '                                 player 2 pressing down arrow key?
  242.         P2Paddle.y = P2Paddle.y + PADDLESPEED '                   yes, move paddle down
  243.         IF P2Paddle.y > PADDLEYMAX THEN P2Paddle.y = PADDLEYMAX ' keep paddle in playing field
  244.     END IF
  245.     P1Paddle.rect.y1 = P1Paddle.y - PADDLEHEIGHT \ 2 '            calc player 1 paddle rectangle
  246.     P1Paddle.rect.y2 = P1Paddle.y + PADDLEHEIGHT \ 2
  247.     P2Paddle.rect.y1 = P2Paddle.y - PADDLEHEIGHT \ 2 '            calc player 2 paddle rectangle
  248.     P2Paddle.rect.y2 = P2Paddle.y + PADDLEHEIGHT \ 2
  249.     LINE (P1Paddle.rect.x1, P1Paddle.rect.y1)-(P1Paddle.rect.x2, P1Paddle.rect.y2), BRIGHTWHITE, BF ' draw
  250.     LINE (P2Paddle.rect.x1, P2Paddle.rect.y1)-(P2Paddle.rect.x2, P2Paddle.rect.y2), BRIGHTWHITE, BF ' paddles
  251.  
  252.  
  253. '-------------------------------------------------------------------------------------------------------------
  254. SUB NewGame ()
  255.     '---------------------------------------------------------------------------------------------------------
  256.  
  257.     SHARED P1Paddle AS MOVINGOBJECT ' need access toplayer 1 paddle
  258.     SHARED P2Paddle AS MOVINGOBJECT ' need access to player 2 paddle
  259.     SHARED Puck AS MOVINGOBJECT '     need access to puck
  260.     SHARED PlayField AS LONG '        need access to playing field image
  261.     SHARED Pscore() AS INTEGER '      need access to player scores
  262.     SHARED Missed AS INTEGER '        need access to puck miss indicator
  263.     SHARED GameOver AS INTEGER '      need access to game over indicator
  264.     SHARED Computer AS INTEGER '      need access to computer opponent
  265.     DIM Blink AS INTEGER '            blink bit
  266.     DIM NumPlayers AS INTEGER '       number of players selected
  267.     DIM Frames AS INTEGER '           frame counter
  268.  
  269.     P1Paddle.y = SCREENHEIGHT \ 2 '                                                reset paddle locations
  270.     P2Paddle.y = P1Paddle.y
  271.     Puck.xv = .5 '                                                                 reset puck X speed
  272.     Puck.y = SCREENHEIGHT \ 2
  273.     Missed = FALSE '                                                               reset missed indicator
  274.     GameOver = FALSE '                                                             reset game over indicator
  275.     Computer = FALSE '                                                             player 2 is human
  276.     SOUND 440, 1 '                                                                 new game audio
  277.     SOUND 880, 1
  278.     DO '                                                                           begin setup loop
  279.         _LIMIT FPS '                                                               limit frame speed
  280.         _PUTIMAGE , PlayField '                                                    restore playing field
  281.         MovePaddles '                                                              allow paddle movement
  282.         Frames = Frames + 1 '                                                      increment frame counter
  283.         IF Frames = FPS THEN '                                                     max frames reached?
  284.             Frames = 0 '                                                           yes, reset frame counter
  285.             Blink = 1 - Blink '                                                    toggle blink bit
  286.         END IF
  287.         LOCATE (SCREENHEIGHT \ 16), ((SCREENWIDTH \ 8) - 51) \ 2 + 1 '             position cursor on screen
  288.         SELECT CASE Blink '                                                        which blinker bit?
  289.             CASE 0 '                                                               blinker bit 0
  290.                 IF NumPlayers = 0 THEN '                                           number of players chosen?
  291.                     PRINT "    SELECT (1) OR (2) PLAYERS NOW : ESC TO EXIT    "; ' no, display player select
  292.                 ELSE '                                                             yes
  293.                     PRINT "(1) OR (2) PLAYERS : SPACEBAR TO PLAY : ESC TO EXIT"; ' display all instructions
  294.                 END IF
  295.             CASE 1 '                                                               blinker bit 1
  296.                 PRINT SPACE$(51); '                                                remove instructions
  297.         END SELECT
  298.         LOCATE (SCREENHEIGHT \ 16) - 2, ((SCREENWIDTH \ 16) - 27) \ 2 + 1 '        position cursor on screen
  299.         PRINT "PLAYER 1: W = UP / S = DOWN" '                                      display keys to player 1
  300.         LOCATE (SCREENHEIGHT \ 16) - 2, ((SCREENWIDTH \ 8) - 31) \ 2 + (SCREENWIDTH \ 32) + 1 'position cursor
  301.         SELECT CASE NumPlayers '                                                   how many players selected?
  302.             CASE 0: PRINT " SELECT (1) OR (2) PLAYERS NOW " '                      ask number of players
  303.             CASE 1: PRINT " PLAYER 2: THE CPU HAS CONTROL " '                      inform computer playing
  304.             CASE 2: PRINT "PLAYER 2: UP ARROW / DOWN ARROW" '                      display keys player 2
  305.         END SELECT
  306.         IF NumPlayers <> 1 THEN '                                                  has one player been chosen?
  307.             IF _KEYDOWN(49) THEN '                                                 no, "1" key been pressed?
  308.                 NumPlayers = 1 '                                                   yes, set players to 1
  309.                 Computer = TRUE '                                                  remember AI now has control
  310.                 SOUND 440, 1 '                                                     play affirmation beep
  311.                 SOUND 880, 1 '                                                     play affirmation beep
  312.             END IF
  313.         END IF
  314.         IF NumPlayers <> 2 THEN '                                                  has two player been chosen?
  315.             IF _KEYDOWN(50) THEN '                                                 no, "2" key been pressed?
  316.                 NumPlayers = 2 '                                                   yes, set players to 2
  317.                 Computer = FALSE '                                                 human is playing human
  318.                 SOUND 440, 1 '                                                     play affirmation beep
  319.                 SOUND 880, 1 '                                                     play affirmation beep
  320.             END IF
  321.         END IF
  322.         _DISPLAY '                                                                 update screen with changes
  323.     LOOP UNTIL (_KEYDOWN(32) AND NumPlayers) OR _KEYDOWN(27) '                     leave when players select
  324.     Pscore(0) = 0 '                                                                reset scores
  325.     Pscore(1) = 0
  326.     UpdateScores '                                                                 update on screen scores
  327.  
  328.  
  329. '-------------------------------------------------------------------------------------------------------------
  330. SUB Initialize ()
  331.     '---------------------------------------------------------------------------------------------------------
  332.  
  333.     SHARED PlayField AS LONG '        need access to playing field image
  334.     SHARED PlayFieldCopy AS LONG '    need access to playing field image copy
  335.     SHARED P1Paddle AS MOVINGOBJECT ' need access to player 1 paddle
  336.     SHARED P2Paddle AS MOVINGOBJECT ' need access to player 2 paddle
  337.     SHARED Puck AS MOVINGOBJECT '     need access to puck
  338.     SHARED Score() AS STRING * 15 '   need access to encoded font data
  339.     DIM y AS INTEGER '                center line counter
  340.  
  341.     RANDOMIZE TIMER '                                               seed random number generator
  342.  
  343.     '** do as many precalculations as possible for values that will not change
  344.  
  345.     P1Paddle.x = PADDLEOFFSET + PADDLEWIDTH \ 2 '                   calculate player 1 paddle X coordinate
  346.     P2Paddle.x = SCREENWIDTH - 1 - PADDLEOFFSET - PADDLEWIDTH \ 2 ' calculate player 2 paddle X coordinate
  347.     P1Paddle.rect.x1 = P1Paddle.x - PADDLEWIDTH \ 2 '               calculate player 1 paddle rectangle
  348.     P1Paddle.rect.x2 = P1Paddle.x + PADDLEWIDTH \ 2
  349.     P2Paddle.rect.x1 = P2Paddle.x - PADDLEWIDTH \ 2 '               calculate player 2 paddle rectangle
  350.     P2Paddle.rect.x2 = P2Paddle.x + PADDLEWIDTH \ 2
  351.  
  352.     '** draw default playing field
  353.  
  354.     PlayField = _NEWIMAGE(SCREENWIDTH, SCREENHEIGHT, 32) '          create image
  355.     _DEST PlayField '                                               make it destination
  356.     CLS '                                                           clear image
  357.     LINE (0, 19)-(SCREENWIDTH - 1, 29), BRIGHTWHITE, BF '           draw top side line
  358.     LINE (0, SCREENHEIGHT - 30)-(SCREENWIDTH - 1, SCREENHEIGHT - 20), BRIGHTWHITE, BF ' draw bottom side line
  359.     FOR y = 40 TO SCREENHEIGHT - 52 STEP 30 '                       cycle top to bottom
  360.         LINE ((SCREENWIDTH \ 2) - 5, y)-((SCREENWIDTH \ 2) + 5, y + 15), GRAY, BF '     draw center lines
  361.     NEXT y
  362.     _DEST 0 '                                                       destination back to screen
  363.     PlayFieldCopy = _COPYIMAGE(PlayField) '                         save a copy of the playing field image
  364.     Score(0) = "0000 00 00 0000" '                                  create numeric font data
  365.     Score(1) = "  1  1  1  1  1"
  366.     Score(2) = "222  22222  222"
  367.     Score(3) = "333  3333  3333"
  368.     Score(4) = "4 44 4444  4  4"
  369.     Score(5) = "5555  555  5555"
  370.     Score(6) = "6666  6666 6666"
  371.     Score(7) = "777  7  7  7  7"
  372.     Score(8) = "8888 88888 8888"
  373.     Score(9) = "9999 9999  9999"
  374.  
  375.  
  376. '-------------------------------------------------------------------------------------------------------------
  377. FUNCTION RectCollide (Rect1 AS RECTTYPE, Rect2 AS RECTTYPE)
  378.     '---------------------------------------------------------------------------------------------------------
  379.  
  380.     RectCollide = FALSE '                       assume no collision
  381.     IF Rect1.x2 >= Rect2.x1 THEN '              rect 1 lower right X >= rect 2 upper left  X ?
  382.         IF Rect1.x1 <= Rect2.x2 THEN '          rect 1 upper left  X <= rect 2 lower right x ?
  383.             IF Rect1.y2 >= Rect2.y1 THEN '      rect 1 lower right Y >= rect 2 upper left  Y ?
  384.                 IF Rect1.y1 <= Rect2.y2 THEN '  rect 1 upper left  Y <= rect 2 lower right Y ?
  385.                     RectCollide = TRUE '        if all 4 IFs TRUE then a collision is occurring
  386.                 END IF
  387.             END IF
  388.         END IF
  389.     END IF
  390.  
  391.  
  392. '-------------------------------------------------------------------------------------------------------------
  393. SUB Cleanup ()
  394.     '---------------------------------------------------------------------------------------------------------
  395.  
  396.     SHARED PlayField AS LONG '     need access to playing field image
  397.     SHARED PlayFieldCopy AS LONG ' need access to playing field image copy
  398.  
  399.     _FREEIMAGE PlayField '         free images from memory
  400.     _FREEIMAGE PlayFieldCopy
  401.     SYSTEM '                       return to operating system
  402.  
  403.  
  404. '-------------------------------------------------------------------------------------------------------------
  405. FUNCTION Yvector (PaddleY AS INTEGER, PuckY AS INTEGER, PuckYV)
  406.     '---------------------------------------------------------------------------------------------------------
  407.  
  408.  
  409.     '** math here (ugh)
  410.  
  411.  
  412.  
  413.  
  414.