Przewodnik krok po kroku z użyciem Rust i Macroquad
W tym samouczku zbudujemy interaktywną symulację fizyczną Kołyski Newtona. Omówimy więzy ciała sztywnego (sznurki) oraz zderzenia sprężyste (transfer energii).
Nagranie ekranu programu:

Najpierw upewnij się, że masz zainstalowany Rust. Utwórz nowy projekt i dodaj zależności.
# Terminal
cargo new newtons_cradle
cd newtons_cradle
Otwórz Cargo.toml i dodaj Macroquad, prosty i szybki framework do gier dla Rusta.
[dependencies]
macroquad = "0.4"
Pobierz pełny kod tutaj: main.rs lub postępuj zgodnie z poniższymi krokami.
Potrzebujemy struktury reprezentującej każdą kulę. Wahadło składa się z kotwicy (gdzie sznurek jest przywiązany), pozycji (gdzie znajduje się kula) oraz prędkości.
use macroquad::prelude::*;
const BALL_RADIUS: f32 = 30.0;
const STRING_LENGTH: f32 = 300.0;
struct Pendulum {
anchor: Vec2,
pos: Vec2,
vel: Vec2,
is_dragged: bool, // Czy użytkownik trzyma tę kulę?
}
impl Pendulum {
fn new(x: f32, y: f32) -> Self {
Self {
anchor: vec2(x, y),
pos: vec2(x, y + STRING_LENGTH),
vel: Vec2::ZERO,
is_dragged: false,
}
}
}
Zazwyczaj wahadła wymagają trygonometrii. Jednak w fizyce gier często łatwiej jest używać więzów wektorowych.
impl Pendulum {
fn update(&mut self, dt: f32) {
if self.is_dragged {
self.vel = Vec2::ZERO;
return;
}
// 1. Zastosuj grawitację
self.vel.y += 900.0 * dt;
self.vel *= 0.995; // Opór powietrza (Tłumienie)
self.pos += self.vel * dt;
// 2. Wymuś długość sznura
let to_ball = self.pos - self.anchor;
let dist = to_ball.length();
if dist > 0.0 {
// Zresetuj pozycję dokładnie na odległość długości sznura
let dir = to_ball / dist;
self.pos = self.anchor + dir * STRING_LENGTH;
// Usuń prędkość, która odciąga od sznura (Napięcie)
let vel_parallel = self.vel.dot(dir);
if vel_parallel > 0.0 {
self.vel -= dir * vel_parallel;
}
}
}
}
Kołyska Newtona działa, ponieważ zderzenia są niemal idealnie sprężyste, a masy są identyczne. W fizyce, gdy dwa obiekty o równej masie zderzają się sprężyście, po prostu wymieniają się prędkością wzdłuż wektora normalnego zderzenia.
fn resolve_collisions(balls: &mut [Pendulum]) {
for i in 0..balls.len() {
if i + 1 >= balls.len() { break; }
// Pobierz mutowalne referencje do dwóch sąsiednich kul
let (left, right) = balls.split_at_mut(i + 1);
let b1 = &mut left[i];
let b2 = &mut right[0];
let delta = b2.pos - b1.pos;
let dist = delta.length();
let min_dist = BALL_RADIUS * 2.0;
if dist < min_dist {
let n = delta / dist; // Normalna kolizji
// 1. Rozsuń je, aby na siebie nie nachodziły
let push = n * (min_dist - dist) * 0.5;
if !b1.is_dragged { b1.pos -= push; }
if !b2.is_dragged { b2.pos += push; }
// 2. Wymiana prędkości (Logika zderzenia sprężystego)
let v1_n = b1.vel.dot(n);
let v2_n = b2.vel.dot(n);
// Zamień tylko, jeśli poruszają się w swoją stronę
if v1_n - v2_n > 0.0 {
let v1_t = b1.vel - n * v1_n; // Składowa styczna
let v2_t = b2.vel - n * v2_n;
// Zamień składowe normalne!
b1.vel = v1_t + n * v2_n;
b2.vel = v2_t + n * v1_n;
}
}
}
}
Wreszcie łączymy wszystko w funkcji main.
#[macroquad::main("Newton's Cradle")]
async fn main() {
// Utwórz 5 kul
let mut balls: Vec<Pendulum> = (0..5)
.map(|i| Pendulum::new(300.0 + i as f32 * 60.0, 100.0))
.collect();
loop {
clear_background(LIGHTGRAY);
// Obsługa myszy (Przeciąganie)
let mouse_pos = Vec2::from(mouse_position());
if is_mouse_button_pressed(MouseButton::Left) {
for b in &mut balls {
if b.pos.distance(mouse_pos) < BALL_RADIUS {
b.is_dragged = true;
break;
}
}
}
if is_mouse_button_released(MouseButton::Left) {
for b in &mut balls { b.is_dragged = false; }
}
// Logika przeciągania
for b in &mut balls {
if b.is_dragged {
let dir = (mouse_pos - b.anchor).normalize();
b.pos = b.anchor + dir * STRING_LENGTH;
b.vel = Vec2::ZERO;
}
}
// Aktualizacja fizyki
let dt = get_frame_time();
for b in &mut balls { b.update(dt); }
// Podkroki kolizji dla stabilności
for _ in 0..4 { resolve_collisions(&mut balls); }
// Rysowanie
for b in &balls {
draw_line(b.anchor.x, b.anchor.y, b.pos.x, b.pos.y, 2.0, BLACK);
draw_circle(b.pos.x, b.pos.y, BALL_RADIUS, BLUE);
}
next_frame().await
}
}
Następny artykuł: