Add fruit spawning

This commit is contained in:
2022-08-19 22:09:34 +02:00
parent d1db542b16
commit 7c3855853f
6 changed files with 205 additions and 30 deletions

37
Cargo.lock generated
View File

@@ -248,6 +248,7 @@ dependencies = [
"bevy-inspector-egui", "bevy-inspector-egui",
"itertools", "itertools",
"iyes_loopless", "iyes_loopless",
"rand",
] ]
[[package]] [[package]]
@@ -2716,6 +2717,12 @@ dependencies = [
"unicode-xid", "unicode-xid",
] ]
[[package]]
name = "ppv-lite86"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
[[package]] [[package]]
name = "pretty-type-name" name = "pretty-type-name"
version = "1.0.0" version = "1.0.0"
@@ -2763,6 +2770,36 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17fd96390ed3feda12e1dfe2645ed587e0bea749e319333f104a33ff62f77a0b" checksum = "17fd96390ed3feda12e1dfe2645ed587e0bea749e319333f104a33ff62f77a0b"
[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
dependencies = [
"getrandom",
]
[[package]] [[package]]
name = "range-alloc" name = "range-alloc"
version = "0.1.2" version = "0.1.2"

View File

@@ -9,6 +9,7 @@ edition = "2021"
iyes_loopless = "0.7.0" iyes_loopless = "0.7.0"
itertools = "0.10.3" itertools = "0.10.3"
bevy-inspector-egui = "0.12.1" bevy-inspector-egui = "0.12.1"
rand = "0.8.5"
# bevy_editor_pls = { git = "https://github.com/jakobhellermann/bevy_editor_pls" } # bevy_editor_pls = { git = "https://github.com/jakobhellermann/bevy_editor_pls" }
[dependencies.bevy] [dependencies.bevy]

85
src/fruit.rs Normal file
View 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));
}
}

View File

@@ -1,13 +1,15 @@
use bevy::{prelude::*, render::camera::ScalingMode}; use bevy::{prelude::*, render::camera::ScalingMode};
// use bevy_editor_pls::prelude::*; // 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 bevy_inspector_egui::WorldInspectorPlugin;
use grid::{SEGMENT_SIZE, SIZE}; use grid::{SEGMENT_SIZE, SIZE};
use iyes_loopless::prelude::*; use iyes_loopless::prelude::*;
mod canvas; mod canvas;
mod fruit;
mod grid; mod grid;
mod snake; mod snake;
mod tick;
const ASPECT_RATIO: f32 = 16. / 9.; const ASPECT_RATIO: f32 = 16. / 9.;
const WINDOW_WIDTH: f32 = 720. * ASPECT_RATIO; const WINDOW_WIDTH: f32 = 720. * ASPECT_RATIO;
@@ -23,7 +25,7 @@ enum AppState {
fn main() { fn main() {
App::new() App::new()
.insert_resource(ClearColor(Color::rgb_u8(12, 12, 12))) .insert_resource(ClearColor(Color::rgb_u8(64, 64, 64)))
.insert_resource(WindowDescriptor { .insert_resource(WindowDescriptor {
title: "Bevy-Snake".into(), title: "Bevy-Snake".into(),
resizable: true, resizable: true,
@@ -35,7 +37,9 @@ fn main() {
.add_plugins(DefaultPlugins) .add_plugins(DefaultPlugins)
.add_plugin(WorldInspectorPlugin::new()) .add_plugin(WorldInspectorPlugin::new())
// .add_plugin(EditorPlugin) // .add_plugin(EditorPlugin)
.add_plugin(TickPlugin)
.add_plugin(SnakePlugin) .add_plugin(SnakePlugin)
.add_plugin(FruitPlugin)
.add_plugin(CanvasPlugin) .add_plugin(CanvasPlugin)
.add_startup_system(setup_system) .add_startup_system(setup_system)
.add_system(camera_move_system) .add_system(camera_move_system)

View File

@@ -1,43 +1,47 @@
use crate::{grid, AppState}; use crate::{fruit, grid, tick::tick_triggered, AppState};
use bevy::prelude::*; use bevy::prelude::*;
use itertools::Itertools; use itertools::Itertools;
use iyes_loopless::prelude::*; use iyes_loopless::prelude::*;
use std::time::Duration;
#[derive(Debug, Clone, PartialEq, Eq, Hash, StageLabel)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
struct FixedTimeStage; #[derive(StageLabel)]
struct MovementStage;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[derive(SystemLabel)]
pub enum SystemLabel {
SegmentMovement,
}
pub struct SnakePlugin; pub struct SnakePlugin;
impl Plugin for SnakePlugin { impl Plugin for SnakePlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
let mut fixed_time_systems0 = SystemStage::parallel(); let movement_stage = SystemStage::parallel();
fixed_time_systems0.add_system(
segments_movement_system
.run_in_state(AppState::InGame)
.run_if_not(about_to_lose),
);
let mut fixed_time_systems1 = SystemStage::parallel(); app.add_startup_system(setup_snake_system)
fixed_time_systems1 .add_stage_after(CoreStage::Update, MovementStage, movement_stage)
.add_system( .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 head_movement_system
.run_in_state(AppState::InGame) .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( .add_system(
game_over_system game_over_system
.run_in_state(AppState::InGame) .run_in_state(AppState::InGame)
.run_if(about_to_lose), .run_if(about_to_lose)
); .run_if(tick_triggered),
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),
) )
.add_system(add_direction_system.run_in_state(AppState::Begin)) .add_system(add_direction_system.run_in_state(AppState::Begin))
.add_system(change_direction_system.run_in_state(AppState::InGame)) .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)] #[derive(Component)]
struct SnakeSegment; struct SnakeSegment;
#[derive(Component)] #[derive(Component)]
struct SnakeHead; pub struct SnakeHead;
#[derive(Component, Copy, Clone)] #[derive(Component, Copy, Clone)]
enum Direction { enum Direction {
@@ -138,10 +142,10 @@ fn add_direction_system(
fn add_tail_system( fn add_tail_system(
mut commands: Commands, mut commands: Commands,
keypress: Res<Input<KeyCode>>, mut eaten_event_reader: EventReader<fruit::EatenEvent>,
mut snake_query: Query<(Entity, &mut SnakeSegments)>, mut snake_query: Query<(Entity, &mut SnakeSegments)>,
) { ) {
if keypress.just_pressed(KeyCode::Space) { for _ in eaten_event_reader.iter() {
let segment = let segment =
create_snake_segment(&mut commands, grid::Coordinate::splat(grid::Index::MIN / 2)); create_snake_segment(&mut commands, grid::Coordinate::splat(grid::Index::MIN / 2));
let (snake, mut snake_segments) = snake_query.single_mut(); let (snake, mut snake_segments) = snake_query.single_mut();

44
src/tick.rs Normal file
View 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");
// }
// }