====== Pong ====== 作成日: 2022-12-07 (水) {{:youtube:sfml-pong.png?400|}} src/main.rs use sfml::{system::*, window::*, graphics::*}; struct MoveFlags { up: bool, down: bool, } struct Game<'a> { window: RenderWindow, paddle: RectangleShape<'a>, paddle_speed: f32, ball: CircleShape<'a>, ball_velocity: Vector2f, move_flags: MoveFlags } impl<'a> Game<'a> { const FPS: f32 = 60.0; // Time::seconds(1.0 / 60.0) is not allowed for const const WIDTH: u32 = 1000; const HEIGHT: u32 = 800; const PADDLE_SPEED: f32 = 200.0; const PADDLE_SIZE_X: f32 = 30.0; const PADDLE_SIZE_Y: f32 = 100.0; const BALL_SPEED: f32 = 400.0; const BALL_RADIUS: f32 = 20.0; pub fn new() -> Game<'a> { let window = RenderWindow::new( (Self::WIDTH, Self::HEIGHT), "Pong with Rust and SFML", Style::CLOSE, &Default::default()); let mut paddle = RectangleShape::with_size(Vector2f::new(Self::PADDLE_SIZE_X, Self::PADDLE_SIZE_Y)); paddle.set_position((Self::PADDLE_SIZE_X, (Self::HEIGHT as f32 / 2.0) - Self::PADDLE_SIZE_Y)); let paddle_speed = Self::PADDLE_SPEED; let mut ball = CircleShape::new(Self::BALL_RADIUS, 30); ball.set_position((Self::WIDTH as f32 / 2.0, Self::HEIGHT as f32 / 2.0)); ball.set_origin((Self::BALL_RADIUS, Self::BALL_RADIUS)); let ball_velocity = Vector2f::new(Self::BALL_SPEED, Self::BALL_SPEED); Game { window, paddle, paddle_speed, ball, ball_velocity, move_flags: MoveFlags { up: false, down: false, } } } pub fn run(&mut self) { let mut clock = Clock::start(); let mut time_since_last_update = Time::ZERO; let time_per_frame = Time::seconds(1.0 / Self::FPS); while self.window.is_open() { self.process_events(); time_since_last_update += clock.restart(); while time_since_last_update > time_per_frame { time_since_last_update -= time_per_frame; self.process_events(); self.update(&time_per_frame); } self.render(); } } fn process_events(&mut self) { while let Some(event) = self.window.poll_event() { match event { Event::Closed => self.window.close(), Event::KeyPressed { code, .. } => if code == Key::Enter || code == Key::Escape { self.window.close(); } else { self.handle_player_input(code, true); }, Event::KeyReleased { code, .. } => self.handle_player_input(code, false), _ => {} } } } fn update(&mut self, delta_time: &Time) { // short names for readability let paddle = &mut self.paddle; let ball = &mut self.ball; let ball_velocity = &mut self.ball_velocity; // update paddle let mut movement = Vector2f::new(0.0, 0.0); if self.move_flags.up { if paddle.position().y > 0.0 { movement.y -= self.paddle_speed; } } if self.move_flags.down { if paddle.position().y + paddle.size().y < Self::HEIGHT as f32 { movement.y += self.paddle_speed; } } paddle.move_(movement * delta_time.as_seconds()); // update ball // update ball x ball.move_((ball_velocity.x * delta_time.as_seconds(), 0.0)); if ball.position().x + ball.radius() > Self::WIDTH as f32 { ball_velocity.x *= -1.0; // align to left of paddle ball.set_position((Self::WIDTH as f32 - ball.radius(), ball.position().y)); } let paddle_rect = paddle.global_bounds(); let mut ball_rect = ball.global_bounds(); // mut for reusing later if ball_rect.intersection(&paddle_rect) != None { ball.set_position((paddle_rect.left + paddle_rect.width + ball.radius(), ball.position().y)); ball_velocity.x *= -1.0; } // update ball y ball.move_((0.0, ball_velocity.y * delta_time.as_seconds())); if ball.position().y - ball.radius() < 0.0 { ball_velocity.y *= -1.0; ball.set_position((ball.position().x, ball.radius())); } if ball.position().y + ball.radius() > Self::HEIGHT as f32 { ball_velocity.y *= -1.0; ball.set_position((ball.position().x, Self::HEIGHT as f32 - ball.radius())); } ball_rect = ball.global_bounds(); // get new rect which has been updated for x if ball_rect.intersection(&paddle_rect) != None { if ball_velocity.y > 0.0 { // align to top of paddle ball.set_position((ball.position().x, paddle_rect.top - ball.radius())); } else { // align to bottom of paddle ball.set_position((ball.position().x, paddle_rect.top + paddle_rect.height)); } ball_velocity.y *= -1.0; } assert!(ball.position().x + ball.radius() <= Self::WIDTH as f32); } fn render(&mut self) { self.window.clear(Color::BLUE); self.window.draw(&self.paddle); self.window.draw(&self.ball); self.window.display(); } fn handle_player_input(&mut self, code: Key, pressed: bool) { match code { Key::W => self.move_flags.up = pressed, Key::S => self.move_flags.down = pressed, _ => {} } } } fn main() { let mut game = Game::new(); game.run(); } Cargo.toml [package] name = "pong" version = "0.1.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] sfml = "0.19.0"