pub fn window_width() -> f32Examples found in repository?
examples/space_game.rs (line 161)
158 fn update(&mut self) -> bool {
159 self.pos += self.vel;
160
161 let half_w = screen_distance_to_world(window_width()) / 2.0;
162 let half_h = screen_distance_to_world(window_height()) / 2.0;
163
164 self.pos.x.abs() <= half_w && self.pos.y.abs() <= half_h
165 }
166
167 fn draw(&self) {
168 draw_circle_world(self.pos, BULLET_RADIUS, FG);
169 }
170}
171
172struct Player {
173 pos: Vec2,
174 rotation: f32,
175 health: usize,
176 last_hit: f32,
177 speed: f32,
178 angvel: f32,
179 bullets: Vec<Bullet>,
180}
181
182impl Player {
183 fn new() -> Self {
184 Self {
185 pos: Vec2::ZERO,
186 rotation: 0.0,
187 health: MAX_PLAYER_HEALTH,
188 last_hit: f32::MIN,
189 speed: 0.0,
190 angvel: 0.0,
191 bullets: vec![],
192 }
193 }
194
195 fn vel(&self) -> Vec2 {
196 self.speed * Vec2::new(self.rotation.sin(), self.rotation.cos())
197 }
198
199 fn update(&mut self) {
200 self.update_input();
201 self.update_velocities();
202 self.pos = wrap_position(self.pos, PLAYER_SIZE);
203
204 for bullet in self.bullets.iter_mut() {
205 bullet.update();
206 }
207 }
208
209 fn emit_movement_particles(&self) {
210 let batch = ParticleOneshot::builder()
211 .shape(&Circle::new(
212 Vec2::ZERO,
213 Vec2::splat(LINE_THICKNESS * 1.5),
214 FG,
215 ))
216 .end_color(RED.with_alpha(0.0))
217 .speed(self.speed * 0.8)
218 .direction(-self.rotation)
219 .direction_randomness(0.2)
220 .speed_randomness(self.speed * 0.5)
221 .lifetime(0.8)
222 .lifetime_randomness(0.4)
223 .quantity((self.speed * 5.0).max(1.0) as usize)
224 .position_randomness(Vec2::splat(PLAYER_SIZE * 0.3))
225 .build();
226 get_particles().spawn_oneshot(
227 &batch,
228 self.pos + vec2(0.0, -PLAYER_SIZE * 0.3).rotated_around_origin(-self.rotation),
229 );
230 }
231
232 fn update_input(&mut self) {
233 if action_held(UP) {
234 self.speed += PLAYER_ACCEL;
235 self.emit_movement_particles();
236
237 if once_per_n_seconds(0.1) {
238 play_sound_ex(get_sounds().engine).volume(0.1).start();
239 }
240 }
241
242 if action_held(DOWN) {
243 self.speed -= PLAYER_ACCEL;
244 }
245
246 if action_held(RIGHT) {
247 self.angvel -= PLAYER_TURN_SPEED;
248 }
249
250 if action_held(LEFT) {
251 self.angvel += PLAYER_TURN_SPEED;
252 }
253
254 if action_pressed(SHOOT) {
255 play_sound(get_sounds().shoot);
256 let tip = self.pos + vec2(0.0, PLAYER_SIZE).rotated_around_origin(-self.rotation);
257 self.bullets
258 .push(Bullet::new(tip, self.rotation, self.vel()));
259 }
260 }
261
262 fn update_velocities(&mut self) {
263 self.pos += self.vel();
264 self.rotation += self.angvel;
265
266 self.speed *= PLAYER_LIN_DECEL;
267 self.angvel *= PLAYER_ANG_DECEL;
268
269 if self.speed.abs() < 0.1 {
270 self.speed = 0.0;
271 }
272
273 if self.angvel.abs() < 0.1 {
274 self.angvel = 0.0;
275 }
276 }
277
278 fn draw(&self) {
279 for bullet in &self.bullets {
280 bullet.draw();
281 }
282
283 let c = self.pos;
284 let tip = c + vec2(0.0, PLAYER_SIZE);
285 let elbow = c + vec2(0.0, -PLAYER_SIZE * 0.5);
286 let left_wing = c + vec2(-PLAYER_SIZE, -PLAYER_SIZE);
287 let right_wing = c + vec2(PLAYER_SIZE, -PLAYER_SIZE);
288
289 let now = time();
290 let time = (now - self.last_hit) * 10.0;
291 let blink_on = time as i32 % 2 == 0;
292 let currently_invincible = now - self.last_hit < PLAYER_INVINCIBILITY_DURATION;
293 let color = if currently_invincible && blink_on {
294 FG.with_alpha(0.3)
295 } else {
296 FG
297 };
298
299 if currently_invincible && blink_on {
300 vignette_screen(RED, 0.5);
301 }
302
303 draw_circle_path_world(
304 &[tip, left_wing, elbow, right_wing, tip]
305 .map(|p| p.rotated_around_point(c, -self.rotation)),
306 LINE_THICKNESS,
307 color,
308 );
309
310 self.draw_healthbar();
311 }
312
313 fn draw_healthbar(&self) {
314 let width = PLAYER_HEALTHBAR_WIDTH / MAX_PLAYER_HEALTH as f32;
315 let height = PLAYER_HEALTHBAR_HEIGHT;
316
317 for i in 0..self.health {
318 let offset = (width + PLAYER_HEALTHBAR_PADDING) * i as f32 + PLAYER_HEALTHBAR_PADDING;
319 draw_rect(
320 vec2(offset, window_height() - height - PLAYER_HEALTHBAR_PADDING),
321 vec2(width, height),
322 FG,
323 );
324 }
325 }
326}
327
328struct Asteroid {
329 health: usize,
330 max_health: usize,
331 last_hit: f32,
332 points: Vec<Vec2>,
333 pos: Vec2,
334 velocity: Vec2,
335 rotation: f32,
336 angvel: f32,
337 radius: f32,
338}
339
340impl Asteroid {
341 fn new() -> Self {
342 let radius = rand_range(MIN_ASTEROID_RADIUS..MAX_ASTEROID_RADIUS);
343 let health = (radius / MIN_ASTEROID_RADIUS) as usize * 2;
344 let last_hit = f32::MIN;
345 let pos = rand_vec2() * BASE_WINDOW_SIZE / 2.0;
346 let velocity = rand_vec2() * MAX_ASTEROID_SPEED;
347 let rotation = rand_f32() * TAU;
348 let angvel = rand_f32() * MAX_ASTEROID_ANGVEL;
349
350 let num_points = (radius * MAX_ASTEROID_POINTS) as usize;
351 let max_offset = radius * MAX_ASTEROID_POINT_OFFSET / 2.0;
352
353 let mut points = Vec::with_capacity(num_points + 1);
354
355 for i in 0..num_points {
356 let angle = (TAU / num_points as f32) * i as f32;
357 let offset = rand_range(-max_offset..max_offset) + radius;
358 let pos = Vec2::new(0.0, offset).rotated_around_origin(angle);
359 points.push(pos);
360 }
361
362 points.push(points[0]);
363
364 Self {
365 health,
366 last_hit,
367 points,
368 pos,
369 velocity,
370 rotation,
371 angvel,
372 radius,
373 max_health: health,
374 }
375 }
376
377 fn update(&mut self) {
378 self.pos += self.velocity;
379 self.rotation += self.angvel;
380 self.pos = wrap_position(self.pos, self.radius);
381 }
382
383 fn draw(&self) {
384 let points = &self
385 .points
386 .iter()
387 .map(|p| p.rotated_around_origin(self.rotation) + self.pos)
388 .collect::<Vec<_>>();
389
390 let blinking = time() - self.last_hit < ASTEROID_BLINK_DURATION;
391 let fill = if blinking { FG } else { BG };
392
393 draw_custom_shape_world(points, fill);
394 draw_circle_path_world(points, LINE_THICKNESS, FG);
395 }
396}
397
398struct Sounds {
399 shoot: SoundRef,
400 asteroid_hit: SoundRef,
401 asteroid_death: SoundRef,
402 player_hit: SoundRef,
403 game_over: SoundRef,
404 engine: SoundRef,
405}
406
407impl Sounds {
408 fn new() -> Result<Self, SoundLoadError> {
409 Ok(Self {
410 shoot: include_sound!("../assets/sounds/Gun.wav")?,
411 asteroid_hit: include_sound!("../assets/sounds/Click.wav")?,
412 asteroid_death: include_sound!("../assets/sounds/Explosion.wav")?,
413 player_hit: include_sound!("../assets/sounds/Hurt.wav")?,
414 game_over: include_sound!("../assets/sounds/Powerdown.wav")?,
415 engine: include_sound!("../assets/sounds/Crunch.wav")?,
416 })
417 }
418}
419
420#[main("Space game")]
421fn main() -> anyhow::Result<()> {
422 storage_store_state(ParticleSystem::new());
423 storage_store_state(Sounds::new()?);
424
425 bind! {
426 UP => KeyCode::ArrowUp;
427 UP => KeyCode::KeyW;
428 DOWN => KeyCode::ArrowDown;
429 DOWN => KeyCode::KeyS;
430 LEFT => KeyCode::ArrowLeft;
431 LEFT => KeyCode::KeyA;
432 RIGHT => KeyCode::ArrowRight;
433 RIGHT => KeyCode::KeyD;
434 SHOOT => KeyCode::Space;
435 }
436
437 let mut state = State::new();
438
439 loop {
440 clear_screen(BG);
441 let scale = window_size() / BASE_WINDOW_SIZE * BASE_SCALE;
442 let scale = scale.x.min(scale.y);
443 get_camera_2d_mut().set_scale(scale);
444
445 state.update();
446
447 get_particles().update();
448 get_particles().draw_world();
449 state.draw();
450
451 if should_quit() {
452 break;
453 }
454
455 next_frame().await;
456 }
457
458 Ok(())
459}
460
461fn get_particles() -> &'static mut ParticleSystem {
462 storage_get_state_mut()
463}
464
465fn get_sounds() -> &'static Sounds {
466 storage_get_state()
467}
468
469fn spawn_hit_particles(pos: Vec2, impact_dir: Vec2) {
470 play_sound(get_sounds().asteroid_hit);
471 let batch = ParticleOneshot::builder()
472 .shape(&Circle::new(
473 Vec2::ZERO,
474 Vec2::splat(LINE_THICKNESS * 1.5),
475 FG.with_alpha(0.5),
476 ))
477 .end_color(FG.with_alpha(0.0))
478 .speed(3.0)
479 .direction(impact_dir.to_angle() + FRAC_PI_2)
480 .direction_randomness(1.5)
481 .speed_randomness(2.0)
482 .lifetime(0.4)
483 .lifetime_randomness(0.2)
484 .quantity(8)
485 .build();
486 get_particles().spawn_oneshot(&batch, pos);
487}
488
489fn spawn_death_particles(pos: Vec2, radius: f32) {
490 play_sound(get_sounds().asteroid_death);
491 let batch = ParticleOneshot::builder()
492 .shape(&Circle::new(
493 Vec2::ZERO,
494 Vec2::splat(LINE_THICKNESS * 2.0),
495 FG.with_alpha(0.7),
496 ))
497 .end_color(FG.with_alpha(0.0))
498 .speed(radius * 0.1)
499 .direction(0.0)
500 .direction_randomness(std::f32::consts::TAU)
501 .speed_randomness(radius * 0.2)
502 .lifetime(0.8)
503 .lifetime_randomness(0.4)
504 .quantity((radius * 1.5) as usize)
505 .position_randomness(Vec2::splat(radius * 0.5))
506 .build();
507 get_particles().spawn_oneshot(&batch, pos);
508}
509
510fn wrap_position(pos: Vec2, size: f32) -> Vec2 {
511 let half_w = screen_distance_to_world(window_width()) / 2.0 + size;
512 let half_h = screen_distance_to_world(window_height()) / 2.0 + size;
513
514 let x = if pos.x > half_w {
515 -half_w
516 } else if pos.x < -half_w {
517 half_w
518 } else {
519 pos.x
520 };
521
522 let y = if pos.y > half_h {
523 -half_h
524 } else if pos.y < -half_h {
525 half_h
526 } else {
527 pos.y
528 };
529
530 Vec2::new(x, y)
531}More examples
examples/custom_init.rs (line 36)
8fn main() -> anyhow::Result<()> {
9 let opts = EngineCreationOptions::builder()
10 .window_transparent(true)
11 .opengl_debug(true)
12 .dithering(false)
13 .opengl_profile(GlProfile::Core)
14 .default_magnify_filter(MagnifySamplerFilter::Nearest)
15 .log_verbosity(Verbosity::Medium)
16 .window_blur(true)
17 // .swap_interval(SwapInterval::DontWait)
18 .title("Custom init".to_string())
19 .build();
20
21 init_custom(opts)?;
22
23 let mut obj = PhysicsCircle {
24 circle: Circle {
25 center: Vec2::splat(300.0),
26 radius: Vec2::splat(100.0),
27 color: Color::RED_400,
28 },
29 velocity: vec2(100.0, 200.0),
30 };
31
32 run_async(async move {
33 loop {
34 clear_screen(Color::TRANSPARENT);
35
36 if obj.circle.center.x >= window_width() - obj.circle.radius.x
37 || obj.circle.center.x <= obj.circle.radius.x
38 {
39 obj.velocity.x = -obj.velocity.x;
40 }
41
42 if obj.circle.center.y >= window_height() - obj.circle.radius.y
43 || obj.circle.center.y <= obj.circle.radius.y
44 {
45 obj.velocity.y = -obj.velocity.y;
46 }
47
48 obj.circle.center += obj.velocity * physics_delta_time();
49 dbg!(obj.circle.center);
50
51 draw(&obj.circle);
52 draw_fps();
53
54 if should_quit() {
55 break;
56 }
57
58 next_frame().await;
59 }
60 });
61
62 Ok(())
63}