1
- use std::u8;
2
1
use macroquad::prelude::*;
2
+ extern crate micromath;
3
+ use micromath::F32Ext;
4
+
5
+ fn clip(pos: f32, bound: f32, velocity: f32) -> f32 {
6
+ if pos > bound {
7
+ let delta = (pos - bound).abs();
8
+ let mut new_velocity = velocity;
9
+ if velocity > 0.0 {
10
+ new_velocity = -velocity
11
+ }
12
+ if (new_velocity.abs() + BALL_RADIUS) < delta {
13
+ return -delta / get_fps() as f32 * 60.0;
14
+ }
15
+ return new_velocity * FLOOR_LOSS;
16
+ }
17
+ velocity
18
+ }
19
+
20
+ fn clip_minus(pos: f32, bound: f32, velocity: f32) -> f32 {
21
+ return -clip(bound, pos, -velocity);
22
+ }
3
23
4
24
#[derive(Debug, Clone, Copy)]
5
25
pub struct Ball {
@@ -12,109 +32,144 @@ pub struct Ball {
12
32
}
13
33
14
34
// Customizable
15
- const FLOOR_LOSS: f32 = 0.98 ;
35
+ const FLOOR_LOSS: f32 = 0.9 ;
16
36
const DRAG: f32 = 0.998;
17
37
const GRAVITY: f32 = 1.0;
38
+ const MAX_VEL: f32 = 40.0;
18
39
19
- const BALL_RADIUS: f32 = 6 .0;
40
+ const BALL_RADIUS: f32 = 5 .0;
20
41
21
42
// Please, don't change
22
- static mut BALL_ID : usize = 0;
23
- const BALL_RADIUS_SQR: f32= BALL_RADIUS * BALL_RADIUS;
43
+ static mut BALL_ID: usize = 0;
44
+ const BALL_RADIUS_SQR: f32 = BALL_RADIUS * BALL_RADIUS;
24
45
25
46
impl Ball {
26
- fn new(x: f32, y:f32, seed:usize) -> Ball{
47
+ fn new(x: f32, y: f32, seed: usize) -> Ball {
27
48
let id;
28
49
unsafe {
29
50
id = BALL_ID;
30
51
BALL_ID += 1;
31
52
}
32
53
33
- let seed = seed % (usize::MAX/ 64);
34
- let random = (32_1239 * seed* seed* seed* 17 + id)% 1000 ;
54
+ let seed = seed % (usize::MAX / 64);
55
+ let random = (32_1239 * seed * seed * seed * 17 + id) % 1000;
35
56
let x_vel = random % 9;
36
- let x_vel = (x_vel as f32 - 4.0) / 2.0;
37
-
38
- return Ball{id, x, y, x_vel, y_vel:0.0, color:Color::from_rgba((random as usize*47%255) as u8, (random as usize*29%255) as u8, (random as usize*101%255) as u8, u8::MAX)}
57
+ let x_vel = (x_vel as f32 - 4.0) / 1.5;
58
+
59
+ return Ball {
60
+ id,
61
+ x,
62
+ y,
63
+ x_vel,
64
+ y_vel: 0.0,
65
+ color: Color::from_rgba(
66
+ (random as usize * 47 % 255) as u8,
67
+ (random as usize * 29 % 255) as u8,
68
+ (random as usize * 101 % 255) as u8,
69
+ 255,
70
+ ),
71
+ };
39
72
}
40
73
41
- fn tick(&mut self){
74
+ fn tick(&mut self) {
42
75
self.x_vel *= DRAG;
43
76
self.y_vel *= DRAG;
44
77
45
78
self.x += self.x_vel;
46
79
self.y += self.y_vel;
47
80
48
- if self.y > screen_height(){
49
- // self.y = screen_height();
50
- self.y_vel = -self.y_vel * FLOOR_LOSS;
51
- }else{
52
- self.y_vel += GRAVITY;
53
- }
54
-
55
- if self.x < 0.0 || self.x > screen_width(){
56
- self.x_vel = -self.x_vel * FLOOR_LOSS;
57
- }
81
+ self.x = self.x.clamp(-3000.0, 3000.0);
82
+ self.x = if self.x.is_normal() { self.x } else { 0.0 };
83
+ self.y = self.y.clamp(-3000.0, 3000.0);
84
+ self.y = if self.y.is_normal() { self.y } else { 0.0 };
85
+ self.y_vel = self.y_vel.clamp(-MAX_VEL, MAX_VEL);
86
+ self.y_vel = if self.y_vel.is_normal() {
87
+ self.y_vel
88
+ } else {
89
+ 0.0
90
+ };
91
+ self.x_vel = self.x_vel.clamp(-MAX_VEL, MAX_VEL);
92
+ self.x_vel = if self.x_vel.is_normal() {
93
+ self.x_vel
94
+ } else {
95
+ 0.0
96
+ };
97
+
98
+ self.y_vel = clip(self.y, screen_height(), self.y_vel + GRAVITY);
99
+ self.x_vel = clip(self.x, screen_width(), self.x_vel);
100
+ self.x_vel = clip_minus(self.x, 0.0, self.x_vel);
101
+ self.y_vel = clip_minus(self.y, -screen_height(), self.y_vel);
58
102
}
59
103
60
- fn draw(&self){
61
- draw_circle(self.x, self.y- BALL_RADIUS, BALL_RADIUS, self.color);
104
+ fn draw(&self) {
105
+ draw_circle(self.x, self.y - BALL_RADIUS, BALL_RADIUS, self.color);
62
106
}
63
107
64
108
fn collide(&mut self, balls: &[Ball]) {
65
- for other in balls {
66
- if other.id == self.id {
67
- continue;
68
- }
109
+ unsafe {
110
+ let skip = (BALL_ID / 1000).max(8);
69
111
70
- let dx = self.x - other.x;
71
- let dy = self.y - other.y;
72
- let distance_sqr = dx * dx + dy * dy;
112
+ for other in balls {
113
+ if other.id == self.id || other.id % 10 < skip {
114
+ continue;
115
+ }
73
116
74
- if distance_sqr > BALL_RADIUS_SQR {
75
- continue;
76
- }
117
+ let dx = self.x - other.x;
118
+ let dy = self.y - other.y;
119
+ let distance_sqr = dx * dx + dy * dy;
77
120
78
- let distance = distance_sqr.sqrt();
79
- if distance == 0.0 {
80
- continue;
81
- }
82
- let nx = dx / distance;
83
- let ny = dy / distance;
121
+ if distance_sqr > BALL_RADIUS_SQR {
122
+ continue;
123
+ }
84
124
85
- let relative_velocity_x = self.x_vel - other.x_vel;
86
- let relative_velocity_y = self.y_vel - other.y_vel;
87
- let relative_velocity_normal = relative_velocity_x * nx + relative_velocity_y * ny;
125
+ let distance = distance_sqr.sqrt();
126
+ if distance == 0.0 {
127
+ continue;
128
+ }
129
+ let nx = dx / distance;
130
+ let ny = dy / distance;
88
131
89
- if relative_velocity_normal > 0.0 {
90
- continue ;
91
- }
132
+ let relative_velocity_x = self.x_vel - other.x_vel;
133
+ let relative_velocity_y = self.y_vel - other.y_vel ;
134
+ let relative_velocity_normal = relative_velocity_x * nx + relative_velocity_y * ny;
92
135
93
- let impulse = (2.0 * relative_velocity_normal) / 2.0 * FLOOR_LOSS;
136
+ if relative_velocity_normal > 0.0 {
137
+ continue;
138
+ }
94
139
95
- self.x_vel -= impulse * nx;
96
- self.y_vel -= impulse * ny;
140
+ let impulse = (2.0 * relative_velocity_normal) / 2.0 * FLOOR_LOSS;
141
+
142
+ self.x_vel -= (impulse * nx).clamp(-MAX_VEL, MAX_VEL);
143
+ self.y_vel -= (impulse * ny).clamp(-MAX_VEL, MAX_VEL);
144
+ }
97
145
}
98
146
}
99
-
100
147
}
101
148
102
149
#[macroquad::main("AM - Confetti")]
103
150
async fn main() {
104
-
105
151
let mut balls: Vec<Ball> = Vec::new();
106
152
let mut frame_count: usize = 0;
107
153
loop {
108
154
// Loop start
109
155
frame_count += 1;
110
- clear_background(Color { r: 0.95, g: 0.9, b: 0.9, a: 1.0 });
156
+ clear_background(Color {
157
+ r: 0.95,
158
+ g: 0.9,
159
+ b: 0.9,
160
+ a: 1.0,
161
+ });
111
162
112
163
// Handle Inputs
113
- if is_mouse_button_down(MouseButton::Left) || is_mouse_button_pressed(MouseButton::Right) {
114
- let (mouse_x,mouse_y) = mouse_position();
115
- balls.push(Ball::new(mouse_x, mouse_y ,frame_count))
164
+ if is_mouse_button_down(MouseButton::Left) {
165
+ let (mouse_x, mouse_y) = mouse_position();
166
+ balls.push(Ball::new(mouse_x, mouse_y, frame_count));
167
+ balls.push(Ball::new(mouse_x, mouse_y, frame_count));
168
+ }
169
+ if is_mouse_button_pressed(MouseButton::Right) {
170
+ let (mouse_x, mouse_y) = mouse_position();
171
+ balls.push(Ball::new(mouse_x, mouse_y, frame_count))
116
172
}
117
-
118
173
if is_key_pressed(KeyCode::Space) {
119
174
unsafe {
120
175
BALL_ID = 0;
@@ -123,24 +178,44 @@ async fn main() {
123
178
}
124
179
125
180
// Handle Tick
126
- let balls_prev = balls.to_vec();
181
+ let balls_prev = balls.to_vec();
127
182
for ball in &mut balls {
128
183
ball.collide(&balls_prev);
129
184
ball.tick();
130
185
ball.draw();
131
- }
186
+ }
132
187
133
188
unsafe {
134
189
if BALL_ID == 0 {
135
- draw_text("Click anywhere to begin!", screen_width()/2.0 - 240.0, screen_height()/2.0, 48.0, RED);
136
- }else{
137
- draw_text(&format!("Balls{}: {}", if BALL_ID > 999 {" (Press Space to Reset)"} else {""}, BALL_ID), 15.0, 25.0, 32.0, BLACK);
138
- }
190
+ draw_text(
191
+ "Click anywhere to begin!",
192
+ screen_width() / 2.0 - 240.0,
193
+ screen_height() / 2.0,
194
+ 48.0,
195
+ RED,
196
+ );
197
+ } else {
198
+ draw_text(
199
+ &format!(
200
+ "Balls: {}\nFPS: {} {}",
201
+ BALL_ID,
202
+ get_fps(),
203
+ if BALL_ID > 1000 {
204
+ "(Press Space to Reset)"
205
+ } else {
206
+ ""
207
+ }
208
+ ),
209
+ 15.0,
210
+ 25.0,
211
+ 32.0,
212
+ BLACK,
213
+ );
214
+ }
139
215
}
140
216
141
217
next_frame().await
142
218
}
143
219
}
144
-
145
220
// Andrew McCall <3
146
221
// https://github.com/Andrew-McCall/MacroquadConfetti
0 commit comments