Add fruit spawning
This commit is contained in:
85
src/fruit.rs
Normal file
85
src/fruit.rs
Normal file
@@ -0,0 +1,85 @@
|
||||
use crate::{
|
||||
grid,
|
||||
snake::{self, SnakeHead, Z_HEIGHT},
|
||||
tick::tick_triggered,
|
||||
};
|
||||
use bevy::prelude::*;
|
||||
use iyes_loopless::prelude::*;
|
||||
use rand::prelude::*;
|
||||
|
||||
pub struct FruitPlugin;
|
||||
|
||||
impl Plugin for FruitPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.add_event::<EatenEvent>()
|
||||
.add_startup_system(spawn_fruit_system)
|
||||
.add_system(
|
||||
eat_fruit_system
|
||||
.run_if(tick_triggered)
|
||||
.before(snake::SystemLabel::SegmentMovement),
|
||||
)
|
||||
.add_system(debug_eaten_event_system)
|
||||
.add_system(despawn_fruit_system)
|
||||
.add_system(spawn_fruit_system.run_on_event::<EatenEvent>());
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EatenEvent(Option<Entity>);
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct Fruit;
|
||||
|
||||
fn spawn_fruit_system(mut commands: Commands) {
|
||||
// TODO: not spawn in snake
|
||||
let mut rng = rand::thread_rng();
|
||||
let coordinate_range = 0..grid::SIZE;
|
||||
let fruit_coordinate = grid::Coordinate(
|
||||
rng.gen_range(coordinate_range.clone()),
|
||||
rng.gen_range(coordinate_range),
|
||||
);
|
||||
|
||||
commands
|
||||
.spawn_bundle(SpriteBundle {
|
||||
sprite: Sprite {
|
||||
color: Color::GREEN,
|
||||
custom_size: Some(Vec2::splat(grid::SEGMENT_SIZE) * 0.6),
|
||||
..Default::default()
|
||||
},
|
||||
transform: Transform::from_translation(Vec2::from(fruit_coordinate).extend(Z_HEIGHT)),
|
||||
..Default::default()
|
||||
})
|
||||
.insert(fruit_coordinate)
|
||||
.insert(Fruit)
|
||||
.insert(Name::new("Fruit"));
|
||||
}
|
||||
|
||||
fn eat_fruit_system(
|
||||
snake_head_query: Query<&grid::Coordinate, With<SnakeHead>>,
|
||||
fruit_query: Query<(Entity, &grid::Coordinate), With<Fruit>>,
|
||||
mut eaten_event_writer: EventWriter<EatenEvent>,
|
||||
) {
|
||||
let &snake_head_coordinate = snake_head_query.single();
|
||||
|
||||
for (fruit, &fruit_coordinate) in fruit_query.iter() {
|
||||
if snake_head_coordinate == fruit_coordinate {
|
||||
eaten_event_writer.send(EatenEvent(Some(fruit)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn despawn_fruit_system(mut commands: Commands, mut event_reader: EventReader<EatenEvent>) {
|
||||
for &EatenEvent(fruit) in event_reader.iter() {
|
||||
if let Some(fruit) = fruit {
|
||||
commands.entity(fruit).despawn();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn debug_eaten_event_system(
|
||||
mut event_writer: EventWriter<EatenEvent>,
|
||||
keypress: Res<Input<KeyCode>>,
|
||||
) {
|
||||
if keypress.just_pressed(KeyCode::Space) {
|
||||
event_writer.send(EatenEvent(None));
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,15 @@
|
||||
use bevy::{prelude::*, render::camera::ScalingMode};
|
||||
// use bevy_editor_pls::prelude::*;
|
||||
use crate::{canvas::CanvasPlugin, snake::SnakePlugin};
|
||||
use crate::{canvas::CanvasPlugin, fruit::FruitPlugin, snake::SnakePlugin, tick::TickPlugin};
|
||||
use bevy_inspector_egui::WorldInspectorPlugin;
|
||||
use grid::{SEGMENT_SIZE, SIZE};
|
||||
use iyes_loopless::prelude::*;
|
||||
|
||||
mod canvas;
|
||||
mod fruit;
|
||||
mod grid;
|
||||
mod snake;
|
||||
mod tick;
|
||||
|
||||
const ASPECT_RATIO: f32 = 16. / 9.;
|
||||
const WINDOW_WIDTH: f32 = 720. * ASPECT_RATIO;
|
||||
@@ -23,7 +25,7 @@ enum AppState {
|
||||
|
||||
fn main() {
|
||||
App::new()
|
||||
.insert_resource(ClearColor(Color::rgb_u8(12, 12, 12)))
|
||||
.insert_resource(ClearColor(Color::rgb_u8(64, 64, 64)))
|
||||
.insert_resource(WindowDescriptor {
|
||||
title: "Bevy-Snake".into(),
|
||||
resizable: true,
|
||||
@@ -35,7 +37,9 @@ fn main() {
|
||||
.add_plugins(DefaultPlugins)
|
||||
.add_plugin(WorldInspectorPlugin::new())
|
||||
// .add_plugin(EditorPlugin)
|
||||
.add_plugin(TickPlugin)
|
||||
.add_plugin(SnakePlugin)
|
||||
.add_plugin(FruitPlugin)
|
||||
.add_plugin(CanvasPlugin)
|
||||
.add_startup_system(setup_system)
|
||||
.add_system(camera_move_system)
|
||||
|
||||
60
src/snake.rs
60
src/snake.rs
@@ -1,43 +1,47 @@
|
||||
use crate::{grid, AppState};
|
||||
use crate::{fruit, grid, tick::tick_triggered, AppState};
|
||||
use bevy::prelude::*;
|
||||
use itertools::Itertools;
|
||||
use iyes_loopless::prelude::*;
|
||||
use std::time::Duration;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, StageLabel)]
|
||||
struct FixedTimeStage;
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
#[derive(StageLabel)]
|
||||
struct MovementStage;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
#[derive(SystemLabel)]
|
||||
pub enum SystemLabel {
|
||||
SegmentMovement,
|
||||
}
|
||||
|
||||
pub struct SnakePlugin;
|
||||
|
||||
impl Plugin for SnakePlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
let mut fixed_time_systems0 = SystemStage::parallel();
|
||||
fixed_time_systems0.add_system(
|
||||
segments_movement_system
|
||||
.run_in_state(AppState::InGame)
|
||||
.run_if_not(about_to_lose),
|
||||
);
|
||||
let movement_stage = SystemStage::parallel();
|
||||
|
||||
let mut fixed_time_systems1 = SystemStage::parallel();
|
||||
fixed_time_systems1
|
||||
.add_system(
|
||||
app.add_startup_system(setup_snake_system)
|
||||
.add_stage_after(CoreStage::Update, MovementStage, movement_stage)
|
||||
.add_system_to_stage(
|
||||
MovementStage,
|
||||
segments_movement_system
|
||||
.run_in_state(AppState::InGame)
|
||||
.run_if_not(about_to_lose)
|
||||
.run_if(tick_triggered)
|
||||
.label(SystemLabel::SegmentMovement),
|
||||
)
|
||||
.add_system_to_stage(
|
||||
MovementStage,
|
||||
head_movement_system
|
||||
.run_in_state(AppState::InGame)
|
||||
.run_if_not(about_to_lose),
|
||||
.run_if_not(about_to_lose)
|
||||
.run_if(tick_triggered)
|
||||
.after(SystemLabel::SegmentMovement),
|
||||
)
|
||||
.add_system(
|
||||
game_over_system
|
||||
.run_in_state(AppState::InGame)
|
||||
.run_if(about_to_lose),
|
||||
);
|
||||
|
||||
app.add_startup_system(setup_snake_system)
|
||||
.add_stage_before(
|
||||
CoreStage::Update,
|
||||
FixedTimeStage,
|
||||
FixedTimestepStage::new(Duration::from_millis(125))
|
||||
.with_stage(fixed_time_systems0)
|
||||
.with_stage(fixed_time_systems1),
|
||||
.run_if(about_to_lose)
|
||||
.run_if(tick_triggered),
|
||||
)
|
||||
.add_system(add_direction_system.run_in_state(AppState::Begin))
|
||||
.add_system(change_direction_system.run_in_state(AppState::InGame))
|
||||
@@ -46,13 +50,13 @@ impl Plugin for SnakePlugin {
|
||||
}
|
||||
}
|
||||
|
||||
const Z_HEIGHT: f32 = 10.;
|
||||
pub const Z_HEIGHT: f32 = 10.;
|
||||
|
||||
#[derive(Component)]
|
||||
struct SnakeSegment;
|
||||
|
||||
#[derive(Component)]
|
||||
struct SnakeHead;
|
||||
pub struct SnakeHead;
|
||||
|
||||
#[derive(Component, Copy, Clone)]
|
||||
enum Direction {
|
||||
@@ -138,10 +142,10 @@ fn add_direction_system(
|
||||
|
||||
fn add_tail_system(
|
||||
mut commands: Commands,
|
||||
keypress: Res<Input<KeyCode>>,
|
||||
mut eaten_event_reader: EventReader<fruit::EatenEvent>,
|
||||
mut snake_query: Query<(Entity, &mut SnakeSegments)>,
|
||||
) {
|
||||
if keypress.just_pressed(KeyCode::Space) {
|
||||
for _ in eaten_event_reader.iter() {
|
||||
let segment =
|
||||
create_snake_segment(&mut commands, grid::Coordinate::splat(grid::Index::MIN / 2));
|
||||
let (snake, mut snake_segments) = snake_query.single_mut();
|
||||
|
||||
44
src/tick.rs
Normal file
44
src/tick.rs
Normal file
@@ -0,0 +1,44 @@
|
||||
use crate::AppState;
|
||||
use bevy::prelude::*;
|
||||
use iyes_loopless::prelude::*;
|
||||
use std::time::Duration;
|
||||
|
||||
const TICK_PERIOD: u64 = 125;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
#[derive(StageLabel)]
|
||||
struct FixedTimeStage;
|
||||
|
||||
pub struct TickPlugin;
|
||||
|
||||
impl Plugin for TickPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
let mut fixed_time_system_stage = SystemStage::parallel();
|
||||
fixed_time_system_stage.add_system(tick_event_system.run_in_state(AppState::InGame));
|
||||
|
||||
app.add_event::<TickEvent>()
|
||||
.add_stage_before(
|
||||
CoreStage::Update,
|
||||
FixedTimeStage,
|
||||
FixedTimestepStage::new(Duration::from_millis(TICK_PERIOD))
|
||||
.with_stage(fixed_time_system_stage),
|
||||
);
|
||||
// .add_system(print_tick_system);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TickEvent;
|
||||
|
||||
fn tick_event_system(mut event_writer: EventWriter<TickEvent>) {
|
||||
event_writer.send(TickEvent);
|
||||
}
|
||||
|
||||
pub fn tick_triggered(mut event_reader: EventReader<TickEvent>) -> bool {
|
||||
event_reader.iter().count() != 0
|
||||
}
|
||||
|
||||
// fn print_tick_system(mut event_reader: EventReader<TickEvent>) {
|
||||
// for _ in event_reader.iter() {
|
||||
// println!("Tick");
|
||||
// }
|
||||
// }
|
||||
Reference in New Issue
Block a user