some tweaks

pull/42/head
Ben Hansen 4 years ago
parent 0c3249e7f9
commit 32e5989dc1

@ -0,0 +1,51 @@
use winit::event::{VirtualKeyCode, ElementState};
#[derive(Debug, Default)]
pub struct Input {
pub p1_up_pressed: bool,
pub p1_down_pressed: bool,
pub p2_up_pressed: bool,
pub p2_down_pressed: bool,
pub enter_pressed: bool,
}
impl Input {
pub fn new() -> Self {
Default::default()
}
pub fn update(&mut self, key: VirtualKeyCode, state: ElementState) -> bool {
let pressed = state == ElementState::Pressed;
match key {
VirtualKeyCode::Up => {
self.p2_up_pressed = pressed;
true
}
VirtualKeyCode::Down => {
self.p2_down_pressed = pressed;
true
}
VirtualKeyCode::W => {
self.p1_up_pressed = pressed;
true
}
VirtualKeyCode::S => {
self.p1_down_pressed = pressed;
true
}
VirtualKeyCode::Return => {
self.enter_pressed = pressed;
true
}
_ => false
}
}
pub fn ui_up_pressed(&self) -> bool {
self.p1_up_pressed || self.p2_up_pressed
}
pub fn ui_down_pressed(&self) -> bool {
self.p1_down_pressed || self.p2_down_pressed
}
}

@ -3,8 +3,10 @@ mod state;
mod util;
mod system;
mod sound;
mod input;
use system::System;
use input::Input;
use winit::event::*;
use winit::dpi::LogicalSize;
@ -89,12 +91,17 @@ fn main() {
let sound_system = sound::SoundSystem::new();
let sound_pack = sound::SoundPack::new();
let mut events = Vec::new();
let mut input = Input::new();
let mut play_system = system::PlaySystem::new();
let mut menu_system = system::MenuSystem::new();
let mut menu_system = system::MenuSystem;
let mut serving_system = system::ServingSystem::new();
let mut play_system = system::PlaySystem;
let ball_system = system::BallSystem;
let mut game_over_system = system::GameOverSystem::new();
let mut visiblity_system = system::VisibilitySystem;
visiblity_system.start(&mut state);
menu_system.start(&mut state);
event_loop.run(move |event, _, control_flow| {
@ -115,31 +122,19 @@ fn main() {
event: WindowEvent::KeyboardInput {
input: KeyboardInput {
state: element_state,
virtual_keycode: Some(keycode),
virtual_keycode: Some(key),
..
},
..
},
..
} => {
let pressed = element_state == ElementState::Pressed;
let input_handled = match state.game_state {
state::GameState::MainMenu => {
menu_system.process_input(keycode, pressed)
},
state::GameState::Serving => {
serving_system.process_input(keycode, pressed)
},
state::GameState::Playing => {
play_system.process_input(keycode, pressed)
},
state::GameState::GameOver => {
game_over_system.process_input(keycode, pressed)
},
state::GameState::Quiting => true,
_ => input.update(key, element_state),
};
if !input_handled {
process_input(element_state, keycode, control_flow);
process_input(element_state, key, control_flow);
}
}
Event::RedrawRequested(_) => {
@ -155,26 +150,29 @@ fn main() {
state::Event::Score(_) => {
sound_system.queue(sound_pack.bounce());
}
_ => {}
// _ => {}
}
}
events.clear();
visiblity_system.update_state(&input, &mut state, &mut events);
match state.game_state {
state::GameState::MainMenu => {
menu_system.update_state(&mut state, &mut events);
menu_system.update_state(&input, &mut state, &mut events);
if state.game_state == state::GameState::Serving {
serving_system.start(&mut state);
}
},
state::GameState::Serving => {
serving_system.update_state(&mut state, &mut events);
serving_system.update_state(&input, &mut state, &mut events);
play_system.update_state(&input, &mut state, &mut events);
if state.game_state == state::GameState::Playing {
play_system.start(&mut state);
}
},
state::GameState::Playing => {
play_system.update_state(&mut state, &mut events);
ball_system.update_state(&input, &mut state, &mut events);
play_system.update_state(&input, &mut state, &mut events);
if state.game_state == state::GameState::Serving {
serving_system.start(&mut state);
} else if state.game_state == state::GameState::GameOver {
@ -182,7 +180,7 @@ fn main() {
}
},
state::GameState::GameOver => {
game_over_system.update_state(&mut state, &mut events);
game_over_system.update_state(&input, &mut state, &mut events);
if state.game_state == state::GameState::MainMenu {
menu_system.start(&mut state);
}
@ -191,7 +189,9 @@ fn main() {
}
render.render_state(&state);
window.request_redraw();
if state.game_state != state::GameState::Quiting {
window.request_redraw();
}
}
_ => {}
}

@ -12,6 +12,7 @@ impl SoundSystem {
pub fn new() -> Self {
let device = rodio::default_output_device().unwrap();
let sink = rodio::Sink::new(&device);
sink.set_volume(0.5);
Self {
device,

@ -28,6 +28,7 @@ pub struct Ball {
pub visible: bool,
}
#[derive(Debug)]
pub struct Player {
pub position: cgmath::Vector2<f32>,
pub size: cgmath::Vector2<f32>,
@ -41,8 +42,18 @@ impl Player {
let radii = self.size * 0.5;
let min = self.position - radii;
let max = self.position + radii;
ball.position.x > min.x && ball.position.y > min.y
&& ball.position.x < max.x && ball.position.y < max.y
let b_radii = cgmath::Vector2 {
x: ball.radius,
y: ball.radius,
};
let b_min = ball.position - b_radii;
let b_max = ball.position + b_radii;
min.x < b_max.x
&& max.x > b_min.x
&& min.y < b_max.y
&& max.y > b_min.y
}
}
@ -81,5 +92,28 @@ pub enum Event {
FocusChanged,
BallBounce(cgmath::Vector2<f32>),
Score(u32),
GameOver,
// GameOver,
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn player_contains_ball() {
let ball = Ball {
position: (0.0, 0.0).into(),
velocity: (0.0, 0.0).into(),
radius: 1.0,
visible: true,
};
let p1 = Player {
position: (0.0, -1.5).into(),
size: (2.0, 2.0).into(),
score: 0,
visible: true,
};
assert!(p1.contains(&ball));
}
}

@ -1,161 +1,106 @@
use winit::event::*;
use crate::state;
use crate::state::{self, GameState};
use crate::input;
use crate::util;
pub trait System {
fn start(&mut self, state: &mut state::State);
fn process_input(&mut self, keycode: VirtualKeyCode, pressed: bool) -> bool;
fn update_state(&self, state: &mut state::State, events: &mut Vec<state::Event>);
}
use cgmath::prelude::*;
#[derive(Debug)]
pub struct MenuSystem {
enter_pressed: bool,
down_pressed: bool,
up_pressed: bool,
pub trait System {
#[allow(unused_variables)]
fn start(&mut self, state: &mut state::State) {}
fn update_state(
&self,
input: &input::Input,
state: &mut state::State,
events: &mut Vec<state::Event>,
);
}
impl MenuSystem {
pub fn new() -> Self {
Self {
enter_pressed: false,
down_pressed: false,
up_pressed: false,
}
pub struct VisibilitySystem;
impl System for VisibilitySystem {
fn update_state(
&self,
_input: &input::Input,
state: &mut state::State,
_events: &mut Vec<state::Event>,
) {
let gs = state.game_state;
let is_in_game = any!(gs, GameState::Serving, GameState::Playing, GameState::GameOver);
state.ball.visible = is_in_game && gs != GameState::GameOver;
state.player1.visible = is_in_game;
state.player1_score.visible = is_in_game;
state.player2.visible = is_in_game;
state.player2_score.visible = is_in_game;
state.title_text.visible = gs == GameState::MainMenu;
state.play_button.visible = gs == GameState::MainMenu;
state.quit_button.visible = gs == GameState::MainMenu;
state.win_text.visible = gs == GameState::GameOver;
}
}
#[derive(Debug)]
pub struct MenuSystem;
impl System for MenuSystem {
fn start(&mut self, state: &mut state::State) {
self.enter_pressed = false;
self.down_pressed = false;
self.up_pressed = false;
state.player1.score = 0;
state.player2.score = 0;
state.ball.visible = false;
state.player1.visible = false;
state.player2.visible = false;
state.title_text.visible = true;
state.play_button.visible = true;
state.player1.position.y = 0.0;
state.player2.position.y = 0.0;
state.play_button.focused = true;
state.quit_button.visible = true;
state.player1_score.visible = false;
state.player2_score.visible = false;
state.win_text.visible = false;
state.quit_button.focused = false;
}
fn process_input(&mut self, keycode: VirtualKeyCode, pressed: bool) -> bool {
match keycode {
VirtualKeyCode::Up | VirtualKeyCode::W => {
self.up_pressed = pressed;
true
}
VirtualKeyCode::Down | VirtualKeyCode::S => {
self.down_pressed = pressed;
true
}
VirtualKeyCode::Return
| VirtualKeyCode::NumpadEnter
| VirtualKeyCode::Space => {
self.enter_pressed = pressed;
true
}
_ => false,
}
}
fn update_state(&self, state: &mut state::State, events: &mut Vec<state::Event>) {
fn update_state(
&self,
input: &input::Input,
state: &mut state::State,
events: &mut Vec<state::Event>
) {
if state.play_button.focused && self.down_pressed {
if state.play_button.focused && input.ui_down_pressed() {
events.push(state::Event::FocusChanged);
state.play_button.focused = false;
state.quit_button.focused = true;
} else if state.quit_button.focused && self.up_pressed {
} else if state.quit_button.focused && input.ui_up_pressed() {
events.push(state::Event::FocusChanged);
state.quit_button.focused = false;
state.play_button.focused = true;
}
if state.play_button.focused && self.enter_pressed {
if state.play_button.focused && input.enter_pressed {
events.push(state::Event::ButtonPressed);
state.game_state = state::GameState::Serving;
} else if state.quit_button.focused && self.enter_pressed {
} else if state.quit_button.focused && input.enter_pressed {
events.push(state::Event::ButtonPressed);
state.game_state = state::GameState::Quiting;
}
}
}
pub struct PlaySystem {
player1_up_pressed: bool,
player1_down_pressed: bool,
player2_up_pressed: bool,
player2_down_pressed: bool,
}
impl PlaySystem {
pub fn new() -> Self {
Self {
player1_up_pressed: false,
player1_down_pressed: false,
player2_up_pressed: false,
player2_down_pressed: false,
}
}
}
pub struct PlaySystem;
impl System for PlaySystem {
fn start(&mut self, state: &mut state::State) {
self.player1_up_pressed = false;
self.player1_down_pressed = false;
self.player2_up_pressed = false;
self.player2_down_pressed = false;
state.ball.visible = true;
state.player1.visible = true;
state.player2.visible = true;
state.title_text.visible = false;
state.play_button.visible = false;
state.quit_button.visible = false;
state.player1_score.visible = true;
state.player2_score.visible = true;
}
fn process_input(&mut self, keycode: VirtualKeyCode, pressed: bool) -> bool {
match keycode {
VirtualKeyCode::W => {
self.player1_up_pressed = pressed;
true
}
VirtualKeyCode::S => {
self.player1_down_pressed = pressed;
true
}
VirtualKeyCode::Up => {
self.player2_up_pressed = pressed;
true
}
VirtualKeyCode::Down => {
self.player2_down_pressed = pressed;
true
}
_ => false,
}
}
fn update_state(&self, state: &mut state::State, events: &mut Vec<state::Event>) {
fn update_state(
&self,
input: &input::Input,
state: &mut state::State,
_events: &mut Vec<state::Event>,
) {
// move the players
if self.player1_up_pressed {
state.player1.position.y += 0.025;
if input.p1_up_pressed {
state.player1.position.y += util::PLAYER_SPEED;
}
if self.player1_down_pressed {
state.player1.position.y -= 0.025;
if input.p1_down_pressed {
state.player1.position.y -= util::PLAYER_SPEED;
}
if self.player2_up_pressed {
state.player2.position.y += 0.025;
if input.p2_up_pressed {
state.player2.position.y += util::PLAYER_SPEED;
}
if self.player2_down_pressed {
state.player2.position.y -= 0.025;
if input.p2_down_pressed {
state.player2.position.y -= util::PLAYER_SPEED;
}
// normalize players
@ -170,11 +115,40 @@ impl System for PlaySystem {
state.player2.position.y = -1.0;
}
if state.player1.score > 2 || state.player2.score > 2 {
state.game_state = state::GameState::GameOver;
}
}
}
pub struct BallSystem;
impl System for BallSystem {
fn update_state(
&self,
_input: &input::Input,
state: &mut state::State,
events: &mut Vec<state::Event>,
) {
// bounce the ball off the players
if state.player1.contains(&state.ball) {
events.push(state::Event::BallBounce(state.ball.position));
state.ball.position.x -= state.ball.velocity.x - state.player1.size.x;
state.ball.velocity = util::calc_ball_velocity(&state.ball, &state.player1);
} else if state.player2.contains(&state.ball) {
events.push(state::Event::BallBounce(state.ball.position));
state.ball.position.x -= state.ball.velocity.x + state.player2.size.x;
state.ball.velocity.x *= -state.player2.size.y;
state.ball.velocity = util::calc_ball_velocity(&state.ball, &state.player2);
}
state.ball.position += state.ball.velocity;
if state.ball.position.y > 1.0 {
events.push(state::Event::BallBounce(state.ball.position));
state.ball.position.y = 1.0;
state.ball.velocity.y *= -1.0;
} else if state.ball.position.y < -1.0 {
events.push(state::Event::BallBounce(state.ball.position));
state.ball.position.y = -1.0;
state.ball.velocity.y *= -1.0;
}
@ -182,24 +156,11 @@ impl System for PlaySystem {
if state.ball.position.x > 1.0 {
state.player1.score += 1;
state.game_state = state::GameState::Serving;
events.push(state::Event::Score(0));
} else if state.ball.position.x < -1.0 {
state.player2.score += 1;
state.game_state = state::GameState::Serving;
}
if state.player1.score > 2 || state.player2.score > 2 {
state.game_state = state::GameState::GameOver;
}
// bounce the ball off the players
if state.player1.contains(&state.ball) {
events.push(state::Event::BallBounce(state.ball.position));
state.ball.position.x -= state.ball.velocity.x - state.player1.size.x;
state.ball.velocity.x *= -1.0;
} else if state.player2.contains(&state.ball) {
events.push(state::Event::BallBounce(state.ball.position));
state.ball.position.x -= state.ball.velocity.x + state.player2.size.x;
state.ball.velocity.x *= -1.0;
events.push(state::Event::Score(1));
}
}
}
@ -220,28 +181,22 @@ impl ServingSystem {
impl System for ServingSystem {
fn start(&mut self, state: &mut state::State) {
self.last_time = std::time::Instant::now();
state.ball.visible = true;
state.player1.visible = true;
state.player2.visible = true;
state.title_text.visible = false;
state.play_button.visible = false;
state.quit_button.visible = false;
let direction = state.ball.position.x.signum();
state.ball.position = (0.0, 0.0).into();
state.ball.velocity = util::random_vec2_scaled(0.025);
state.player1_score.visible = true;
state.player2_score.visible = true;
state.ball.velocity = cgmath::Vector2::unit_x() * direction * -util::BALL_SPEED;
state.player1_score.text = format!("{}", state.player1.score);
state.player2_score.text = format!("{}", state.player2.score);
}
fn process_input(&mut self, _keycode: VirtualKeyCode, _pressed: bool) -> bool {
false
}
fn update_state(&self, state: &mut state::State, _events: &mut Vec<state::Event>) {
fn update_state(
&self,
_input: &input::Input,
state: &mut state::State,
_events: &mut Vec<state::Event>,
) {
let current_time = std::time::Instant::now();
let delta_time = current_time - self.last_time;
if delta_time.as_secs_f32() > 1.0 {
if delta_time.as_secs_f32() > 2.0 {
state.game_state = state::GameState::Playing;
}
}
@ -249,14 +204,12 @@ impl System for ServingSystem {
pub struct GameOverSystem {
last_time: std::time::Instant,
key_pressed: bool,
}
impl GameOverSystem {
pub fn new() -> Self {
Self {
last_time: std::time::Instant::now(),
key_pressed: false,
}
}
}
@ -264,22 +217,19 @@ impl GameOverSystem {
impl System for GameOverSystem {
fn start(&mut self, state: &mut state::State) {
self.last_time = std::time::Instant::now();
self.key_pressed = false;
state.ball.visible = false;
state.win_text.text = if state.player1.score > state.player2.score {
String::from("Player1 wins!")
} else {
String::from("Player2 wins!")
};
state.win_text.visible = true;
}
fn process_input(&mut self, _keycode: VirtualKeyCode, _pressed: bool) -> bool {
false
}
fn update_state(&self, state: &mut state::State, _events: &mut Vec<state::Event>) {
fn update_state(&self,
_input: &input::Input,
state: &mut state::State,
_events: &mut Vec<state::Event>,
) {
let current_time = std::time::Instant::now();
let delta_time = current_time - self.last_time;
if delta_time.as_secs_f32() > 1.0 {

@ -1,17 +1,58 @@
// use cgmath::prelude::*;
pub fn random_vec2() -> cgmath::Vector2<f32> {
let theta: f32 = rand::random();
cgmath::Vector2::new(
theta.cos(),
theta.sin(),
)
}
#![macro_use]
use crate::state;
pub const PLAYER_SPEED: f32 = 0.05;
pub const BALL_SPEED: f32 = 0.025;
pub fn random_vec2_scaled(scale: f32) -> cgmath::Vector2<f32> {
random_vec2() * scale
const BOUNCE_ANGLE: f32 = std::f32::consts::FRAC_PI_2;
pub fn calc_ball_velocity(ball: &state::Ball, player: &state::Player) -> cgmath::Vector2<f32> {
let diff_y = ball.position.y - player.position.y;
let ratio = diff_y / player.size.y * 0.5;
cgmath::Vector2 {
x: (BOUNCE_ANGLE * ratio).cos() * -player.position.x.signum(),
y: (BOUNCE_ANGLE * ratio).sin(),
} * BALL_SPEED
}
pub fn size_of_slice<T: Sized>(slice: &[T]) -> usize {
std::mem::size_of::<T>() * slice.len()
}
#[macro_export]
macro_rules! any {
($x:expr, $($y:expr),+ $(,)?) => {
{
false $(|| $x == $y)+
}
};
}
#[cfg(test)]
mod test {
#[allow(unused_imports)]
use super::*;
use crate::state;
#[test]
fn any_with_game_state() {
let game_state = state::GameState::GameOver;
assert!(any!(game_state, state::GameState::GameOver));
assert!(!any!(game_state, state::GameState::MainMenu));
assert!(!any!(game_state, state::GameState::Serving));
assert!(!any!(game_state, state::GameState::Playing));
assert!(!any!(game_state, state::GameState::Quiting));
assert!(any!(
game_state,
state::GameState::MainMenu,
state::GameState::Serving,
state::GameState::Playing,
state::GameState::GameOver,
state::GameState::Quiting,
));
}
}
Loading…
Cancel
Save