====== 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"