Author Topic: Collisions - Version 9  (Read 4703 times)

0 Members and 1 Guest are viewing this topic.

Offline STxAxTIC

  • Library Staff
  • Forum Resident
  • Posts: 1091
  • he lives
    • View Profile
Collisions - Version 9
« on: March 05, 2020, 07:53:38 pm »
Hello all,

This thread more-or-less marks a milestone in the performance of the physics engine on which I've been elaborating at https://www.qb64.org/forum/index.php?topic=2207.0. The reason I start a new thread is to erase the old code from institutional memory as the old thread slips away into ancient forum history. (Give it a day or two.) Since its inception, I can now be more clear about what this does, what it doesn't do, and what it may do in the future. The overall plan is for this to become a library for folks to use in any project where the physics needs to be correct. For the sake of having it all in one place, here we go:

Review:
  • This is a two dimensional engine that calculates the motion of arbitrarily*-shaped colliding bodies.
  • Each body, or "shape" as named in the code, is a wire-frame object made of closely-separated points.
  • The mass of a given shape is directly proportional to the area it encloses, however the centroid is calculated as if all mass were concentrated in the wireframe itself. (This is subject to change but still captures the physics we want.)
  • Everything is unified. There are no special objects, boundaries, or miracles. The walls are made of the same stuff as the objects contained in them, and are subject to the same physics. Typically I set the mass of a wall element to an extremely high number so they can't move.
  • * Shapes cannot be truly arbitrary - they must be simply-connected and for now, the centroid must lie within the main body. The rest of this math will be work out soon.
  • I have done absolutely nothing to fine-tune this engine. Everything it does so far is a product of its prototype parts working together. If one were to use this to make one particular game, some time would be spent getting the adjustable parameters correct. With that said, it's still possible to make things fly too fast, send objects through each other, etc - so nobody complain about this! Just be gentle and get a feel for it.

Controls:
This update also includes revamped controls. You can select and move any piece, and you can even fling them with the mouse. It's super addictive - just click+drag and let go to send it flying. Again, this isn't really tuned so just toss gently. Just so you see them in more than once, the controls I have are given in:

Code: QB64: [Select]
  1. CALL cprintstring(-16 * 4, "During Play:")
  2. CALL cprintstring(-16 * 6, "Move mouse to select closest object (by centroid).")
  3. CALL cprintstring(-16 * 7, "Boost velocity with arrow keys or W/S/A/D.        ")
  4. CALL cprintstring(-16 * 8, "Boost angluar velocity with Q/E.                  ")
  5. CALL cprintstring(-16 * 9, "Drag and fling object with Mouse 1.               ")
  6. CALL cprintstring(-16 * 10, "Rotate selected object with Mousewheel.           ")
  7. CALL cprintstring(-16 * 11, "Halt all motion with ESC.                         ")
  8. CALL cprintstring(-16 * 12, "Create new ball with Mouse 2.                     ")
  9. CALL cprintstring(-16 * 13, "Initiate creative mode with SPACE.                ")
  10. CALL cprintstring(-16 * 14, "Restart by pressing R during motion.              ")
  11. CALL cprintstring(-16 * 16, "PRESS ANY KEY TO BEGIN.")

Screenshots:
Before blurting out the code, let me point to the screenshots attached. These all came from running the same code, just with very slightly different options at startup.
  • In screenshot 1 we have what started as a pool game, then I right-clicked a bunch of times to add extra balls. Then I hit space to draw a few oddly-shaped yams, and moved those around (and spun them) to knock the balls around.
  • In screenshot 2 I let the "Wacky Game" option play out a while, then I decided that the "bottleneck", or hourglass thing, whatever - was too narrow so I moved a piece... then added a bunch of balls to see if I could get close to the results @bplus was getting with stacking balls. (No change of settings, just added balls.)
  • In screenshot 3 I demonstrate that shape-within-shape momentum exchange works correctly. Since everything is wireframe, why not let things bounce inside each other? If you'e paying attention, this is hardly new, because that's what the box containing the level is up to!

Levels:
And once again, before I blurt out the full code, here is what setting up one particular level looks like. The way this code might interact with other code, like for whoever is making a game or something, a level might look exactly like this. Here's the pool level:

Code: QB64: [Select]
  1. SUB SetupPoolGame
  2.     ' Set external field
  3.     ForceField.x = 0
  4.     ForceField.y = 0
  5.  
  6.     ' Rectangular border
  7.     wx = 42
  8.     wy = 10
  9.     CALL NewBrickLine(-_WIDTH / 2 + wx, _HEIGHT / 2 - wy, _WIDTH / 2 - wx, _HEIGHT / 2 - wy, wx, wy)
  10.     CALL NewBrickLine(-_WIDTH / 2 + wx, -_HEIGHT / 2 + wy, _WIDTH / 2 - wx, -_HEIGHT / 2 + wy, wx, wy)
  11.     wx = 40
  12.     wy = 10
  13.     CALL NewBrickLine(-_WIDTH / 2 + wy, -_HEIGHT / 2 + 2 * wx, -_WIDTH / 2 + wy, _HEIGHT / 2 - 2 * wx, wx, wy)
  14.     CALL NewBrickLine(_WIDTH / 2 - wy, -_HEIGHT / 2 + 2 * wx, _WIDTH / 2 - wy, _HEIGHT / 2 - 2 * wx, wx, wy)
  15.  
  16.     ' Balls (billiard setup)
  17.     x0 = 160
  18.     y0 = 0
  19.     r = 15
  20.     gg = 2 * r + 4
  21.     xf = COS(30 * 3.14159 / 180)
  22.     yf = SIN(30 * 3.14159 / 180)
  23.     gx = gg * xf
  24.     gy = gg * yf
  25.  
  26.     CALL NewAutoBall(x0 + 0 * gx, y0 + 0 * gy, r, 0, 1, 1, 0)
  27.     CALL NewAutoBall(x0 + 1 * gx, y0 + 1 * gy, r, 0, 1, 1, 0)
  28.     CALL NewAutoBall(x0 + 1 * gx, y0 - 1 * gy, r, 0, 1, 1, 0)
  29.     CALL NewAutoBall(x0 + 2 * gx, y0 + 2 * gy, r, 0, 1, 1, 0)
  30.     CALL NewAutoBall(x0 + 2 * gx, y0 + 0 * gy, r, 0, 1, 1, 0)
  31.     CALL NewAutoBall(x0 + 2 * gx, y0 - 2 * gy, r, 0, 1, 1, 0)
  32.     CALL NewAutoBall(x0 + 3 * gx, y0 + 3 * gy, r, 0, 1, 1, 0)
  33.     CALL NewAutoBall(x0 + 3 * gx, y0 + 1 * gy, r, 0, 1, 1, 0)
  34.     CALL NewAutoBall(x0 + 3 * gx, y0 - 1 * gy, r, 0, 1, 1, 0)
  35.     CALL NewAutoBall(x0 + 3 * gx, y0 - 3 * gy, r, 0, 1, 1, 0)
  36.     CALL NewAutoBall(x0 + 4 * gx, y0 + 4 * gy, r, 0, 1, 1, 0)
  37.     CALL NewAutoBall(x0 + 4 * gx, y0 + 2 * gy, r, 0, 1, 1, 0)
  38.     CALL NewAutoBall(x0 + 4 * gx, y0 - 0 * gy, r, 0, 1, 1, 0)
  39.     CALL NewAutoBall(x0 + 4 * gx, y0 - 2 * gy, r, 0, 1, 1, 0)
  40.     CALL NewAutoBall(x0 + 4 * gx, y0 - 4 * gy, r, 0, 1, 1, 0)
  41.  
  42.     ' Cue ball
  43.     CALL NewAutoBall(-220, 0, r, 0, 1, 1, 0)
  44.     Shape(ShapeCount).Velocity.x = 10 + 2 * RND
  45.     Shape(ShapeCount).Velocity.y = 1 * (RND - .5)
  46.     Shape(ShapeCount).Shade = _RGB(255, 255, 255)
  47.  
  48.     ' Parameters
  49.     CPC = 1.15
  50.     FPC = 8
  51.     RST = 0.75
  52.     VD = 0.995
  53.     SV = 0
  54.  

You see we have no gravity, a bit of geometry for the rack of balls, the cue ball, and then some magic numbers at the end - just throwaway values that would be tuned for a real game. But that's it - any game's specification is in a sub just like the one above.

Full Code:
This code has no dependencies, no includes, doesn't need an experimental version of the compiler (although I have yet to test it on an SDL version). There is so much more work to do, but you can probably take this version as solid. Oh yeah - why version 9? No, versions 1 through 8 did not exist. And I promise, nor will version 10 or beyond. This software, regardless of what date the newest code corresponds to, shall always be version 9. That's INT(9). N-i-n-e.

UPDATED

Code: QB64: [Select]
  1. ' Display
  2. SCREEN _NEWIMAGE(800, 600, 32)
  3. _SCREENMOVE (_DESKTOPWIDTH \ 2 - _WIDTH \ 2) - 3, (_DESKTOPHEIGHT \ 2 - _HEIGHT \ 2) - 29
  4. _TITLE "Collisions - Version 9"
  5.  
  6. ' Meta
  7. start:
  8.  
  9. ' Data structures
  10. TYPE Vector
  11.     x AS DOUBLE
  12.     y AS DOUBLE
  13.  
  14. DIM SHARED vtemp AS Vector
  15.  
  16. ' Object type
  17. TYPE Object
  18.     Centroid AS Vector
  19.     Collisions AS LONG
  20.     CollisionSwitch AS INTEGER
  21.     DeltaO AS DOUBLE
  22.     DeltaV AS Vector
  23.     Diameter AS DOUBLE
  24.     Elements AS INTEGER
  25.     Fixed AS INTEGER
  26.     Mass AS DOUBLE
  27.     MOI AS DOUBLE
  28.     PartialNormal AS Vector
  29.     Omega AS DOUBLE
  30.     Shade AS _UNSIGNED LONG
  31.     Velocity AS Vector
  32.  
  33. ' Object storage
  34. DIM SHARED Shape(300) AS Object
  35. DIM SHARED PointChain(300, 500) AS Vector
  36. DIM SHARED TempChain(300, 500) AS Vector
  37. DIM SHARED ShapeCount AS INTEGER
  38. DIM SHARED SelectedShape AS INTEGER
  39.  
  40. ' Dynamics
  41. DIM SHARED CollisionCount AS INTEGER
  42. DIM SHARED ProximalPairs(300 / 2, 1 TO 2)
  43. DIM SHARED ProximalPairsCount
  44. DIM SHARED CPC, FPC, RST, VD, SV
  45.  
  46. ' Environment
  47. DIM SHARED ForceField AS Vector ' Ex: gravity
  48.  
  49. ' Initialize
  50. ShapeCount = 0
  51. CollisionCount = 0
  52.  
  53. ' Prompt
  54. CALL cprintstring(16 * 17, "WELCOME!                    ")
  55. CALL cprintstring(16 * 16, "Press 1 for Pool prototype  ")
  56. CALL cprintstring(16 * 15, "Press 2 for Wacky game      ")
  57. CALL cprintstring(16 * 14, "Press 3 for Concentric rings")
  58. CALL cprintstring(16 * 13, "Press 4 for Walls only      ")
  59. CALL cprintstring(16 * 12, "Press 5 for Angle pong game ")
  60.  
  61.     kk = _KEYHIT
  62.     SELECT CASE kk
  63.         CASE ASC("1")
  64.             CALL SetupPoolGame
  65.             EXIT DO
  66.         CASE ASC("2")
  67.             CALL SetupWackyGame
  68.             EXIT DO
  69.         CASE ASC("3")
  70.             CALL SetupRings
  71.             EXIT DO
  72.         CASE ASC("4")
  73.             CALL SetupWallsOnly
  74.             EXIT DO
  75.         CASE ASC("5")
  76.             CALL SetupAnglePong
  77.             EXIT DO
  78.         CASE ELSE
  79.             _KEYCLEAR
  80.     END SELECT
  81.     _LIMIT 30
  82.  
  83. CALL Graphics
  84. CALL cprintstring(-16 * 4, "During Play:")
  85. CALL cprintstring(-16 * 6, "Move mouse to select closest object (by centroid).")
  86. CALL cprintstring(-16 * 7, "Boost velocity with arrow keys or W/S/A/D.        ")
  87. CALL cprintstring(-16 * 8, "Boost angluar velocity with Q/E.                  ")
  88. CALL cprintstring(-16 * 9, "Drag and fling object with Mouse 1.               ")
  89. CALL cprintstring(-16 * 10, "Rotate selected object with Mousewheel.           ")
  90. CALL cprintstring(-16 * 11, "Halt all motion with ESC.                         ")
  91. CALL cprintstring(-16 * 12, "Create new ball with Mouse 2.                     ")
  92. CALL cprintstring(-16 * 13, "Initiate creative mode with SPACE.                ")
  93. CALL cprintstring(-16 * 14, "Restart by pressing R during motion.              ")
  94. CALL cprintstring(-16 * 16, "PRESS ANY KEY TO BEGIN.")
  95.  
  96. ' Main loop
  97.     IF (UserInput = -1) THEN GOTO start
  98.     CALL PairDynamics(CPC, FPC, RST)
  99.     CALL FleetDynamics(VD, SV)
  100.     CALL Graphics
  101.     _LIMIT 120
  102.  
  103.  
  104. FUNCTION UserInput
  105.     TheReturn = 0
  106.     ' Keyboard input
  107.     kk = _KEYHIT
  108.     SELECT CASE kk
  109.         CASE 32
  110.             DO: LOOP UNTIL _KEYHIT
  111.             WHILE _MOUSEINPUT: WEND
  112.             _KEYCLEAR
  113.             CALL cprintstring(16 * 17, "Drag Mouse 1 counter-clockwise to draw a new shape.")
  114.             CALL cprintstring(16 * 16, "Make sure centroid is inside body.                 ")
  115.             CALL NewMouseShape(7.5, 150, 15)
  116.             CLS
  117.         CASE 18432, ASC("w"), ASC("W") ' Up arrow
  118.             Shape(SelectedShape).Velocity.y = Shape(SelectedShape).Velocity.y * 1.05 + 1.5
  119.         CASE 20480, ASC("s"), ASC("S") ' Down arrow
  120.             Shape(SelectedShape).Velocity.y = Shape(SelectedShape).Velocity.y * 0.95 - 1.5
  121.         CASE 19200, ASC("a"), ASC("A") ' Left arrow
  122.             Shape(SelectedShape).Velocity.x = Shape(SelectedShape).Velocity.x * 0.95 - 1.5
  123.         CASE 19712, ASC("d"), ASC("D") ' Right arrow
  124.             Shape(SelectedShape).Velocity.x = Shape(SelectedShape).Velocity.x * 1.05 + 1.5
  125.         CASE ASC("e"), ASC("E")
  126.             Shape(SelectedShape).Omega = Omega * 0.5 - .02
  127.         CASE ASC("q"), ASC("Q")
  128.             Shape(SelectedShape).Omega = Omega * 1.5 + .02
  129.         CASE ASC("r"), ASC("R")
  130.             TheReturn = -1
  131.         CASE 27
  132.             FOR k = 1 TO ShapeCount
  133.                 Shape(k).Velocity.x = .000001 * (RND - .5)
  134.                 Shape(k).Velocity.y = .000001 * (RND - .5)
  135.                 Shape(k).Omega = .000001 * (RND - .5)
  136.             NEXT
  137.     END SELECT
  138.     IF (kk) THEN
  139.         _KEYCLEAR
  140.     END IF
  141.  
  142.     ' Mouse input
  143.     mb = 0
  144.     mxold = 999999999
  145.     myold = 999999999
  146.         x = _MOUSEX
  147.         y = _MOUSEY
  148.         IF (x > 0) AND (x < _WIDTH) AND (y > 0) AND (y < _HEIGHT) THEN
  149.             x = x - (_WIDTH / 2)
  150.             y = -y + (_HEIGHT / 2)
  151.             rmin = 999999999
  152.             FOR k = 1 TO ShapeCount
  153.                 dx = x - Shape(k).Centroid.x
  154.                 dy = y - Shape(k).Centroid.y
  155.                 r2 = dx * dx + dy * dy
  156.                 IF (r2 < rmin) THEN
  157.                     rmin = r2
  158.                     SelectedShape = k
  159.                 END IF
  160.             NEXT
  161.             IF (_MOUSEBUTTON(1)) THEN
  162.                 IF (mb = 0) THEN
  163.                     mb = 1
  164.                     vtemp.x = x - Shape(SelectedShape).Centroid.x
  165.                     vtemp.y = y - Shape(SelectedShape).Centroid.y
  166.                     CALL TranslateShape(SelectedShape, vtemp)
  167.                     Shape(SelectedShape).Velocity.x = 0
  168.                     Shape(SelectedShape).Velocity.y = 0
  169.                     Shape(SelectedShape).Omega = 0
  170.                     mxold = x
  171.                     myold = y
  172.                 END IF
  173.             END IF
  174.             IF (_MOUSEBUTTON(2)) THEN
  175.                 IF (mb = 0) THEN
  176.                     mb = 1
  177.                     CALL NewAutoBall(x, y, 15, 0, 1, 1, 0)
  178.                     _DELAY .1
  179.                 END IF
  180.             END IF
  181.             IF (_MOUSEWHEEL > 0) THEN
  182.                 CALL RotShape(SelectedShape, Shape(SelectedShape).Centroid, -.02 * 8 * ATN(1))
  183.             END IF
  184.             IF (_MOUSEWHEEL < 0) THEN
  185.                 CALL RotShape(SelectedShape, Shape(SelectedShape).Centroid, .02 * 8 * ATN(1))
  186.             END IF
  187.         END IF
  188.     LOOP
  189.     IF ((mxold <> 999999999) AND (myold <> 999999999)) THEN
  190.         Shape(SelectedShape).Velocity.x = x - mxold
  191.         Shape(SelectedShape).Velocity.y = y - myold
  192.     END IF
  193.     UserInput = TheReturn
  194.  
  195. SUB PairDynamics (CoarseProximityConstant, FineProximityConstant, Restitution)
  196.  
  197.     DIM GrossJ(300) AS INTEGER
  198.     DIM GrossK(300) AS INTEGER
  199.     DIM NumJK AS INTEGER
  200.  
  201.     ' Proximity detection
  202.     ProximalPairsCount = 0
  203.     Shape1 = 0
  204.     Shape2 = 0
  205.     FOR j = 1 TO ShapeCount
  206.         Shape(j).CollisionSwitch = 0
  207.         Shape(j).DeltaO = 0
  208.         Shape(j).DeltaV.x = 0
  209.         Shape(j).DeltaV.y = 0
  210.         Shape(j).PartialNormal.x = 0
  211.         Shape(j).PartialNormal.y = 0
  212.         FOR k = j + 1 TO ShapeCount
  213.             dx = Shape(j).Centroid.x - Shape(k).Centroid.x
  214.             dy = Shape(j).Centroid.y - Shape(k).Centroid.y
  215.             dr = SQR(dx * dx + dy * dy)
  216.             IF (dr < (CoarseProximityConstant) * (Shape(j).Diameter + Shape(k).Diameter)) THEN
  217.                 ProximalPairsCount = ProximalPairsCount + 1
  218.                 ProximalPairs(ProximalPairsCount, 1) = j
  219.                 ProximalPairs(ProximalPairsCount, 2) = k
  220.                 Shape1 = j
  221.                 Shape2 = k
  222.             END IF
  223.         NEXT
  224.     NEXT
  225.  
  226.     IF (ProximalPairsCount > 0) THEN
  227.         FOR n = 1 TO ProximalPairsCount
  228.             Shape1 = ProximalPairs(n, 1)
  229.             Shape2 = ProximalPairs(n, 2)
  230.  
  231.             ' Collision detection
  232.             rmin = 999999999
  233.             ClosestIndex1 = 0
  234.             ClosestIndex2 = 0
  235.             NumJK = 0
  236.             FOR j = 1 TO Shape(Shape1).Elements
  237.                 FOR k = 1 TO Shape(Shape2).Elements
  238.                     dx = PointChain(Shape1, j).x - PointChain(Shape2, k).x
  239.                     dy = PointChain(Shape1, j).y - PointChain(Shape2, k).y
  240.                     r2 = dx * dx + dy * dy
  241.  
  242.                     IF (r2 <= FineProximityConstant) THEN
  243.  
  244.                         ' Partial normal vector 1
  245.                         nx1 = CalculateNormalY(Shape1, j)
  246.                         ny1 = -CalculateNormalX(Shape1, j)
  247.                         nn = SQR(nx1 * nx1 + ny1 * ny1)
  248.                         nx1 = nx1 / nn
  249.                         ny1 = ny1 / nn
  250.                         Shape(Shape1).PartialNormal.x = Shape(Shape1).PartialNormal.x + nx1
  251.                         Shape(Shape1).PartialNormal.y = Shape(Shape1).PartialNormal.y + ny1
  252.  
  253.                         ' Partial normal vector 2
  254.                         nx2 = CalculateNormalY(Shape2, k)
  255.                         ny2 = -CalculateNormalX(Shape2, k)
  256.                         nn = SQR(nx2 * nx2 + ny2 * ny2)
  257.                         nx2 = nx2 / nn
  258.                         ny2 = ny2 / nn
  259.                         Shape(Shape2).PartialNormal.x = Shape(Shape2).PartialNormal.x + nx2
  260.                         Shape(Shape2).PartialNormal.y = Shape(Shape2).PartialNormal.y + ny2
  261.  
  262.                         NumJK = NumJK + 1
  263.                         GrossJ(NumJK) = j
  264.                         GrossK(NumJK) = k
  265.  
  266.                     END IF
  267.                     IF (r2 < rmin) THEN
  268.                         rmin = r2
  269.                         ClosestIndex1 = j
  270.                         ClosestIndex2 = k
  271.                     END IF
  272.                 NEXT
  273.             NEXT
  274.  
  275.             IF (NumJK > 1) THEN
  276.                 IF ((GrossJ(1) - GrossJ(NumJK)) * (GrossJ(1) - GrossJ(NumJK)) > 50) THEN
  277.                     'ClosestIndex1 = 1
  278.                 ELSE
  279.                     ClosestIndex1 = INT(IntegrateArray(GrossJ(), NumJK) / NumJK)
  280.                 END IF
  281.                 IF ((GrossK(1) - GrossK(NumJK)) * (GrossK(1) - GrossK(NumJK)) > 50) THEN
  282.                     'ClosestIndex2 = 1
  283.                 ELSE
  284.                     ClosestIndex2 = INT(IntegrateArray(GrossK(), NumJK) / NumJK)
  285.                 END IF
  286.             END IF
  287.  
  288.             IF (rmin <= FineProximityConstant) THEN
  289.  
  290.                 CollisionCount = CollisionCount + 1
  291.                 Shape(Shape1).CollisionSwitch = 1
  292.                 Shape(Shape2).CollisionSwitch = 1
  293.  
  294.                 ' Undo previous motion
  295.                 IF (Shape(Shape1).Collisions = 0) THEN
  296.                     CALL RotShape(Shape1, Shape(Shape1).Centroid, -Shape(Shape1).Omega)
  297.                     vtemp.x = -(Shape(Shape1).Velocity.x)
  298.                     vtemp.y = -(Shape(Shape1).Velocity.y)
  299.                     CALL TranslateShape(Shape1, vtemp)
  300.                 END IF
  301.                 IF (Shape(Shape2).Collisions = 0) THEN
  302.                     CALL RotShape(Shape2, Shape(Shape2).Centroid, -Shape(Shape2).Omega)
  303.                     vtemp.x = -(Shape(Shape2).Velocity.x)
  304.                     vtemp.y = -(Shape(Shape2).Velocity.y)
  305.                     CALL TranslateShape(Shape2, vtemp)
  306.                 END IF
  307.  
  308.                 ' Momentum absorption
  309.                 IF (Shape(Shape1).Collisions = 0) THEN
  310.                     Shape(Shape1).Velocity.x = Shape(Shape1).Velocity.x * Restitution
  311.                     Shape(Shape1).Velocity.y = Shape(Shape1).Velocity.y * Restitution
  312.                 END IF
  313.                 IF (Shape(Shape2).Collisions = 0) THEN
  314.                     Shape(Shape2).Velocity.x = Shape(Shape2).Velocity.x * Restitution
  315.                     Shape(Shape2).Velocity.y = Shape(Shape2).Velocity.y * Restitution
  316.                 END IF
  317.  
  318.                 ' Centroid of object 1 (cx1, cy1)
  319.                 cx1 = Shape(Shape1).Centroid.x
  320.                 cy1 = Shape(Shape1).Centroid.y
  321.  
  322.                 ' Centroid of object 2 (cx2, cy2)
  323.                 cx2 = Shape(Shape2).Centroid.x
  324.                 cy2 = Shape(Shape2).Centroid.y
  325.  
  326.                 ' Contact point on object 1 (px1, py1)
  327.                 px1 = PointChain(Shape1, ClosestIndex1).x
  328.                 py1 = PointChain(Shape1, ClosestIndex1).y
  329.  
  330.                 ' Contact point on object 2 (px2, py2)
  331.                 px2 = PointChain(Shape2, ClosestIndex2).x
  332.                 py2 = PointChain(Shape2, ClosestIndex2).y
  333.  
  334.                 ' Contact-centroid differentials 1 (dx1, dy1)
  335.                 dx1 = px1 - cx1
  336.                 dy1 = py1 - cy1
  337.  
  338.                 ' Contact-centroid differentials 2 (dx2, dy2)
  339.                 dx2 = px2 - cx2
  340.                 dy2 = py2 - cy2
  341.  
  342.                 ' Normal vector 1 (nx1, ny1)
  343.                 nn = SQR(Shape(Shape1).PartialNormal.x * Shape(Shape1).PartialNormal.x + Shape(Shape1).PartialNormal.y * Shape(Shape1).PartialNormal.y)
  344.                 nx1 = Shape(Shape1).PartialNormal.x / nn
  345.                 ny1 = Shape(Shape1).PartialNormal.y / nn
  346.  
  347.                 ' Normal vector 2 (nx2, ny2)
  348.                 nn = SQR(Shape(Shape2).PartialNormal.x * Shape(Shape2).PartialNormal.x + Shape(Shape2).PartialNormal.y * Shape(Shape2).PartialNormal.y)
  349.                 nx2 = Shape(Shape2).PartialNormal.x / nn
  350.                 ny2 = Shape(Shape2).PartialNormal.y / nn
  351.  
  352.                 '''
  353.                 'nx1 = CalculateNormalY(Shape1, ClosestIndex1)
  354.                 'ny1 = -CalculateNormalX(Shape1, ClosestIndex1)
  355.                 'nn = SQR(nx1 * nx1 + ny1 * ny1)
  356.                 'nx1 = nx1 / nn
  357.                 'ny1 = ny1 / nn
  358.  
  359.                 'nx2 = CalculateNormalY(Shape2, ClosestIndex2)
  360.                 'ny2 = -CalculateNormalX(Shape2, ClosestIndex2)
  361.                 'nn = SQR(nx2 * nx2 + ny2 * ny2)
  362.                 'nx2 = nx2 / nn
  363.                 'ny2 = ny2 / nn
  364.                 '''
  365.  
  366.                 ' Perpendicular vector 1 (prx1, pry1)
  367.                 prx1 = -dy1
  368.                 pry1 = dx1
  369.                 pp = SQR(prx1 * prx1 + pry1 * pry1)
  370.                 prx1 = prx1 / pp
  371.                 pry1 = pry1 / pp
  372.  
  373.                 ' Perpendicular vector 2 (prx2, pry2)
  374.                 prx2 = -dy2
  375.                 pry2 = dx2
  376.                 pp = SQR(prx2 * prx2 + pry2 * pry2)
  377.                 prx2 = prx2 / pp
  378.                 pry2 = pry2 / pp
  379.  
  380.                 ' Angular velocity vector 1 (w1, r1, vx1, vy1)
  381.                 w1 = Shape(Shape1).Omega
  382.                 r1 = SQR(dx1 * dx1 + dy1 * dy1)
  383.                 vx1 = w1 * r1 * prx1
  384.                 vy1 = w1 * r1 * pry1
  385.  
  386.                 ' Angular velocity vector 2 (w2, r2, vx2, vy2)
  387.                 w2 = Shape(Shape2).Omega
  388.                 r2 = SQR(dx2 * dx2 + dy2 * dy2)
  389.                 vx2 = w2 * r2 * prx2
  390.                 vy2 = w2 * r2 * pry2
  391.  
  392.                 ' Mass terms (m1, m2, mu)
  393.                 m1 = Shape(Shape1).Mass
  394.                 m2 = Shape(Shape2).Mass
  395.                 mu = 1 / (1 / m1 + 1 / m2)
  396.  
  397.                 ' Re-Calculate moment of inertia (i1, i2)
  398.                 vtemp.x = px1
  399.                 vtemp.y = py1
  400.                 CALL CalculateMOI(Shape1, vtemp)
  401.                 vtemp.x = px2
  402.                 vtemp.y = py2
  403.                 CALL CalculateMOI(Shape2, vtemp)
  404.                 i1 = Shape(Shape1).MOI
  405.                 i2 = Shape(Shape2).MOI
  406.  
  407.                 ' Velocity differentials (v1, v2, dvtx, dvty)
  408.                 vcx1 = Shape(Shape1).Velocity.x
  409.                 vcy1 = Shape(Shape1).Velocity.y
  410.                 vcx2 = Shape(Shape2).Velocity.x
  411.                 vcy2 = Shape(Shape2).Velocity.y
  412.                 vtx1 = vcx1 + vx1
  413.                 vty1 = vcy1 + vy1
  414.                 vtx2 = vcx2 + vx2
  415.                 vty2 = vcy2 + vy2
  416.                 v1 = SQR(vtx1 * vtx1 + vty1 * vty1)
  417.                 v2 = SQR(vtx2 * vtx2 + vty2 * vty2)
  418.                 dvtx = vtx2 - vtx1
  419.                 dvty = vty2 - vty1
  420.  
  421.                 ' Geometry (n1dotdvt, n2dotdvt)
  422.                 n1dotdvt = nx1 * dvtx + ny1 * dvty
  423.                 n2dotdvt = nx2 * dvtx + ny2 * dvty
  424.  
  425.                 ' Momentum exchange (qx1, qy1, qx2, qy2)
  426.                 qx1 = nx1 * 2 * mu * n1dotdvt
  427.                 qy1 = ny1 * 2 * mu * n1dotdvt
  428.                 qx2 = nx2 * 2 * mu * n2dotdvt
  429.                 qy2 = ny2 * 2 * mu * n2dotdvt
  430.  
  431.                 ' Momentum exchange unit vector (qhat)
  432.                 qq = SQR(qx1 * qx1 + qy1 * qy1)
  433.                 qhatx1 = qx1 / qq
  434.                 qhaty1 = qy1 / qq
  435.                 qq = SQR(qx2 * qx2 + qy2 * qy2)
  436.                 qhatx2 = qx2 / qq
  437.                 qhaty2 = qy2 / qq
  438.  
  439.                 ' Angular impulse
  440.                 q1dotp1 = qx1 * prx1 + qy1 * pry1
  441.                 q2dotp2 = qx2 * prx2 + qy2 * pry2
  442.  
  443.                 ' Translational impulse
  444.                 q1dotn1 = qhatx1 * nx1 + qhaty1 * ny1
  445.                 q2dotn2 = qhatx2 * nx2 + qhaty2 * ny2
  446.                 n1dotr1hat = (nx1 * dx1 + ny1 * dy1) / r1
  447.                 n2dotr2hat = (nx2 * dx2 + ny2 * dy2) / r2
  448.                 f1 = -q1dotn1 * n1dotr1hat
  449.                 f2 = -q2dotn2 * n2dotr2hat
  450.  
  451.                 ' Special case for shape within shape.
  452.                 np = nx1 * nx2 + ny1 * ny2
  453.                 IF (np > 0) THEN
  454.                     dcx = cx1 - cx2
  455.                     dcy = cy1 - cy2
  456.                     dc = SQR(dcx * dcx + dcy * dcy)
  457.                     IF (dc < (r1 + r2)) THEN
  458.                         IF (m1 > m2) THEN ' This criteria may be bullshit in general but works now.
  459.                             q1dotp1 = -q1dotp1
  460.                             f1 = -f1
  461.                         ELSE
  462.                             q2dotp2 = -q2dotp2
  463.                             f2 = -f2
  464.                         END IF
  465.                     END IF
  466.                 END IF
  467.  
  468.                 ' Angular impulse update
  469.                 Shape(Shape1).DeltaO = Shape(Shape1).DeltaO + r1 * q1dotp1 / i1
  470.                 Shape(Shape2).DeltaO = Shape(Shape2).DeltaO - r2 * q2dotp2 / i2
  471.  
  472.                 ' Linear impulse update
  473.                 dvx1 = f1 * qx1 / m1
  474.                 dvy1 = f1 * qy1 / m1
  475.                 dvx2 = f2 * qx2 / m2
  476.                 dvy2 = f2 * qy2 / m2
  477.                 dvx1s = dvx1 * dvx1
  478.                 dvy1s = dvy1 * dvy1
  479.                 dvx2s = dvx2 * dvx2
  480.                 dvy2s = dvy2 * dvy2
  481.                 IF ((dvx1s > .001) AND (dvx1s < 60)) THEN
  482.                     Shape(Shape1).DeltaV.x = Shape(Shape1).DeltaV.x + dvx1
  483.                 END IF
  484.                 IF ((dvy1s > .001) AND (dvy1s < 60)) THEN
  485.                     Shape(Shape1).DeltaV.y = Shape(Shape1).DeltaV.y + dvy1
  486.                 END IF
  487.                 IF ((dvx2s > .001) AND (dvx2s < 60)) THEN
  488.                     Shape(Shape2).DeltaV.x = Shape(Shape2).DeltaV.x + dvx2
  489.                 END IF
  490.                 IF ((dvy2s > .001) AND (dvy2s < 60)) THEN
  491.                     Shape(Shape2).DeltaV.y = Shape(Shape2).DeltaV.y + dvy2
  492.                 END IF
  493.  
  494.                 ' External torque
  495.                 torque1 = m1 * (dx1 * ForceField.y - dy1 * ForceField.x)
  496.                 torque2 = m2 * (dx2 * ForceField.y - dy2 * ForceField.x)
  497.                 Shape(Shape1).DeltaO = Shape(Shape1).DeltaO - torque1 / i1
  498.                 Shape(Shape2).DeltaO = Shape(Shape2).DeltaO - torque2 / i2
  499.  
  500.                 ' Separate along normal
  501.                 vtemp.x = -nx1 * 1 * ((v1 + 0.01) / m1)
  502.                 vtemp.y = -ny1 * 1 * ((v1 + 0.01) / m1)
  503.                 CALL TranslateShape(Shape1, vtemp)
  504.                 vtemp.x = -nx2 * 1 * ((v2 + 0.01) / m2)
  505.                 vtemp.y = -ny2 * 1 * ((v2 + 0.01) / m2)
  506.                 CALL TranslateShape(Shape2, vtemp)
  507.  
  508.                 ' Dent along normal
  509.                 'PointChain(Shape1, ClosestIndex1).x = PointChain(Shape1, ClosestIndex1).x - v1 * nx1 / 2
  510.                 'PointChain(Shape1, ClosestIndex1).y = PointChain(Shape1, ClosestIndex1).y - v1 * ny1 / 2
  511.                 'PointChain(Shape2, ClosestIndex2).x = PointChain(Shape2, ClosestIndex2).x - v2 * nx2 / 2
  512.                 'PointChain(Shape2, ClosestIndex2).y = PointChain(Shape2, ClosestIndex2).y - v2 * ny2 / 2
  513.  
  514.                 ' Feedback
  515.                 IF (Shape(Shape1).Collisions = 0) AND (Shape(Shape2).Collisions = 0) THEN
  516.                     CALL snd(100 * (v1 + v2) / 2, .5)
  517.                 END IF
  518.  
  519.             END IF
  520.         NEXT
  521.     END IF
  522.  
  523. SUB FleetDynamics (MotionDamping, StoppingVelocity)
  524.  
  525.     FOR ShapeIndex = 1 TO ShapeCount
  526.  
  527.         ' Contact update
  528.         IF (Shape(ShapeIndex).CollisionSwitch = 1) THEN
  529.             Shape(ShapeIndex).Collisions = Shape(ShapeIndex).Collisions + 1
  530.         ELSE
  531.             Shape(ShapeIndex).Collisions = 0
  532.         END IF
  533.  
  534.         IF (Shape(ShapeIndex).Fixed = 0) THEN
  535.  
  536.             ' Angular velocity update
  537.             Shape(ShapeIndex).Omega = Shape(ShapeIndex).Omega + Shape(ShapeIndex).DeltaO
  538.  
  539.             ' Linear velocity update
  540.             Shape(ShapeIndex).Velocity.x = Shape(ShapeIndex).Velocity.x + Shape(ShapeIndex).DeltaV.x
  541.             Shape(ShapeIndex).Velocity.y = Shape(ShapeIndex).Velocity.y + Shape(ShapeIndex).DeltaV.y
  542.  
  543.             IF (Shape(ShapeIndex).Collisions < 2) THEN ' changed from = 0
  544.                 ' Freefall (if airborne)
  545.                 Shape(ShapeIndex).Velocity.x = Shape(ShapeIndex).Velocity.x + ForceField.x
  546.                 Shape(ShapeIndex).Velocity.y = Shape(ShapeIndex).Velocity.y + ForceField.y
  547.             ELSE
  548.                 ' Static friction
  549.                 IF ((Shape(ShapeIndex).Velocity.x * Shape(ShapeIndex).Velocity.x) < StoppingVelocity) THEN
  550.                     Shape(ShapeIndex).Velocity.x = Shape(ShapeIndex).Velocity.x * .05
  551.                 END IF
  552.                 IF ((Shape(ShapeIndex).Velocity.y * Shape(ShapeIndex).Velocity.y) < StoppingVelocity) THEN
  553.                     Shape(ShapeIndex).Velocity.y = Shape(ShapeIndex).Velocity.y * .05
  554.                 END IF
  555.                 IF ((Shape(ShapeIndex).Omega * Shape(ShapeIndex).Omega) < .000015 * StoppingVelocity) THEN
  556.                     Shape(ShapeIndex).Omega = 0
  557.                 END IF
  558.             END IF
  559.  
  560.             ' Rotation update
  561.             CALL RotShape(ShapeIndex, Shape(ShapeIndex).Centroid, Shape(ShapeIndex).Omega)
  562.  
  563.             ' Position update
  564.             CALL TranslateShape(ShapeIndex, Shape(ShapeIndex).Velocity)
  565.  
  566.             ' Motion Damping
  567.             Shape(ShapeIndex).Velocity.x = Shape(ShapeIndex).Velocity.x * MotionDamping
  568.             Shape(ShapeIndex).Velocity.y = Shape(ShapeIndex).Velocity.y * MotionDamping
  569.             Shape(ShapeIndex).Omega = Shape(ShapeIndex).Omega * MotionDamping
  570.  
  571.         ELSE
  572.  
  573.             ' Lock all motion
  574.             Shape(ShapeIndex).Velocity.x = 0
  575.             Shape(ShapeIndex).Velocity.y = 0
  576.             Shape(ShapeIndex).Omega = 0
  577.  
  578.         END IF
  579.     NEXT
  580.  
  581.  
  582. SUB Graphics
  583.     LINE (0, 0)-(_WIDTH, _HEIGHT), _RGBA(0, 0, 0, 200), BF
  584.     FOR ShapeIndex = 1 TO ShapeCount
  585.         FOR i = 1 TO Shape(ShapeIndex).Elements - 1
  586.             CALL cpset(PointChain(ShapeIndex, i).x, PointChain(ShapeIndex, i).y, Shape(ShapeIndex).Shade)
  587.             CALL cline(PointChain(ShapeIndex, i).x, PointChain(ShapeIndex, i).y, PointChain(ShapeIndex, i + 1).x, PointChain(ShapeIndex, i + 1).y, Shape(ShapeIndex).Shade)
  588.             IF (ShapeIndex = SelectedShape) THEN
  589.                 CALL ccircle(PointChain(ShapeIndex, i).x, PointChain(ShapeIndex, i).y, 1, Shape(ShapeIndex).Shade)
  590.             END IF
  591.         NEXT
  592.         CALL cpset(PointChain(ShapeIndex, Shape(ShapeIndex).Elements).x, PointChain(ShapeIndex, Shape(ShapeIndex).Elements).y, Shape(ShapeIndex).Shade)
  593.         CALL cline(PointChain(ShapeIndex, 1).x, PointChain(ShapeIndex, 1).y, PointChain(ShapeIndex, Shape(ShapeIndex).Elements).x, PointChain(ShapeIndex, Shape(ShapeIndex).Elements).y, Shape(ShapeIndex).Shade)
  594.         CALL cline(Shape(ShapeIndex).Centroid.x, Shape(ShapeIndex).Centroid.y, PointChain(ShapeIndex, 1).x, PointChain(ShapeIndex, 1).y, Shape(ShapeIndex).Shade)
  595.         IF (ShapeIndex = SelectedShape) THEN
  596.             CALL ccircle(Shape(ShapeIndex).Centroid.x, Shape(ShapeIndex).Centroid.y, 3, Shape(ShapeIndex).Shade)
  597.             CALL cpaint(Shape(ShapeIndex).Centroid.x, Shape(ShapeIndex).Centroid.y, Shape(ShapeIndex).Shade, Shape(ShapeIndex).Shade)
  598.         END IF
  599.     NEXT
  600.     _DISPLAY
  601.  
  602. FUNCTION IntegrateArray (arr() AS INTEGER, lim AS INTEGER)
  603.     t = 0
  604.     FOR j = 1 TO lim
  605.         t = t + arr(j)
  606.     NEXT
  607.     IntegrateArray = t
  608.  
  609. FUNCTION CalculateNormalX (k AS INTEGER, i AS INTEGER)
  610.     DIM l AS Vector
  611.     DIM r AS Vector
  612.     li = i - 1
  613.     ri = i + 1
  614.     IF (i = 1) THEN li = Shape(k).Elements
  615.     IF (i = Shape(k).Elements) THEN ri = 1
  616.     l.x = PointChain(k, li).x
  617.     r.x = PointChain(k, ri).x
  618.     dx = r.x - l.x
  619.     CalculateNormalX = dx
  620.  
  621. FUNCTION CalculateNormalY (k AS INTEGER, i AS INTEGER)
  622.     DIM l AS Vector
  623.     DIM r AS Vector
  624.     li = i - 1
  625.     ri = i + 1
  626.     IF (i = 1) THEN li = Shape(k).Elements
  627.     IF (i = Shape(k).Elements) THEN ri = 1
  628.     l.y = PointChain(k, li).y
  629.     r.y = PointChain(k, ri).y
  630.     dy = r.y - l.y
  631.     CalculateNormalY = dy
  632.  
  633. SUB CalculateCentroid (k AS INTEGER)
  634.     xx = 0
  635.     yy = 0
  636.     FOR i = 1 TO Shape(k).Elements
  637.         xx = xx + PointChain(k, i).x
  638.         yy = yy + PointChain(k, i).y
  639.     NEXT
  640.     Shape(k).Centroid.x = xx / Shape(k).Elements
  641.     Shape(k).Centroid.y = yy / Shape(k).Elements
  642.  
  643. SUB CalculateDiameter (k AS INTEGER)
  644.     r2max = -1
  645.     FOR i = 1 TO Shape(k).Elements
  646.         xx = Shape(k).Centroid.x - PointChain(k, i).x
  647.         yy = Shape(k).Centroid.y - PointChain(k, i).y
  648.         r2 = xx * xx + yy * yy
  649.         IF (r2 > r2max) THEN
  650.             r2max = r2
  651.         END IF
  652.     NEXT
  653.     Shape(k).Diameter = SQR(r2max)
  654.  
  655. SUB CalculateMass (k AS INTEGER, factor AS DOUBLE)
  656.     aa = 0
  657.     FOR i = 2 TO Shape(k).Elements
  658.         x = PointChain(k, i).x - Shape(k).Centroid.x
  659.         y = PointChain(k, i).y - Shape(k).Centroid.y
  660.         dx = (PointChain(k, i).x - PointChain(k, i - 1).x)
  661.         dy = (PointChain(k, i).y - PointChain(k, i - 1).y)
  662.         da = .5 * (x * dy - y * dx)
  663.         aa = aa + da
  664.     NEXT
  665.     Shape(k).Mass = factor * SQR(aa * aa)
  666.  
  667. SUB CalculateMOI (k AS INTEGER, ctrvec AS Vector)
  668.     xx = 0
  669.     yy = 0
  670.     FOR i = 1 TO Shape(k).Elements
  671.         a = ctrvec.x - PointChain(k, i).x
  672.         b = ctrvec.y - PointChain(k, i).y
  673.         xx = xx + a * a
  674.         yy = yy + b * b
  675.     NEXT
  676.     Shape(k).MOI = SQR((xx + yy) * (xx + yy)) * (Shape(k).Mass / Shape(k).Elements)
  677.  
  678. SUB TranslateShape (k AS INTEGER, c AS Vector)
  679.     FOR i = 1 TO Shape(k).Elements
  680.         PointChain(k, i).x = PointChain(k, i).x + c.x
  681.         PointChain(k, i).y = PointChain(k, i).y + c.y
  682.     NEXT
  683.     Shape(k).Centroid.x = Shape(k).Centroid.x + c.x
  684.     Shape(k).Centroid.y = Shape(k).Centroid.y + c.y
  685.  
  686. SUB RotShape (k AS INTEGER, c AS Vector, da AS DOUBLE)
  687.     FOR i = 1 TO Shape(k).Elements
  688.         xx = PointChain(k, i).x - c.x
  689.         yy = PointChain(k, i).y - c.y
  690.         PointChain(k, i).x = c.x + xx * COS(da) - yy * SIN(da)
  691.         PointChain(k, i).y = c.y + yy * COS(da) + xx * SIN(da)
  692.     NEXT
  693.  
  694. SUB NewAutoBall (x1, y1, r1, r2, pa, pb, fx)
  695.     ShapeCount = ShapeCount + 1
  696.     Shape(ShapeCount).Fixed = fx
  697.     Shape(ShapeCount).Collisions = 0
  698.     i = 0
  699.     FOR j = 0 TO (8 * ATN(1)) STEP .02 * 8 * ATN(1)
  700.         i = i + 1
  701.         r = r1 + r2 * COS(pa * j) ^ pb
  702.         PointChain(ShapeCount, i).x = x1 + r * COS(j)
  703.         PointChain(ShapeCount, i).y = y1 + r * SIN(j)
  704.     NEXT
  705.     Shape(ShapeCount).Elements = i
  706.     CALL CalculateCentroid(ShapeCount)
  707.     IF (fx = 0) THEN
  708.         CALL CalculateMass(ShapeCount, 1)
  709.     ELSE
  710.         CALL CalculateMass(ShapeCount, 999999)
  711.     END IF
  712.     CALL CalculateMOI(ShapeCount, Shape(ShapeCount).Centroid)
  713.     CALL CalculateDiameter(ShapeCount)
  714.     Shape(ShapeCount).Velocity.x = 0
  715.     Shape(ShapeCount).Velocity.y = 0
  716.     Shape(ShapeCount).Omega = 0
  717.     IF (fx = 0) THEN
  718.         Shape(ShapeCount).Shade = _RGB(100 + INT(RND * 155), 100 + INT(RND * 155), 100 + INT(RND * 155))
  719.     ELSE
  720.         Shape(ShapeCount).Shade = _RGB(100, 100, 100)
  721.     END IF
  722.     SelectedShape = ShapeCount
  723.  
  724. SUB NewAutoBrick (x1, y1, wx, wy, ang)
  725.     ShapeCount = ShapeCount + 1
  726.     Shape(ShapeCount).Fixed = 1
  727.     Shape(ShapeCount).Collisions = 0
  728.     i = 0
  729.     FOR j = -wy / 2 TO wy / 2 STEP 5
  730.         i = i + 1
  731.         PointChain(ShapeCount, i).x = x1 + wx / 2
  732.         PointChain(ShapeCount, i).y = y1 + j
  733.     NEXT
  734.     FOR j = wx / 2 TO -wx / 2 STEP -5
  735.         i = i + 1
  736.         PointChain(ShapeCount, i).x = x1 + j
  737.         PointChain(ShapeCount, i).y = y1 + wy / 2
  738.     NEXT
  739.     FOR j = wy / 2 TO -wy / 2 STEP -5
  740.         i = i + 1
  741.         PointChain(ShapeCount, i).x = x1 - wx / 2
  742.         PointChain(ShapeCount, i).y = y1 + j
  743.     NEXT
  744.     FOR j = -wx / 2 TO wx / 2 STEP 5
  745.         i = i + 1
  746.         PointChain(ShapeCount, i).x = x1 + j
  747.         PointChain(ShapeCount, i).y = y1 - wy / 2
  748.     NEXT
  749.     Shape(ShapeCount).Elements = i
  750.     CALL CalculateCentroid(ShapeCount)
  751.     CALL CalculateMass(ShapeCount, 99999)
  752.     CALL CalculateMOI(ShapeCount, Shape(ShapeCount).Centroid)
  753.     CALL CalculateDiameter(ShapeCount)
  754.     Shape(ShapeCount).Velocity.x = 0
  755.     Shape(ShapeCount).Velocity.y = 0
  756.     Shape(ShapeCount).Omega = 0
  757.     Shape(ShapeCount).Shade = _RGB(100, 100, 100)
  758.     SelectedShape = ShapeCount
  759.     CALL RotShape(ShapeCount, Shape(ShapeCount).Centroid, ang)
  760.  
  761. SUB NewBrickLine (xi, yi, xf, yf, wx, wy)
  762.     d1 = SQR((xf - xi) ^ 2 + (yf - yi) ^ 2)
  763.     d2 = SQR(wx ^ 2 + wy ^ 2)
  764.     ang = ATN((yf - yi) / (xf - xi))
  765.     f = 1.2 * d2 / d1
  766.     FOR t = 0 TO 1 + f STEP f
  767.         CALL NewAutoBrick(xi * (1 - t) + xf * t, yi * (1 - t) + yf * t, wx, wy, ang)
  768.     NEXT
  769.  
  770. SUB NewMouseShape (rawresolution AS DOUBLE, targetpoints AS INTEGER, smoothiterations AS INTEGER)
  771.     ShapeCount = ShapeCount + 1
  772.     Shape(ShapeCount).Fixed = 0
  773.     Shape(ShapeCount).Collisions = 0
  774.     numpoints = 0
  775.     xold = 999 ^ 999
  776.     yold = 999 ^ 999
  777.     DO
  778.         DO WHILE _MOUSEINPUT
  779.             x = _MOUSEX
  780.             y = _MOUSEY
  781.             IF (x > 0) AND (x < _WIDTH) AND (y > 0) AND (y < _HEIGHT) THEN
  782.                 IF _MOUSEBUTTON(1) THEN
  783.                     x = x - (_WIDTH / 2)
  784.                     y = -y + (_HEIGHT / 2)
  785.                     delta = SQR((x - xold) ^ 2 + (y - yold) ^ 2)
  786.                     IF (delta > rawresolution) AND (numpoints < targetpoints - 1) THEN
  787.                         numpoints = numpoints + 1
  788.                         PointChain(ShapeCount, numpoints).x = x
  789.                         PointChain(ShapeCount, numpoints).y = y
  790.                         CALL cpset(x, y, _RGB(0, 255, 255))
  791.                         xold = x
  792.                         yold = y
  793.                     END IF
  794.                 END IF
  795.             END IF
  796.         LOOP
  797.         _DISPLAY
  798.     LOOP UNTIL NOT _MOUSEBUTTON(1) AND (numpoints > 1)
  799.  
  800.     DO WHILE (numpoints < targetpoints)
  801.         rad2max = -1
  802.         kmax = -1
  803.         FOR k = 1 TO numpoints - 1
  804.             xfac = PointChain(ShapeCount, k).x - PointChain(ShapeCount, k + 1).x
  805.             yfac = PointChain(ShapeCount, k).y - PointChain(ShapeCount, k + 1).y
  806.             rad2 = xfac ^ 2 + yfac ^ 2
  807.             IF rad2 > rad2max THEN
  808.                 kmax = k
  809.                 rad2max = rad2
  810.             END IF
  811.         NEXT
  812.         edgecase = 0
  813.         xfac = PointChain(ShapeCount, numpoints).x - PointChain(ShapeCount, 1).x
  814.         yfac = PointChain(ShapeCount, numpoints).y - PointChain(ShapeCount, 1).y
  815.         rad2 = xfac ^ 2 + yfac ^ 2
  816.         IF (rad2 > rad2max) THEN
  817.             kmax = numpoints
  818.             rad2max = rad2
  819.             edgecase = 1
  820.         END IF
  821.         numpoints = numpoints + 1
  822.         IF (edgecase = 0) THEN
  823.             FOR j = numpoints TO kmax + 1 STEP -1
  824.                 PointChain(ShapeCount, j + 1).x = PointChain(ShapeCount, j).x
  825.                 PointChain(ShapeCount, j + 1).y = PointChain(ShapeCount, j).y
  826.             NEXT
  827.             PointChain(ShapeCount, kmax + 1).x = (1 / 2) * (PointChain(ShapeCount, kmax).x + PointChain(ShapeCount, kmax + 2).x)
  828.             PointChain(ShapeCount, kmax + 1).y = (1 / 2) * (PointChain(ShapeCount, kmax).y + PointChain(ShapeCount, kmax + 2).y)
  829.         ELSE
  830.             PointChain(ShapeCount, numpoints).x = (1 / 2) * (PointChain(ShapeCount, 1).x + PointChain(ShapeCount, numpoints - 1).x)
  831.             PointChain(ShapeCount, numpoints).y = (1 / 2) * (PointChain(ShapeCount, 1).y + PointChain(ShapeCount, numpoints - 1).y)
  832.         END IF
  833.     LOOP
  834.  
  835.     FOR j = 1 TO smoothiterations
  836.         FOR k = 2 TO numpoints - 1
  837.             TempChain(ShapeCount, k).x = (1 / 2) * (PointChain(ShapeCount, k - 1).x + PointChain(ShapeCount, k + 1).x)
  838.             TempChain(ShapeCount, k).y = (1 / 2) * (PointChain(ShapeCount, k - 1).y + PointChain(ShapeCount, k + 1).y)
  839.         NEXT
  840.         FOR k = 2 TO numpoints - 1
  841.             PointChain(ShapeCount, k).x = TempChain(ShapeCount, k).x
  842.             PointChain(ShapeCount, k).y = TempChain(ShapeCount, k).y
  843.         NEXT
  844.         TempChain(ShapeCount, 1).x = (1 / 2) * (PointChain(ShapeCount, numpoints).x + PointChain(ShapeCount, 2).x)
  845.         TempChain(ShapeCount, 1).y = (1 / 2) * (PointChain(ShapeCount, numpoints).y + PointChain(ShapeCount, 2).y)
  846.         PointChain(ShapeCount, 1).x = TempChain(ShapeCount, 1).x
  847.         PointChain(ShapeCount, 1).y = TempChain(ShapeCount, 1).y
  848.         TempChain(ShapeCount, numpoints).x = (1 / 2) * (PointChain(ShapeCount, 1).x + PointChain(ShapeCount, numpoints - 1).x)
  849.         TempChain(ShapeCount, numpoints).y = (1 / 2) * (PointChain(ShapeCount, 1).y + PointChain(ShapeCount, numpoints - 1).y)
  850.         PointChain(ShapeCount, numpoints).x = TempChain(ShapeCount, numpoints).x
  851.         PointChain(ShapeCount, numpoints).y = TempChain(ShapeCount, numpoints).y
  852.     NEXT
  853.  
  854.     Shape(ShapeCount).Elements = numpoints
  855.     CALL CalculateCentroid(ShapeCount)
  856.     CALL CalculateMass(ShapeCount, 1)
  857.     CALL CalculateMOI(ShapeCount, Shape(ShapeCount).Centroid)
  858.     CALL CalculateDiameter(ShapeCount)
  859.     Shape(ShapeCount).Velocity.x = 0
  860.     Shape(ShapeCount).Velocity.y = 0
  861.     Shape(ShapeCount).Omega = 0
  862.     Shape(ShapeCount).Shade = _RGB(100 + INT(RND * 155), 100 + INT(RND * 155), 100 + INT(RND * 155))
  863.     SelectedShape = ShapeCount
  864.  
  865. SUB cline (x1, y1, x2, y2, col AS _UNSIGNED LONG)
  866.     LINE (_WIDTH / 2 + x1, -y1 + _HEIGHT / 2)-(_WIDTH / 2 + x2, -y2 + _HEIGHT / 2), col
  867.  
  868. SUB ccircle (x1, y1, rad, col AS _UNSIGNED LONG)
  869.     CIRCLE (_WIDTH / 2 + x1, -y1 + _HEIGHT / 2), rad, col
  870.  
  871. SUB cpset (x1, y1, col AS _UNSIGNED LONG)
  872.     PSET (_WIDTH / 2 + x1, -y1 + _HEIGHT / 2), col
  873.  
  874. SUB cpaint (x1, y1, col1 AS _UNSIGNED LONG, col2 AS _UNSIGNED LONG)
  875.     PAINT (_WIDTH / 2 + x1, -y1 + _HEIGHT / 2), col1, col2
  876.  
  877. SUB cprintstring (y, a AS STRING)
  878.     _PRINTSTRING (_WIDTH / 2 - (LEN(a) * 8) / 2, -y + _HEIGHT / 2), a
  879.  
  880. SUB snd (frq, dur)
  881.     IF ((frq >= 37) AND (frq <= 2000)) THEN
  882.         SOUND frq, dur
  883.     END IF
  884.  
  885. SUB SetupPoolGame
  886.     ' Set external field
  887.     ForceField.x = 0
  888.     ForceField.y = 0
  889.  
  890.     ' Rectangular border
  891.     wx = 42
  892.     wy = 10
  893.     CALL NewBrickLine(-_WIDTH / 2 + wx, _HEIGHT / 2 - wy, _WIDTH / 2 - wx, _HEIGHT / 2 - wy, wx, wy)
  894.     CALL NewBrickLine(-_WIDTH / 2 + wx, -_HEIGHT / 2 + wy, _WIDTH / 2 - wx, -_HEIGHT / 2 + wy, wx, wy)
  895.     wx = 40
  896.     wy = 10
  897.     CALL NewBrickLine(-_WIDTH / 2 + wy, -_HEIGHT / 2 + 2 * wx, -_WIDTH / 2 + wy, _HEIGHT / 2 - 2 * wx, wx, wy)
  898.     CALL NewBrickLine(_WIDTH / 2 - wy, -_HEIGHT / 2 + 2 * wx, _WIDTH / 2 - wy, _HEIGHT / 2 - 2 * wx, wx, wy)
  899.  
  900.     ' Balls (billiard setup)
  901.     x0 = 160
  902.     y0 = 0
  903.     r = 15
  904.     gg = 2 * r + 4
  905.     xf = COS(30 * 3.14159 / 180)
  906.     yf = SIN(30 * 3.14159 / 180)
  907.     gx = gg * xf
  908.     gy = gg * yf
  909.  
  910.     CALL NewAutoBall(x0 + 0 * gx, y0 + 0 * gy, r, 0, 1, 1, 0)
  911.     CALL NewAutoBall(x0 + 1 * gx, y0 + 1 * gy, r, 0, 1, 1, 0)
  912.     CALL NewAutoBall(x0 + 1 * gx, y0 - 1 * gy, r, 0, 1, 1, 0)
  913.     CALL NewAutoBall(x0 + 2 * gx, y0 + 2 * gy, r, 0, 1, 1, 0)
  914.     CALL NewAutoBall(x0 + 2 * gx, y0 + 0 * gy, r, 0, 1, 1, 0)
  915.     CALL NewAutoBall(x0 + 2 * gx, y0 - 2 * gy, r, 0, 1, 1, 0)
  916.     CALL NewAutoBall(x0 + 3 * gx, y0 + 3 * gy, r, 0, 1, 1, 0)
  917.     CALL NewAutoBall(x0 + 3 * gx, y0 + 1 * gy, r, 0, 1, 1, 0)
  918.     CALL NewAutoBall(x0 + 3 * gx, y0 - 1 * gy, r, 0, 1, 1, 0)
  919.     CALL NewAutoBall(x0 + 3 * gx, y0 - 3 * gy, r, 0, 1, 1, 0)
  920.     CALL NewAutoBall(x0 + 4 * gx, y0 + 4 * gy, r, 0, 1, 1, 0)
  921.     CALL NewAutoBall(x0 + 4 * gx, y0 + 2 * gy, r, 0, 1, 1, 0)
  922.     CALL NewAutoBall(x0 + 4 * gx, y0 - 0 * gy, r, 0, 1, 1, 0)
  923.     CALL NewAutoBall(x0 + 4 * gx, y0 - 2 * gy, r, 0, 1, 1, 0)
  924.     CALL NewAutoBall(x0 + 4 * gx, y0 - 4 * gy, r, 0, 1, 1, 0)
  925.  
  926.     ' Cue ball
  927.     CALL NewAutoBall(-220, 0, r, 0, 1, 1, 0)
  928.     Shape(ShapeCount).Velocity.x = 10 + 2 * RND
  929.     Shape(ShapeCount).Velocity.y = 1 * (RND - .5)
  930.     Shape(ShapeCount).Shade = _RGB(255, 255, 255)
  931.  
  932.     ' Parameters
  933.     CPC = 1.15
  934.     FPC = 8
  935.     RST = 0.75
  936.     VD = 0.995
  937.     SV = 0
  938.  
  939. SUB SetupWackyGame
  940.     ' Set external field
  941.     ForceField.x = 0
  942.     ForceField.y = -.08
  943.  
  944.     ' Rectangular border
  945.     wx = 42
  946.     wy = 10
  947.     CALL NewBrickLine(-_WIDTH / 2 + wx, _HEIGHT / 2 - wy, _WIDTH / 2 - wx, _HEIGHT / 2 - wy, wx, wy)
  948.     CALL NewBrickLine(-_WIDTH / 2 + wx, -_HEIGHT / 2 + wy, _WIDTH / 2 - wx, -_HEIGHT / 2 + wy, wx, wy)
  949.     wx = 40
  950.     wy = 10
  951.     CALL NewBrickLine(-_WIDTH / 2 + wy, -_HEIGHT / 2 + 2 * wx, -_WIDTH / 2 + wy, _HEIGHT / 2 - 2 * wx, wx, wy)
  952.     CALL NewBrickLine(_WIDTH / 2 - wy, -_HEIGHT / 2 + 2 * wx, _WIDTH / 2 - wy, _HEIGHT / 2 - 2 * wx, wx, wy)
  953.  
  954.     ' Wacky balls
  955.     x0 = -70
  956.     y0 = 120
  957.     r1 = 15
  958.     r2 = 2.5
  959.     gg = 2.5 * (r1 + r2) + 3.5
  960.     xf = COS(30 * 3.14159 / 180)
  961.     yf = SIN(30 * 3.14159 / 180)
  962.     gx = gg * xf
  963.     gy = gg * yf
  964.     CALL NewAutoBall(x0 + 0 * gx, y0 + 0 * gy, r1, r2, INT(RND * 3) + 1, INT(RND * 1) + 2, 0)
  965.     CALL NewAutoBall(x0 + 1 * gx, y0 + 1 * gy, r1, r2, INT(RND * 3) + 1, INT(RND * 1) + 2, 0)
  966.     CALL NewAutoBall(x0 + 1 * gx, y0 - 1 * gy, r1, r2, INT(RND * 3) + 1, INT(RND * 1) + 2, 0)
  967.     CALL NewAutoBall(x0 + 2 * gx, y0 + 2 * gy, r1, r2, INT(RND * 3) + 1, INT(RND * 1) + 2, 0)
  968.     CALL NewAutoBall(x0 + 2 * gx, y0 + 0 * gy, r1, r2, INT(RND * 3) + 1, INT(RND * 1) + 2, 0)
  969.     CALL NewAutoBall(x0 + 2 * gx, y0 - 2 * gy, r1, r2, INT(RND * 3) + 1, INT(RND * 1) + 2, 0)
  970.     CALL NewAutoBall(x0 + 3 * gx, y0 + 3 * gy, r1, r2, INT(RND * 3) + 1, INT(RND * 1) + 2, 0)
  971.     CALL NewAutoBall(x0 + 3 * gx, y0 + 1 * gy, r1, r2, INT(RND * 3) + 1, INT(RND * 1) + 2, 0)
  972.     CALL NewAutoBall(x0 + 3 * gx, y0 - 1 * gy, r1, r2, INT(RND * 3) + 1, INT(RND * 1) + 2, 0)
  973.     CALL NewAutoBall(x0 + 3 * gx, y0 - 3 * gy, r1, r2, INT(RND * 3) + 1, INT(RND * 1) + 2, 0)
  974.  
  975.     ' Slanted bricks
  976.     wx = 60
  977.     wy = 10
  978.     ww = SQR(wx * wx + wy * wy) * .85
  979.     CALL NewBrickLine(ww, 0, 100 + ww, 100, wx, wy)
  980.     CALL NewBrickLine(-ww, 0, -100 - ww, 100, wx, wy)
  981.  
  982.     ' Fidget spinner
  983.     CALL NewAutoBall(-220, 0, 20, 15, 1.5, 2, 0)
  984.     Shape(ShapeCount).Shade = _RGB(255, 255, 255)
  985.  
  986.     ' Parameters
  987.     CPC = 1.15
  988.     FPC = 8
  989.     RST = 0.70
  990.     VD = 0.995
  991.     SV = 0.025
  992.  
  993. SUB SetupRings
  994.     ' Set external field
  995.     ForceField.x = 0
  996.     ForceField.y = 0
  997.  
  998.     ' Rectangular border
  999.     wx = 42
  1000.     wy = 10
  1001.     CALL NewBrickLine(-_WIDTH / 2 + wx, _HEIGHT / 2 - wy, _WIDTH / 2 - wx, _HEIGHT / 2 - wy, wx, wy)
  1002.     CALL NewBrickLine(-_WIDTH / 2 + wx, -_HEIGHT / 2 + wy, _WIDTH / 2 - wx, -_HEIGHT / 2 + wy, wx, wy)
  1003.     wx = 40
  1004.     wy = 10
  1005.     CALL NewBrickLine(-_WIDTH / 2 + wy, -_HEIGHT / 2 + 2 * wx, -_WIDTH / 2 + wy, _HEIGHT / 2 - 2 * wx, wx, wy)
  1006.     CALL NewBrickLine(_WIDTH / 2 - wy, -_HEIGHT / 2 + 2 * wx, _WIDTH / 2 - wy, _HEIGHT / 2 - 2 * wx, wx, wy)
  1007.  
  1008.     FOR r = 25 TO 175 STEP 25
  1009.         CALL NewAutoBall(0, 0, r, 0, 1, 1, 0)
  1010.     NEXT
  1011.  
  1012.     ' Parameters
  1013.     CPC = 1.15
  1014.     FPC = 8
  1015.     RST = 0.75
  1016.     VD = 0.995
  1017.     SV = 0.025
  1018.  
  1019. SUB SetupWallsOnly
  1020.     ' Set external field
  1021.     ForceField.x = 0
  1022.     ForceField.y = 0 - .08
  1023.  
  1024.     ' Fidget spinner
  1025.     CALL NewAutoBall(-220, 0, 20, 15, 1.5, 2, 0)
  1026.     Shape(ShapeCount).Shade = _RGB(255, 255, 255)
  1027.  
  1028.     ' Rectangular border
  1029.     wx = 42
  1030.     wy = 10
  1031.     CALL NewBrickLine(-_WIDTH / 2 + wx, _HEIGHT / 2 - wy, _WIDTH / 2 - wx, _HEIGHT / 2 - wy, wx, wy)
  1032.     CALL NewBrickLine(-_WIDTH / 2 + wx, -_HEIGHT / 2 + wy, _WIDTH / 2 - wx, -_HEIGHT / 2 + wy, wx, wy)
  1033.     wx = 40
  1034.     wy = 10
  1035.     CALL NewBrickLine(-_WIDTH / 2 + wy, -_HEIGHT / 2 + 2 * wx, -_WIDTH / 2 + wy, _HEIGHT / 2 - 2 * wx, wx, wy)
  1036.     CALL NewBrickLine(_WIDTH / 2 - wy, -_HEIGHT / 2 + 2 * wx, _WIDTH / 2 - wy, _HEIGHT / 2 - 2 * wx, wx, wy)
  1037.  
  1038.     ' Parameters
  1039.     CPC = 1.15
  1040.     FPC = 8
  1041.     RST = 0.75
  1042.     VD = 0.995
  1043.     SV = 0.025
  1044.  
  1045. SUB SetupAnglePong
  1046.     ' Set external field
  1047.     ForceField.x = 0
  1048.     ForceField.y = 0
  1049.  
  1050.     ' Rectangular border
  1051.     wx = 42
  1052.     wy = 10
  1053.     CALL NewBrickLine(-_WIDTH / 2 + wx, _HEIGHT / 2 - wy, _WIDTH / 2 - wx, _HEIGHT / 2 - wy, wx, wy)
  1054.     CALL NewBrickLine(-_WIDTH / 2 + wx, -_HEIGHT / 2 + wy, _WIDTH / 2 - wx, -_HEIGHT / 2 + wy, wx, wy)
  1055.     wx = 40
  1056.     wy = 10
  1057.     CALL NewBrickLine(-_WIDTH / 2 + wy, -_HEIGHT / 2 + 2 * wx, -_WIDTH / 2 + wy, _HEIGHT / 2 - 2 * wx, wx, wy)
  1058.     CALL NewBrickLine(_WIDTH / 2 - wy, -_HEIGHT / 2 + 2 * wx, _WIDTH / 2 - wy, _HEIGHT / 2 - 2 * wx, wx, wy)
  1059.  
  1060.     ' Pong ball
  1061.     CALL NewAutoBall(0, 200, 20, 0, 1, 1, 0)
  1062.     Shape(ShapeCount).Velocity.x = -1
  1063.     Shape(ShapeCount).Velocity.y = -3
  1064.     Shape(ShapeCount).Shade = _RGB(255, 255, 255)
  1065.  
  1066.     ' Pong Paddle
  1067.     CALL NewAutoBrick(-100, 10, 100, -10, .1)
  1068.     vtemp.x = 0
  1069.     vtemp.y = -200
  1070.     CALL TranslateShape(ShapeCount, vtemp)
  1071.     Shape(ShapeCount).Shade = _RGB(200, 200, 200)
  1072.  
  1073.     ' Parameters
  1074.     CPC = 1.15
  1075.     FPC = 8
  1076.     RST = 1 '0.75
  1077.     VD = 1 '0.995
  1078.     SV = 0.025
crazypool.png
* crazypool.png (Filesize: 20.85 KB, Dimensions: 800x600, Views: 198)
manyballs.png
* manyballs.png (Filesize: 26.92 KB, Dimensions: 800x600, Views: 208)
ringsinrings.png
* ringsinrings.png (Filesize: 10.69 KB, Dimensions: 800x600, Views: 199)
« Last Edit: March 10, 2020, 07:33:54 am by STxAxTIC »
You're not done when it works, you're done when it's right.

Offline Ashish

  • Forum Resident
  • Posts: 630
  • Never Give Up!
    • View Profile
Re: Collisions - Version 9
« Reply #1 on: March 06, 2020, 12:40:28 am »
Nicee... Amazing, I like how it do collision detection for my custom non-regular shapes... But sometime shape overlap each other & do weird things.
if (Me.success) {Me.improve()} else {Me.tryAgain()}


My Projects - https://github.com/AshishKingdom?tab=repositories
OpenGL tutorials - https://ashishkingdom.github.io/OpenGL-Tutorials

Offline STxAxTIC

  • Library Staff
  • Forum Resident
  • Posts: 1091
  • he lives
    • View Profile
Re: Collisions - Version 9
« Reply #2 on: March 06, 2020, 06:08:52 am »
Nicee... Amazing, I like how it do collision detection for my custom non-regular shapes... But sometime shape overlap each other & do weird things.

Theres always one isnt there? I dont blame you for not catching my warning:
Quote
I have done absolutely nothing to fine-tune this engine. Everything it does so far is a product of its prototype parts working together. If one were to use this to make one particular game, some time would be spent getting the adjustable parameters correct. With that said, it's still possible to make things fly too fast, send objects through each other, etc - so nobody complain about this! Just be gentle and get a feel for it.
« Last Edit: March 06, 2020, 06:11:55 am by STxAxTIC »
You're not done when it works, you're done when it's right.

Offline Ashish

  • Forum Resident
  • Posts: 630
  • Never Give Up!
    • View Profile
Re: Collisions - Version 9
« Reply #3 on: March 06, 2020, 06:54:08 am »
Oh! I was too lazy to read whole post.
if (Me.success) {Me.improve()} else {Me.tryAgain()}


My Projects - https://github.com/AshishKingdom?tab=repositories
OpenGL tutorials - https://ashishkingdom.github.io/OpenGL-Tutorials

Offline _vince

  • Seasoned Forum Regular
  • Posts: 422
    • View Profile
Re: Collisions - Version 9
« Reply #4 on: March 06, 2020, 10:37:40 pm »
nice mouse controls and centroids feels at home

Offline STxAxTIC

  • Library Staff
  • Forum Resident
  • Posts: 1091
  • he lives
    • View Profile
Re: Collisions - Version 9
« Reply #5 on: March 06, 2020, 10:43:45 pm »
Thanks _vince!

I admitted that the mass and centroid calculations are a slight bit fake, but I can make that perfect later. Tee hee hee. Anyway, the reason I checked this thread was to show off a video I just took. Same engine posted above except I added gravity to option 4. Check out this "catapult" demo:

https://www.youtube.com/watch?v=deW8EqbvWvc
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: Collisions - Version 9
« Reply #6 on: March 06, 2020, 10:48:11 pm »
Cool! Catapult

Offline STxAxTIC

  • Library Staff
  • Forum Resident
  • Posts: 1091
  • he lives
    • View Profile
Re: Collisions - Version 9
« Reply #7 on: March 08, 2020, 03:27:57 pm »
Hello guys,

So I decided to experiment by re-adding some deleted code to this engine that "dents inward" each piece when it collides. While this will be turned off in most cases for obvious reasons, I had to grab a video of it. In this link you can see some odd trinkets grinding away at each other as they tumble down. Then I grab the "fidget spinner", grind and end off, and you can watch it squeeze through the funnel as a sharp corner shaves down the rest.

https://youtu.be/H5zuL7YcBh8
« Last Edit: March 08, 2020, 03:29:19 pm by STxAxTIC »
You're not done when it works, you're done when it's right.

Offline STxAxTIC

  • Library Staff
  • Forum Resident
  • Posts: 1091
  • he lives
    • View Profile
Re: Collisions - Version 9
« Reply #8 on: March 10, 2020, 07:16:57 am »
Good morning,

As a hat tip to what's going on at Angle Pong (https://www.qb64.org/forum/index.php?topic=2309.0), and also as a reality check of my own works, I bring you the angle pong update below. (I quietly updated a few things in the engine, including the way hand-drawn shapes are smoothed. There should be no more sharp edges on those, and it plays a bit nicer now.)

Anyway... You can move the paddle by dragging with mouse 1. Rotate it with mouse wheel. I'll caution you that if the paddle is moving while a collision takes place, the ball will go FLYING and it becomes more like breakout. This is because I didn't hard-code the paddle as such - its just an infinitely heavy rectangle-shaped ball. (The player is a strange god in this universe that can move heavy objects.) So treat it gently and you have angle pong:

... actually - Just see top post and run option 5.
« Last Edit: March 10, 2020, 07:30:11 am by STxAxTIC »
You're not done when it works, you're done when it's right.