use rusting_engine::prelude::*;
const BALL_SPEED: f32 = 8.0;
const PADDLE_SPEED: f32 = 10.0;
const PADDLE_HALF_WIDTH: f32 = 1.0;
const PADDLE_LIMIT: f32 = 6.0 - PADDLE_HALF_WIDTH;
const BALL_REST_Y: f32 = -4.45;
const FLOOR_Y: f32 = -6.5;
const MAX_BOUNCE_ANGLE: f32 = 1.05;
fn value(scene: &mut GameScene<'_>, name: &str) -> i32 {
scene.counter(name).map_or(0, |counter| counter.value)
}
fn add(scene: &mut GameScene<'_>, name: &str, amount: i32) {
if let Some(mut counter) = scene.counter(name) {
counter.value += amount;
}
}
fn complete(scene: &mut GameScene<'_>, name: &str) -> bool {
scene
.counter(name)
.is_some_and(|counter| counter.complete())
}
fn park_ball(scene: &mut GameScene<'_>, paddle_x: f32) {
scene.set_body_kind("Ball", RigidBodyKind::Kinematic);
scene.set_linear_velocity("Ball", [0.0; 3]);
scene
.object("Ball")
.set_position([paddle_x, BALL_REST_Y, 0.0]);
}
fn paddle_bounce(ball_x: f32, paddle_x: f32) -> [f32; 3] {
let offset = ((ball_x - paddle_x) / PADDLE_HALF_WIDTH).clamp(-1.0, 1.0);
let angle = offset * MAX_BOUNCE_ANGLE;
[angle.sin() * BALL_SPEED, angle.cos() * BALL_SPEED, 0.0]
}
fn steady(velocity: [f32; 3]) -> [f32; 3] {
let [x, y, _] = velocity;
let min_y = 0.3;
let length = x.hypot(y).max(f32::EPSILON);
let (mut x, mut y) = (x / length, y / length);
if y.abs() < min_y {
y = min_y.copysign(if y == 0.0 { -1.0 } else { y });
x = (1.0 - y * y).sqrt().copysign(x);
}
[x * BALL_SPEED, y * BALL_SPEED, 0.0]
}
fn update(scene: &mut GameScene<'_>, time: &FrameTime) {
if scene.pressed("restart") {
scene.restart();
return;
}
if complete(scene, "score") || complete(scene, "lost") {
return;
}
let steer = f32::from(u8::from(scene.held("player.right")))
- f32::from(u8::from(scene.held("player.left")));
let mut paddle = scene.object("Paddle");
let [x, y, z] = paddle.position();
let paddle_x = (x + steer * PADDLE_SPEED * time.delta.as_secs_f32())
.clamp(-PADDLE_LIMIT, PADDLE_LIMIT);
paddle.set_position([paddle_x, y, z]);
let velocity = scene.linear_velocity("Ball").unwrap_or([0.0; 3]);
if velocity == [0.0; 3] {
park_ball(scene, paddle_x);
if scene.pressed("launch") {
scene.set_body_kind("Ball", RigidBodyKind::Dynamic);
scene.set_linear_velocity(
"Ball",
paddle_bounce(paddle_x + 0.3, paddle_x),
);
}
return;
}
let touching = scene.touching("Ball");
for brick in touching.iter().filter(|name| name.starts_with("Brick ")) {
if scene.despawn(brick) {
add(scene, "score", 1);
}
}
let ball = scene.object("Ball").position();
let velocity = if touching.iter().any(|name| name == "Paddle") {
paddle_bounce(ball[0], paddle_x)
} else {
steady(velocity)
};
scene.set_linear_velocity("Ball", velocity);
if ball[1] < FLOOR_Y {
add(scene, "lives", -1);
add(scene, "lost", 1);
park_ball(scene, paddle_x);
}
}
rusting_game!(update);