Skip to content

Commit 9f567e6

Browse files
authored
Example for creating balls with simple physics simulation (#5372)
* Example for creating balls with simple physics simulation The goal of this example is to create several colored balls whose movement is simulated and which respond to the action of being grabbed and dragged using the mouse. * renaming example renaming example from physics_bouncing_balls to shapes_ball_physics
1 parent e273aae commit 9f567e6

File tree

2 files changed

+222
-0
lines changed

2 files changed

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

0 commit comments

Comments
 (0)