shapes_ball_physics.c 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. /*******************************************************************************************
  2. *
  3. * raylib [shapes] example - ball physics
  4. *
  5. * Example complexity rating: [★★☆☆] 2/4
  6. *
  7. * Example originally created with raylib 5.6-dev, last time updated with raylib 5.6-dev
  8. *
  9. * Example contributed by David Buzatto (@davidbuzatto) and reviewed by Ramon Santamaria (@raysan5)
  10. *
  11. * Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
  12. * BSD-like license that allows static linking with closed source software
  13. *
  14. * Copyright (c) 2025 David Buzatto (@davidbuzatto)
  15. *
  16. ********************************************************************************************/
  17. #include "raylib.h"
  18. #include <stdlib.h>
  19. #include <math.h>
  20. #define MAX_BALLS 5000 // Maximum quantity of balls
  21. typedef struct Ball {
  22. Vector2 pos; // Position
  23. Vector2 vel; // Velocity
  24. Vector2 ppos; // Previous position
  25. float radius;
  26. float friction;
  27. float elasticity;
  28. Color color;
  29. bool grabbed;
  30. } Ball;
  31. //------------------------------------------------------------------------------------
  32. // Program main entry point
  33. //------------------------------------------------------------------------------------
  34. int main(void)
  35. {
  36. // Initialization
  37. //--------------------------------------------------------------------------------------
  38. const int screenWidth = 800;
  39. const int screenHeight = 450;
  40. InitWindow(screenWidth, screenHeight, "raylib [shapes] example - ball physics");
  41. Ball balls[MAX_BALLS] = {{
  42. .pos = { GetScreenWidth()/2, GetScreenHeight()/2 },
  43. .vel = { 200, 200 },
  44. .ppos = { 0 },
  45. .radius = 40,
  46. .friction = 0.99,
  47. .elasticity = 0.9,
  48. .color = BLUE,
  49. .grabbed = false
  50. }};
  51. int ballCount = 1;
  52. Ball *grabbedBall = NULL; // A pointer to the current ball that is grabbed
  53. Vector2 pressOffset = {0}; // Mouse press offset relative to the ball that grabbedd
  54. float gravity = 100; // World gravity
  55. SetTargetFPS(60); // Set our game to run at 60 frames-per-second
  56. //---------------------------------------------------------------------------------------
  57. // Main game loop
  58. while (!WindowShouldClose()) // Detect window close button or ESC key
  59. {
  60. // Update
  61. //----------------------------------------------------------------------------------
  62. float delta = GetFrameTime();
  63. Vector2 mousePos = GetMousePosition();
  64. // Checks if a ball was grabbed
  65. if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT))
  66. {
  67. for (int i = ballCount - 1; i >= 0; i--)
  68. {
  69. Ball *ball = &balls[i];
  70. pressOffset.x = mousePos.x - ball->pos.x;
  71. pressOffset.y = mousePos.y - ball->pos.y;
  72. // If the distance between the ball position and the mouse press position
  73. // is less than or equal to the ball radius, the event occurred inside the ball
  74. if (hypot(pressOffset.x, pressOffset.y) <= ball->radius)
  75. {
  76. ball->grabbed = true;
  77. grabbedBall = ball;
  78. break;
  79. }
  80. }
  81. }
  82. // Releases any ball the was grabbed
  83. if (IsMouseButtonReleased(MOUSE_BUTTON_LEFT))
  84. {
  85. if (grabbedBall != NULL)
  86. {
  87. grabbedBall->grabbed = false;
  88. grabbedBall = NULL;
  89. }
  90. }
  91. // Creates a new ball
  92. if (IsMouseButtonPressed(MOUSE_BUTTON_RIGHT) || (IsKeyDown(KEY_LEFT_CONTROL) && IsMouseButtonDown(MOUSE_BUTTON_RIGHT)))
  93. {
  94. if (ballCount < MAX_BALLS)
  95. {
  96. balls[ballCount++] = (Ball){
  97. .pos = mousePos,
  98. .vel = { GetRandomValue(-300, 300), GetRandomValue(-300, 300) },
  99. .ppos = { 0 },
  100. .radius = 20 + GetRandomValue(0, 30),
  101. .friction = 0.99,
  102. .elasticity = 0.9,
  103. .color = { GetRandomValue(0, 255), GetRandomValue(0, 255), GetRandomValue(0, 255), 255 },
  104. .grabbed = false
  105. };
  106. }
  107. }
  108. // Shake balls
  109. if (IsMouseButtonPressed(MOUSE_BUTTON_MIDDLE))
  110. {
  111. for (int i = 0; i < ballCount; i++)
  112. {
  113. if (!balls[i].grabbed) balls[i].vel = (Vector2){ GetRandomValue(-2000, 2000), GetRandomValue(-2000, 2000) };
  114. }
  115. }
  116. // Changes gravity
  117. gravity += GetMouseWheelMove()*5;
  118. // Updates each ball state
  119. for (int i = 0; i < ballCount; i++)
  120. {
  121. Ball *ball = &balls[i];
  122. // The ball is not grabbed
  123. if (!ball->grabbed)
  124. {
  125. // Ball repositioning using the velocity
  126. ball->pos.x += ball->vel.x * delta;
  127. ball->pos.y += ball->vel.y * delta;
  128. // Does the ball hit the screen right boundary?
  129. if ((ball->pos.x + ball->radius) >= screenWidth)
  130. {
  131. ball->pos.x = screenWidth - ball->radius; // Ball repositioning
  132. ball->vel.x = -ball->vel.x*ball->elasticity; // Elasticity makes the ball lose 10% of its velocity on hit
  133. }
  134. // Does the ball hit the screen left boundary?
  135. else if ((ball->pos.x - ball->radius) <= 0)
  136. {
  137. ball->pos.x = ball->radius;
  138. ball->vel.x = -ball->vel.x*ball->elasticity;
  139. }
  140. // The same for y axis
  141. if ((ball->pos.y + ball->radius) >= screenHeight)
  142. {
  143. ball->pos.y = screenHeight - ball->radius;
  144. ball->vel.y = -ball->vel.y*ball->elasticity;
  145. }
  146. else if ((ball->pos.y - ball->radius) <= 0)
  147. {
  148. ball->pos.y = ball->radius;
  149. ball->vel.y = -ball->vel.y*ball->elasticity;
  150. }
  151. // Friction makes the ball lose 1% of its velocity each frame
  152. ball->vel.x = ball->vel.x*ball->friction;
  153. // Gravity affects only the y axis
  154. ball->vel.y = ball->vel.y*ball->friction + gravity;
  155. }
  156. else
  157. {
  158. // Ball repositioning using the mouse position
  159. ball->pos.x = mousePos.x - pressOffset.x;
  160. ball->pos.y = mousePos.y - pressOffset.y;
  161. // While the ball is grabbed, recalculates its velocity
  162. ball->vel.x = (ball->pos.x - ball->ppos.x)/delta;
  163. ball->vel.y = (ball->pos.y - ball->ppos.y)/delta;
  164. ball->ppos = ball->pos;
  165. }
  166. }
  167. //----------------------------------------------------------------------------------
  168. // Draw
  169. //----------------------------------------------------------------------------------
  170. BeginDrawing();
  171. ClearBackground(RAYWHITE);
  172. for (int i = 0; i < ballCount; i++)
  173. {
  174. DrawCircleV(balls[i].pos, balls[i].radius, balls[i].color);
  175. DrawCircleLinesV(balls[i].pos, balls[i].radius, BLACK);
  176. }
  177. DrawText("grab a ball by pressing with the mouse and throw it by releasing", 10, 10, 10, DARKGRAY);
  178. DrawText("right click to create new balls (keep left control pressed to create a lot)", 10, 30, 10, DARKGRAY);
  179. DrawText("use mouse wheel to change gravity", 10, 50, 10, DARKGRAY);
  180. DrawText("middle click to shake", 10, 70, 10, DARKGRAY);
  181. DrawText(TextFormat("BALL COUNT: %d", ballCount), 10, GetScreenHeight() - 70, 20, BLACK);
  182. DrawText(TextFormat("GRAVITY: %.2f", gravity), 10, GetScreenHeight() - 40, 20, BLACK);
  183. EndDrawing();
  184. //----------------------------------------------------------------------------------
  185. }
  186. // De-Initialization
  187. //--------------------------------------------------------------------------------------
  188. CloseWindow(); // Close window and OpenGL context
  189. //--------------------------------------------------------------------------------------
  190. return 0;
  191. }