navigation done

pull/42/head
Ben Hansen 4 years ago
parent 87f065acdd
commit 7c595d344a

@ -0,0 +1,93 @@
Copyright 2012 The Press Start 2P Project Authors (cody@zone38.net), with Reserved Font Name "Press Start 2P".
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

@ -5,5 +5,5 @@ layout(location=0) in vec2 vTexCoord;
layout(location=0) out vec4 fColor;
void main() {
fColor = vec4(vTexCoord, 0, 1);
fColor = vec4(1);
}

@ -6,17 +6,24 @@ mod system;
use system::System;
use winit::event::*;
use winit::dpi::LogicalSize;
use winit::window::WindowBuilder;
use winit::event_loop::{EventLoop, ControlFlow};
use futures::executor::block_on;
fn main() {
let event_loop = EventLoop::new();
let mut render: render::Render = block_on(render::Render::new(&event_loop));
let window = WindowBuilder::new()
.with_title("Pong")
.with_inner_size(LogicalSize::<f64>::from((800, 600)))
.build(&event_loop).unwrap();
let mut render = block_on(render::Render::new(&window));
let mut state = state::State {
ball: state::Ball {
position: (0.0, 0.0).into(),
velocity: util::random_vec2_scaled(0.025),
velocity: (0.0, 0.0).into(),
radius: 0.05,
visible: true,
},
@ -32,10 +39,58 @@ fn main() {
score: 0,
visible: true,
},
title_text: state::Text {
position: (20.0, 20.0).into(),
color: (1.0, 1.0, 1.0, 1.0).into(),
text: String::from("PONG"),
size: 64.0,
..Default::default()
},
play_button: state::Text {
position: (40.0, 100.0).into(),
color: (1.0, 1.0, 1.0, 1.0).into(),
text: String::from("Play"),
size: 32.0,
centered: false,
..Default::default()
},
quit_button: state::Text {
position: (40.0, 160.0).into(),
color: (1.0, 1.0, 1.0, 1.0).into(),
text: String::from("Quit"),
size: 32.0,
..Default::default()
},
player1_score: state::Text {
position: (render.width() * 0.25, 20.0).into(),
color: (1.0, 1.0, 1.0, 1.0).into(),
text: String::from("0"),
size: 32.0,
..Default::default()
},
player2_score: state::Text {
position: (render.width() * 0.75, 20.0).into(),
color: (1.0, 1.0, 1.0, 1.0).into(),
text: String::from("0"),
size: 32.0,
..Default::default()
},
win_text: state::Text {
position: (render.width() * 0.5, render.height() * 0.5).into(),
bounds: (render.width(), state::UNBOUNDED_F32).into(),
size: 32.0,
centered: true,
..Default::default()
},
game_state: state::GameState::MainMenu,
};
let mut movement_system = system::MovementSystem::new();
let mut play_system = system::PlaySystem::new();
let mut menu_system = system::MenuSystem::new();
let mut serving_system = system::ServingSystem::new();
let mut game_over_system = system::GameOverSystem::new();
menu_system.start(&mut state);
event_loop.run(move |event, _, control_flow| {
*control_flow = if state.game_state == state::GameState::Quiting {
@ -45,6 +100,12 @@ fn main() {
};
match event {
Event::WindowEvent {
event: WindowEvent::CloseRequested,
..
} => {
state.game_state = state::GameState::Quiting;
}
Event::WindowEvent {
event: WindowEvent::KeyboardInput {
input: KeyboardInput {
@ -57,13 +118,58 @@ fn main() {
..
} => {
let pressed = element_state == ElementState::Pressed;
if !movement_system.process_input(keycode, 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,
};
if !input_handled {
process_input(element_state, keycode, control_flow);
}
}
Event::RedrawRequested(_) => {
movement_system.update_state(&mut state);
match state.game_state {
state::GameState::MainMenu => {
menu_system.update_state(&mut state);
if state.game_state == state::GameState::Serving {
serving_system.start(&mut state);
}
},
state::GameState::Serving => {
serving_system.update_state(&mut state);
if state.game_state == state::GameState::Playing {
play_system.start(&mut state);
}
},
state::GameState::Playing => {
play_system.update_state(&mut state);
if state.game_state == state::GameState::Serving {
serving_system.start(&mut state);
} else if state.game_state == state::GameState::GameOver {
game_over_system.start(&mut state);
}
},
state::GameState::GameOver => {
game_over_system.update_state(&mut state);
if state.game_state == state::GameState::MainMenu {
menu_system.start(&mut state);
}
},
state::GameState::Quiting => {},
}
render.render_state(&state);
window.request_redraw();
}
_ => {}
}

@ -0,0 +1,122 @@
use crate::util::size_of_slice;
use crate::state;
pub const U32_SIZE: wgpu::BufferAddress = std::mem::size_of::<u32>() as wgpu::BufferAddress;
#[derive(Copy, Clone)]
pub struct Vertex {
#[allow(dead_code)]
position: cgmath::Vector2<f32>,
}
unsafe impl bytemuck::Pod for Vertex {}
unsafe impl bytemuck::Zeroable for Vertex {}
impl Vertex {
pub const SIZE: wgpu::BufferAddress = std::mem::size_of::<Self>() as wgpu::BufferAddress;
pub const DESC: wgpu::VertexBufferDescriptor<'static> = wgpu::VertexBufferDescriptor {
stride: Self::SIZE,
step_mode: wgpu::InputStepMode::Vertex,
attributes: &wgpu::vertex_attr_array![
0 => Float2
],
};
}
pub struct QuadBufferBuilder {
vertex_data: Vec<Vertex>,
index_data: Vec<u32>,
current_quad: u32,
}
impl QuadBufferBuilder {
pub fn new() -> Self {
Self {
vertex_data: Vec::new(),
index_data: Vec::new(),
current_quad: 0,
}
}
pub fn push_ball(self, ball: &state::Ball) -> Self {
if ball.visible {
let min_x = ball.position.x - ball.radius;
let min_y = ball.position.y - ball.radius;
let max_x = ball.position.x + ball.radius;
let max_y = ball.position.y + ball.radius;
self.push_quad(min_x, min_y, max_x, max_y)
} else {
self
}
}
pub fn push_player(self, player: &state::Player) -> Self {
if player.visible {
self.push_quad(
player.position.x - player.size.x * 0.5,
player.position.y - player.size.y * 0.5,
player.position.x + player.size.x * 0.5,
player.position.y + player.size.y * 0.5,
)
} else {
self
}
}
pub fn push_quad(mut self, min_x: f32, min_y: f32, max_x: f32, max_y: f32) -> Self {
self.vertex_data.extend(&[
Vertex {
position: (min_x, min_y).into(),
},
Vertex {
position: (max_x, min_y).into(),
},
Vertex {
position: (max_x, max_y).into(),
},
Vertex {
position: (min_x, max_y).into(),
},
]);
self.index_data.extend(&[
self.current_quad * 4 + 0,
self.current_quad * 4 + 1,
self.current_quad * 4 + 2,
self.current_quad * 4 + 0,
self.current_quad * 4 + 2,
self.current_quad * 4 + 3,
]);
self.current_quad += 1;
self
}
pub fn build(self, device: &wgpu::Device) -> (StagingBuffer, StagingBuffer, u32) {
(
StagingBuffer::new(device, &self.vertex_data),
StagingBuffer::new(device, &self.index_data),
self.index_data.len() as u32,
)
}
}
pub struct StagingBuffer {
buffer: wgpu::Buffer,
size: wgpu::BufferAddress,
}
impl StagingBuffer {
pub fn new<T: bytemuck::Pod + Sized>(device: &wgpu::Device, data: &[T]) -> StagingBuffer {
StagingBuffer {
buffer: device.create_buffer_with_data(
bytemuck::cast_slice(data),
wgpu::BufferUsage::COPY_SRC,
),
size: size_of_slice(data) as wgpu::BufferAddress
}
}
pub fn copy_to_buffer(&self, encoder: &mut wgpu::CommandEncoder, other: &wgpu::Buffer) {
encoder.copy_buffer_to_buffer(&self.buffer, 0, other, 0, self.size)
}
}

@ -1,29 +1,42 @@
use winit::window::{Window, WindowBuilder};
use winit::event_loop::EventLoop;
use winit::dpi::LogicalSize;
mod buffer;
use winit::window::{Window};
use buffer::*;
use crate::state;
const FONT_BYTES: &[u8] = include_bytes!("../../res/fonts/PressStart2P-Regular.ttf");
pub struct Render {
window: Window,
// todo: fix this
#[allow(dead_code)]
surface: wgpu::Surface,
#[allow(dead_code)]
adapter: wgpu::Adapter,
device: wgpu::Device,
queue: wgpu::Queue,
sc_desc: wgpu::SwapChainDescriptor,
swap_chain: wgpu::SwapChain,
pipeline: wgpu::RenderPipeline,
vertex_buffer: wgpu::Buffer,
index_buffer: wgpu::Buffer,
glyph_brush: wgpu_glyph::GlyphBrush<'static, ()>,
}
impl Render {
pub async fn new<T: 'static>(event_loop: &EventLoop<T>) -> Self {
let window = WindowBuilder::new()
.with_title("Pong")
.with_inner_size(LogicalSize::<f64>::from((800, 600)))
.build(&event_loop).unwrap();
let surface = wgpu::Surface::create(&window);
pub fn width(&self) -> f32 {
self.sc_desc.width as f32
}
#[allow(dead_code)]
pub fn height(&self) -> f32 {
self.sc_desc.height as f32
}
pub async fn new(window: &Window) -> Self {
let surface = wgpu::Surface::create(window);
let adapter = wgpu::Adapter::request(
&wgpu::RequestAdapterOptions {
@ -56,8 +69,8 @@ impl Render {
&pipeline_layout,
sc_desc.format,
&[Vertex::DESC],
include_str!("textured.vert"),
include_str!("textured.frag"),
include_str!("../../res/shaders/textured.vert"),
include_str!("../../res/shaders/textured.frag"),
);
let vertex_buffer = device.create_buffer(&wgpu::BufferDescriptor {
@ -72,16 +85,21 @@ impl Render {
usage: wgpu::BufferUsage::INDEX | wgpu::BufferUsage::COPY_DST,
});
let font = wgpu_glyph::Font::from_bytes(FONT_BYTES).unwrap();
let glyph_brush = wgpu_glyph::GlyphBrushBuilder::using_font(font)
.build(&device, sc_desc.format);
Self {
window,
surface,
adapter,
device,
queue,
sc_desc,
swap_chain,
pipeline,
vertex_buffer,
index_buffer,
glyph_brush,
}
}
@ -90,14 +108,22 @@ impl Render {
label: None,
});
let (stg_vertex, stg_index, num_indices) = QuadBufferBuilder::new()
.push_ball(&state.ball)
.push_player(&state.player1)
.push_player(&state.player2)
.build(&self.device);
stg_vertex.copy_to_buffer(&mut encoder, &self.vertex_buffer);
stg_index.copy_to_buffer(&mut encoder, &self.index_buffer);
let num_indices = if state.ball.visible
|| state.player1.visible
|| state.player2.visible
{
let (stg_vertex, stg_index, num_indices) = QuadBufferBuilder::new()
.push_ball(&state.ball)
.push_player(&state.player1)
.push_player(&state.player2)
.build(&self.device);
stg_vertex.copy_to_buffer(&mut encoder, &self.vertex_buffer);
stg_index.copy_to_buffer(&mut encoder, &self.index_buffer);
num_indices
} else {
0
};
let frame = self.swap_chain.get_next_texture().unwrap();
@ -114,144 +140,73 @@ impl Render {
depth_stencil_attachment: None,
});
render_pass.set_vertex_buffer(0, &self.vertex_buffer, 0, 0);
render_pass.set_index_buffer(&self.index_buffer, 0, 0);
render_pass.set_pipeline(&self.pipeline);
render_pass.draw_indexed(0..num_indices, 0, 0..1);
if num_indices != 0 {
render_pass.set_vertex_buffer(0, &self.vertex_buffer, 0, 0);
render_pass.set_index_buffer(&self.index_buffer, 0, 0);
render_pass.set_pipeline(&self.pipeline);
render_pass.draw_indexed(0..num_indices, 0, 0..1);
}
drop(render_pass);
self.queue.submit(&[encoder.finish()]);
self.window.request_redraw();
}
}
const U32_SIZE: wgpu::BufferAddress = std::mem::size_of::<u32>() as wgpu::BufferAddress;
pub struct QuadBufferBuilder {
vertex_data: Vec<Vertex>,
index_data: Vec<u32>,
current_quad: u32,
}
impl QuadBufferBuilder {
pub fn new() -> Self {
Self {
vertex_data: Vec::new(),
index_data: Vec::new(),
current_quad: 0,
if state.title_text.visible {
draw_text(&state.title_text, &mut self.glyph_brush);
}
}
pub fn push_ball(mut self, ball: &state::Ball) -> Self {
if ball.visible {
let min_x = ball.position.x - ball.radius;
let min_y = ball.position.y - ball.radius;
let max_x = ball.position.x + ball.radius;
let max_y = ball.position.y + ball.radius;
self.push_quad(min_x, min_y, max_x, max_y)
} else {
self
if state.play_button.visible {
draw_text(&state.play_button, &mut self.glyph_brush);
}
}
pub fn push_player(mut self, player: &state::Player) -> Self {
if player.visible {
self.push_quad(
player.position.x - player.size.x * 0.5,
player.position.y - player.size.y * 0.5,
player.position.x + player.size.x * 0.5,
player.position.y + player.size.y * 0.5,
)
} else {
self
if state.quit_button.visible {
draw_text(&state.quit_button, &mut self.glyph_brush);
}
if state.player1_score.visible {
draw_text(&state.player1_score, &mut self.glyph_brush);
}
if state.player2_score.visible {
draw_text(&state.player2_score, &mut self.glyph_brush);
}
if state.win_text.visible {
draw_text(&state.win_text, &mut self.glyph_brush);
}
}
pub fn push_quad(mut self, min_x: f32, min_y: f32, max_x: f32, max_y: f32) -> Self {
self.vertex_data.extend(&[
Vertex {
position: (min_x, min_y).into(),
tex_coord: (0.0, 0.0).into(),
},
Vertex {
position: (max_x, min_y).into(),
tex_coord: (1.0, 0.0).into(),
},
Vertex {
position: (max_x, max_y).into(),
tex_coord: (1.0, 1.0).into(),
},
Vertex {
position: (min_x, max_y).into(),
tex_coord: (0.0, 1.0).into(),
},
]);
self.index_data.extend(&[
self.current_quad * 4 + 0,
self.current_quad * 4 + 1,
self.current_quad * 4 + 2,
self.current_quad * 4 + 0,
self.current_quad * 4 + 2,
self.current_quad * 4 + 3,
]);
self.current_quad += 1;
self
}
self.glyph_brush.draw_queued(
&self.device,
&mut encoder,
&frame.view,
self.sc_desc.width,
self.sc_desc.height,
).unwrap();
pub fn build(self, device: &wgpu::Device) -> (StagingBuffer, StagingBuffer, u32) {
(
StagingBuffer::new(device, &self.vertex_data),
StagingBuffer::new(device, &self.index_data),
self.index_data.len() as u32,
)
self.queue.submit(&[encoder.finish()]);
}
}
pub struct StagingBuffer {
buffer: wgpu::Buffer,
size: wgpu::BufferAddress,
}
impl StagingBuffer {
pub fn new<T: bytemuck::Pod + Sized>(device: &wgpu::Device, data: &[T]) -> StagingBuffer {
StagingBuffer {
buffer: device.create_buffer_with_data(
bytemuck::cast_slice(data),
wgpu::BufferUsage::COPY_SRC,
),
size: (std::mem::size_of::<T>() * data.len()) as wgpu::BufferAddress
}
}
pub fn copy_to_buffer(&self, encoder: &mut wgpu::CommandEncoder, other: &wgpu::Buffer) {
encoder.copy_buffer_to_buffer(&self.buffer, 0, other, 0, self.size)
fn draw_text(
text: &state::Text,
glyph_brush: &mut wgpu_glyph::GlyphBrush<'static, ()>,
) {
let layout = wgpu_glyph::Layout::default();
if text.centered {
layout.h_align(wgpu_glyph::HorizontalAlign::Center);
}
}
#[derive(Copy, Clone)]
struct Vertex {
position: cgmath::Vector2<f32>,
tex_coord: cgmath::Vector2<f32>,
}
unsafe impl bytemuck::Pod for Vertex {}
unsafe impl bytemuck::Zeroable for Vertex {}
impl Vertex {
const SIZE: wgpu::BufferAddress = std::mem::size_of::<Self>() as wgpu::BufferAddress;
const DESC: wgpu::VertexBufferDescriptor<'static> = wgpu::VertexBufferDescriptor {
stride: Self::SIZE,
step_mode: wgpu::InputStepMode::Vertex,
attributes: &wgpu::vertex_attr_array![
0 => Float2,
1 => Float2
],
let scale = {
let mut size = text.size;
if text.focused {
size += 8.0;
}
wgpu_glyph::Scale::uniform(size)
};
let section = wgpu_glyph::Section {
text: &text.text,
screen_position: (text.position.x, text.position.y),
bounds: (text.bounds.x, text.bounds.y),
color: text.color.into(),
scale,
layout,
..Default::default()
};
}
glyph_brush.queue(section);
}
fn create_render_pipeline(
device: &wgpu::Device,

@ -1,6 +1,5 @@
use cgmath::prelude::*;
#[derive(Eq, PartialEq)]
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum GameState {
MainMenu,
Serving,
@ -13,9 +12,12 @@ pub struct State {
pub ball: Ball,
pub player1: Player,
pub player2: Player,
// pub title_text: Text,
// pub play_button: Text,
// pub quit_button: Text,
pub title_text: Text,
pub play_button: Text,
pub quit_button: Text,
pub player1_score: Text,
pub player2_score: Text,
pub win_text: Text,
pub game_state: GameState,
}
@ -44,10 +46,31 @@ impl Player {
}
}
pub const UNBOUNDED_F32: f32 = std::f32::INFINITY;
#[derive(Debug)]
pub struct Text {
pub position: cgmath::Vector2<f32>,
pub color: wgpu::Color,
pub bounds: cgmath::Vector2<f32>,
pub color: cgmath::Vector4<f32>,
pub text: String,
pub size: f32,
pub visible: bool,
pub focused: bool,
pub centered: bool,
}
impl Default for Text {
fn default() -> Self {
Self {
position: (0.0, 0.0).into(),
bounds: (UNBOUNDED_F32, UNBOUNDED_F32).into(),
color: (1.0, 1.0, 1.0, 1.0).into(),
text: String::new(),
size: 16.0,
visible: false,
focused: false,
centered: false,
}
}
}

@ -4,18 +4,93 @@ use crate::state;
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);
}
pub struct MovementSystem {
#[derive(Debug)]
pub struct MenuSystem {
enter_pressed: bool,
down_pressed: bool,
up_pressed: bool,
}
impl MenuSystem {
pub fn new() -> Self {
Self {
enter_pressed: false,
down_pressed: false,
up_pressed: false,
}
}
}
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.play_button.focused = true;
state.quit_button.visible = true;
state.player1_score.visible = false;
state.player2_score.visible = false;
state.win_text.visible = 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) {
if state.play_button.focused && self.down_pressed {
state.play_button.focused = false;
state.quit_button.focused = true;
} else if state.quit_button.focused && self.up_pressed {
state.quit_button.focused = false;
state.play_button.focused = true;
}
if state.play_button.focused && self.enter_pressed {
state.game_state = state::GameState::Serving;
} else if state.quit_button.focused && self.enter_pressed {
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 MovementSystem {
impl PlaySystem {
pub fn new() -> Self {
Self {
player1_up_pressed: false,
@ -26,7 +101,22 @@ impl MovementSystem {
}
}
impl System for MovementSystem {
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 => {
@ -50,6 +140,7 @@ impl System for MovementSystem {
}
fn update_state(&self, state: &mut state::State) {
// move the players
if self.player1_up_pressed {
state.player1.position.y += 0.025;
}
@ -66,14 +157,12 @@ impl System for MovementSystem {
// normalize players
if state.player1.position.y > 1.0 {
state.player1.position.y = 1.0;
}
if state.player1.position.y < -1.0 {
} else if state.player1.position.y < -1.0 {
state.player1.position.y = -1.0;
}
if state.player2.position.y > 1.0 {
state.player2.position.y = 1.0;
}
if state.player2.position.y < -1.0 {
} else if state.player2.position.y < -1.0 {
state.player2.position.y = -1.0;
}
@ -81,27 +170,114 @@ impl System for MovementSystem {
if state.ball.position.y > 1.0 {
state.ball.position.y = 1.0;
state.ball.velocity.y *= -1.0;
}
if state.ball.position.y < -1.0 {
} else if state.ball.position.y < -1.0 {
state.ball.position.y = -1.0;
state.ball.velocity.y *= -1.0;
}
if state.ball.position.x > 1.0 {
state.ball.position.set(0.0, 0.0);
state.ball.velocity.x *= -1.0;
state.player1.score += 1;
state.game_state = state::GameState::Serving;
} else if state.ball.position.x < -1.0 {
state.player2.score += 1;
state.game_state = state::GameState::Serving;
}
if state.ball.position.x < -1.0 {
state.ball.position.x = 0.0;
state.ball.velocity.x *= -1.0;
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) {
state.ball.position.x -= state.ball.velocity.x + state.player1.size.x;
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) {
state.ball.position.x -= state.ball.velocity.x + state.player2.size.x;
state.ball.velocity.x *= -1.0;
}
}
}
pub struct ServingSystem {
last_time: std::time::Instant,
}
impl ServingSystem {
pub fn new() -> Self {
Self {
last_time: std::time::Instant::now(),
}
}
}
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;
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.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) {
let current_time = std::time::Instant::now();
let delta_time = current_time - self.last_time;
if delta_time.as_secs_f32() > 1.0 {
state.game_state = state::GameState::Playing;
}
}
}
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,
}
}
}
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) {
let current_time = std::time::Instant::now();
let delta_time = current_time - self.last_time;
if delta_time.as_secs_f32() > 1.0 {
state.game_state = state::GameState::MainMenu;
}
}
}

@ -1,4 +1,4 @@
use cgmath::prelude::*;
// use cgmath::prelude::*;
pub fn random_vec2() -> cgmath::Vector2<f32> {
let theta: f32 = rand::random();
@ -10,4 +10,8 @@ pub fn random_vec2() -> cgmath::Vector2<f32> {
pub fn random_vec2_scaled(scale: f32) -> cgmath::Vector2<f32> {
random_vec2() * scale
}
pub fn size_of_slice<T: Sized>(slice: &[T]) -> usize {
std::mem::size_of::<T>() * slice.len()
}
Loading…
Cancel
Save