Added restart, text display and score, updated ReadMe
This commit is contained in:
@@ -30,6 +30,12 @@ brew install sdl2
|
||||
export LIBRARY_PATH="$LIBRARY_PATH:/usr/local/lib"
|
||||
```
|
||||
|
||||
### Run the game
|
||||
```
|
||||
cargo run --release
|
||||
```
|
||||
You can also just enter ``` cargo build --release ```` and then start the binary from target/src
|
||||
|
||||
## How to play ?
|
||||
|
||||
This is a local multiplayer game, so two players can play using the same keyboard, player 1 uses 'w' and 's' and so player 2 uses the arrow keys.
|
||||
|
||||
Binary file not shown.
Binary file not shown.
+114
-58
@@ -5,6 +5,7 @@ use tetra::input::{self, Key};
|
||||
use tetra::math::Vec2;
|
||||
use tetra::window;
|
||||
use tetra::{Context, ContextBuilder, State};
|
||||
use tetra::graphics::text::*;
|
||||
|
||||
const WINDOW_WIDTH:f32 = 640.0;
|
||||
const WINDOW_HEIGHT:f32 = 480.0;
|
||||
@@ -12,6 +13,7 @@ const PADDLE_SPEED:f32 = 8.0;
|
||||
const BALL_SPEED:f32 = 5.0;
|
||||
const PADDLE_SPIN: f32 = 4.0;
|
||||
const BALL_ACC:f32 = 0.05;
|
||||
const SCORE_LOCATION:Vec2<f32> = Vec2::new(16.0,16.0);
|
||||
|
||||
struct Entity {
|
||||
texture: Texture,
|
||||
@@ -51,11 +53,18 @@ impl Entity{
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
enum GameStatus {
|
||||
Playing,
|
||||
Pause,
|
||||
Ended,
|
||||
}
|
||||
struct GameState {
|
||||
player1: Entity,
|
||||
player2: Entity,
|
||||
ball: Entity,
|
||||
score_text: Text,
|
||||
score:i32,
|
||||
state:GameStatus
|
||||
}
|
||||
|
||||
impl GameState{
|
||||
@@ -74,89 +83,136 @@ impl GameState{
|
||||
|
||||
let ball_speed = Vec2::new(-BALL_SPEED,0.0);
|
||||
|
||||
let vector_text = Text::new(
|
||||
format!("Score : {}",0),
|
||||
Font::vector(ctx, "./resources/Roboto-Regular.ttf", 16.0)?,
|
||||
);
|
||||
|
||||
|
||||
Ok(GameState {
|
||||
player1: Entity::new(player1_texture,player1_position),
|
||||
player2: Entity::new(player2_texture,player2_position),
|
||||
ball: Entity::with_velocity(ball_texture,ball_position,ball_speed),
|
||||
score_text: vector_text,
|
||||
score:0,
|
||||
state:GameStatus::Pause,
|
||||
})
|
||||
}
|
||||
fn restart(&mut self){
|
||||
self.player1.position = Vec2::new(16.0,(WINDOW_HEIGHT - self.player1.texture.height() as f32) / 2.0);
|
||||
self.player2.position = Vec2::new(WINDOW_WIDTH - self.player2.texture.width() as f32 -16.0,(WINDOW_HEIGHT - self.player2.texture.height() as f32) / 2.0);
|
||||
self.ball.position = Vec2::new(WINDOW_WIDTH / 2.0 - (self.ball.texture.width() as f32 / 2.0) as f32,WINDOW_HEIGHT / 2.0 - (self.ball.texture.height() as f32 / 2.0) as f32);
|
||||
|
||||
self.ball.velocity = Vec2::new(-BALL_SPEED,0.0);
|
||||
|
||||
self.score = 0;
|
||||
self.score_text.set_content(format!("Score : {}",0));
|
||||
|
||||
self.state = GameStatus::Pause;
|
||||
}
|
||||
}
|
||||
|
||||
impl State for GameState{
|
||||
|
||||
fn draw(&mut self, ctx: &mut Context) -> tetra::Result {
|
||||
graphics::clear(ctx,Color::hex("#CCCCCC"));
|
||||
graphics::clear(ctx,Color::hex("#333333"));
|
||||
|
||||
self.player1.texture.draw(ctx,self.player1.position);
|
||||
self.player2.texture.draw(ctx,self.player2.position);
|
||||
self.ball.texture.draw(ctx,self.ball.position);
|
||||
self.score_text.draw(ctx, SCORE_LOCATION);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn update(&mut self,ctx: &mut Context) -> tetra::Result{
|
||||
//Player 1 movements
|
||||
if input::is_key_down(ctx, Key::W) && self.player1.position.y as i32 > 0{
|
||||
self.player1.position.y -= PADDLE_SPEED;
|
||||
}
|
||||
if input::is_key_down(ctx, Key::S) && ((self.player1.position.y as i32 + self.player1.texture.height()) as f32) < WINDOW_HEIGHT{
|
||||
self.player1.position.y += PADDLE_SPEED;
|
||||
}
|
||||
//player 2 movements
|
||||
if input::is_key_down(ctx, Key::Up) && self.player2.position.y as i32 > 0{
|
||||
self.player2.position.y -= PADDLE_SPEED;
|
||||
}
|
||||
if input::is_key_down(ctx, Key::Down) && ((self.player2.position.y as i32 + self.player1.texture.height()) as f32) < WINDOW_HEIGHT{
|
||||
self.player2.position.y += PADDLE_SPEED;
|
||||
}
|
||||
match self.state {
|
||||
GameStatus::Playing =>{
|
||||
if input::is_key_down(ctx, Key::W) && self.player1.position.y as i32 > 0{
|
||||
self.player1.position.y -= PADDLE_SPEED;
|
||||
}
|
||||
if input::is_key_down(ctx, Key::S) && ((self.player1.position.y as i32 + self.player1.texture.height()) as f32) < WINDOW_HEIGHT{
|
||||
self.player1.position.y += PADDLE_SPEED;
|
||||
}
|
||||
//player 2 movements
|
||||
if input::is_key_down(ctx, Key::Up) && self.player2.position.y as i32 > 0{
|
||||
self.player2.position.y -= PADDLE_SPEED;
|
||||
}
|
||||
if input::is_key_down(ctx, Key::Down) && ((self.player2.position.y as i32 + self.player1.texture.height()) as f32) < WINDOW_HEIGHT{
|
||||
self.player2.position.y += PADDLE_SPEED;
|
||||
}
|
||||
|
||||
//Collisions
|
||||
|
||||
let player1_bounds:Rectangle = self.player1.bounds();
|
||||
let player2_bounds:Rectangle = self.player2.bounds();
|
||||
let ball_bounds:Rectangle = self.ball.bounds();
|
||||
|
||||
let paddle_hit = if ball_bounds.intersects(&player1_bounds) {
|
||||
Some(&self.player1)
|
||||
}else if ball_bounds.intersects(&player2_bounds){
|
||||
Some(&self.player2)
|
||||
}else{
|
||||
None
|
||||
};
|
||||
|
||||
if let Some(paddle) = paddle_hit{
|
||||
//increase ball velocity before flipping it
|
||||
self.ball.velocity.x =
|
||||
-(self.ball.velocity.x + (BALL_ACC * self.ball.velocity.x.signum()));
|
||||
|
||||
//Calculate the offset between the paddle center and the ball
|
||||
let offset = (paddle.centre().y - self.ball.centre().y) / paddle.height();
|
||||
|
||||
self.ball.velocity.y += PADDLE_SPIN * -offset;
|
||||
|
||||
//increase the score as
|
||||
self.score += 1;
|
||||
self.score_text.set_content(format!("Score : {}",self.score));
|
||||
}
|
||||
|
||||
self.ball.position += self.ball.velocity;
|
||||
|
||||
//check borders
|
||||
if self.ball.position.y <= 0.0 || self.ball.position.y + self.ball.height() >= WINDOW_HEIGHT{
|
||||
self.ball.velocity.y = -self.ball.velocity.y;
|
||||
}
|
||||
//check winner
|
||||
if self.ball.position.x <= 0.0{
|
||||
//window::quit(ctx);
|
||||
self.score_text.set_content("Player 2 Won!\nPress Enter to play again");
|
||||
self.state = GameStatus::Ended;
|
||||
}
|
||||
if self.ball.position.x + self.ball.width() >= WINDOW_WIDTH{
|
||||
//window::quit(ctx);
|
||||
self.score_text.set_content("Player 2 Won!\nPress Enter to play again");
|
||||
self.state = GameStatus::Ended;
|
||||
}
|
||||
}
|
||||
GameStatus::Pause =>{
|
||||
self.score_text.set_content("Remember \nPlayer 1 uses 'w' and 's'\nPlayer 2 uses up and down arrow keys\nPress Enter to start the game");
|
||||
|
||||
//Collisions
|
||||
|
||||
let player1_bounds:Rectangle = self.player1.bounds();
|
||||
let player2_bounds:Rectangle = self.player2.bounds();
|
||||
let ball_bounds:Rectangle = self.ball.bounds();
|
||||
|
||||
let paddle_hit = if ball_bounds.intersects(&player1_bounds) {
|
||||
Some(&self.player1)
|
||||
}else if ball_bounds.intersects(&player2_bounds){
|
||||
Some(&self.player2)
|
||||
}else{
|
||||
None
|
||||
};
|
||||
|
||||
if let Some(paddle) = paddle_hit{
|
||||
//increase ball velocity before flipping it
|
||||
self.ball.velocity.x =
|
||||
-(self.ball.velocity.x + (BALL_ACC * self.ball.velocity.x.signum()));
|
||||
|
||||
//Calculate the offset between the paddle center and the ball
|
||||
let offset = (paddle.centre().y - self.ball.centre().y) / paddle.height();
|
||||
|
||||
self.ball.velocity.y += PADDLE_SPIN * -offset;
|
||||
}
|
||||
|
||||
self.ball.position += self.ball.velocity;
|
||||
|
||||
//check borders
|
||||
if self.ball.position.y <= 0.0 || self.ball.position.y + self.ball.height() >= WINDOW_HEIGHT{
|
||||
self.ball.velocity.y = -self.ball.velocity.y;
|
||||
}
|
||||
//check winner
|
||||
if self.ball.position.x <= 0.0{
|
||||
window::quit(ctx);
|
||||
println!("Player 2 won!");
|
||||
}
|
||||
if self.ball.position.x + self.ball.width() >= WINDOW_WIDTH{
|
||||
window::quit(ctx);
|
||||
println!("Player 1 won!");
|
||||
if input::is_key_down(ctx, Key::Enter){
|
||||
self.state = GameStatus::Playing;
|
||||
}
|
||||
}
|
||||
GameStatus::Ended =>{
|
||||
if input::is_key_down(ctx, Key::Enter){
|
||||
self.restart();
|
||||
println!("DEBUG: game restarted");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn main() -> tetra::Result{
|
||||
ContextBuilder::new("Pong",WINDOW_WIDTH as i32,WINDOW_HEIGHT as i32)
|
||||
.quit_on_escape(true)
|
||||
.build()?
|
||||
.run(GameState::new)
|
||||
ContextBuilder::new("Rusty Pong",WINDOW_WIDTH as i32,WINDOW_HEIGHT as i32)
|
||||
.quit_on_escape(true)
|
||||
.build()?
|
||||
.run(GameState::new)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user