Compare commits
10 Commits
d12e53472d
...
7750d192e5
| Author | SHA1 | Date | |
|---|---|---|---|
| 7750d192e5 | |||
| cd6408666f | |||
| d59aa18809 | |||
| 91dbf577bd | |||
| d6f900a3e4 | |||
| 5cb87a3a81 | |||
| 4a98354069 | |||
| 824231a86b | |||
| 58d3258448 | |||
| 202db0be70 |
3951
Cargo.lock
generated
3951
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
13
Cargo.toml
13
Cargo.toml
@@ -4,15 +4,14 @@ version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
itertools = "0.11.0"
|
||||
rand = "0.8.5"
|
||||
bevy_editor_pls = { git = "https://github.com/jakobhellermann/bevy_editor_pls" }
|
||||
bevy_tweening = "0.8.0"
|
||||
leafwing-input-manager = "0.10.0"
|
||||
bevy_asset_loader = "0.17.0"
|
||||
itertools = "0.14.0"
|
||||
rand = "0.9.0"
|
||||
bevy_tweening = "0.12.0"
|
||||
leafwing-input-manager = "0.16.0"
|
||||
bevy_asset_loader = "0.22.0"
|
||||
|
||||
[dependencies.bevy]
|
||||
version = "0.11"
|
||||
version = "0.15"
|
||||
features = ["wayland", "wav"]
|
||||
|
||||
# [profile.dev.package."*"]
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
struct FragmentInput {
|
||||
@builtin(position) fragment_coordinate: vec4<f32>,
|
||||
@location(0) world_position: vec4<f32>,
|
||||
@location(1) world_normal: vec3<f32>,
|
||||
@location(2) uv: vec2<f32>,
|
||||
};
|
||||
#import bevy_sprite::mesh2d_vertex_output::VertexOutput
|
||||
|
||||
struct TileLight {
|
||||
color: vec4<f32>,
|
||||
@@ -18,11 +13,11 @@ struct CanvasMaterial {
|
||||
};
|
||||
|
||||
|
||||
@group(1) @binding(0)
|
||||
@group(2) @binding(0)
|
||||
var<uniform> canvas_material: CanvasMaterial;
|
||||
|
||||
@fragment
|
||||
fn fragment(input: FragmentInput) -> @location(0) vec4<f32> {
|
||||
fn fragment(input: VertexOutput) -> @location(0) vec4<f32> {
|
||||
var output_color = canvas_material.ambient_color;
|
||||
|
||||
var lighting = vec4<f32>();
|
||||
@@ -33,6 +28,5 @@ fn fragment(input: FragmentInput) -> @location(0) vec4<f32> {
|
||||
}
|
||||
|
||||
output_color += lighting;
|
||||
|
||||
return output_color;
|
||||
}
|
||||
|
||||
191
src/canvas.rs
191
src/canvas.rs
@@ -1,109 +1,130 @@
|
||||
mod tile_light;
|
||||
// mod tile_light;
|
||||
|
||||
use crate::grid;
|
||||
use bevy::{
|
||||
prelude::*,
|
||||
reflect::{TypeUuid, TypePath},
|
||||
render::{
|
||||
render_resource::{encase, AsBindGroup, OwnedBindingResource, ShaderRef, ShaderType},
|
||||
renderer::RenderQueue,
|
||||
RenderApp, RenderSet,
|
||||
},
|
||||
sprite::{Material2d, Material2dPlugin, MaterialMesh2dBundle, RenderMaterials2d},
|
||||
// reflect::TypePath,
|
||||
// render::{
|
||||
// render_resource::{encase, AsBindGroup, OwnedBindingResource, ShaderRef, ShaderType},
|
||||
// renderer::RenderQueue,
|
||||
// Extract, Render, RenderApp, RenderSet,
|
||||
// },
|
||||
// sprite::{Material2d, Material2dPlugin, MaterialMesh2dBundle},
|
||||
};
|
||||
use tile_light::{extract_tile_lights, TileLight, TileLightsUniform};
|
||||
|
||||
pub(crate) use tile_light::EmissiveTile;
|
||||
// pub(crate) use tile_light::EmissiveTile;
|
||||
|
||||
pub struct CanvasPlugin;
|
||||
|
||||
impl Plugin for CanvasPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.add_plugins(Material2dPlugin::<CanvasMaterial>::default())
|
||||
.add_systems(Startup, spawn_canvas);
|
||||
// app.add_plugins(Material2dPlugin::<CanvasMaterial>::default())
|
||||
// .add_systems(Startup, spawn_canvas);
|
||||
app.add_systems(Startup, spawn_canvas);
|
||||
|
||||
app.sub_app_mut(RenderApp)
|
||||
.add_systems(ExtractSchedule, extract_tile_lights)
|
||||
.add_systems(Update, prepare_canvas_material.in_set(RenderSet::Prepare));
|
||||
// app.sub_app_mut(RenderApp)
|
||||
// .add_systems(ExtractSchedule, (extract_tile_lights, extract_canvas))
|
||||
// .add_systems(
|
||||
// Render,
|
||||
// prepare_canvas_material.in_set(RenderSet::PrepareAssets),
|
||||
// );
|
||||
}
|
||||
}
|
||||
|
||||
const CANVAS_COLOR: Color = Color::rgb(0.05, 0.05, 0.05);
|
||||
const CANVAS_COLOR: Color = Color::srgb(0.075, 0.075, 0.075);
|
||||
|
||||
#[derive(AsBindGroup, TypeUuid, TypePath, Clone)]
|
||||
#[uuid = "24f83f6e-e52d-41a6-bf1d-0e46e57a4995"]
|
||||
struct CanvasMaterial {
|
||||
#[uniform(0)]
|
||||
ambient_color: Color,
|
||||
#[uniform(0)]
|
||||
tile_lights: tile_light::TileLightsUniform,
|
||||
#[uniform(0)]
|
||||
number_of_lights: u32,
|
||||
}
|
||||
// #[derive(AsBindGroup, TypePath, Asset, Clone)]
|
||||
// struct CanvasMaterial {
|
||||
// #[uniform(0)]
|
||||
// ambient_color: Color,
|
||||
// #[uniform(0)]
|
||||
// tile_lights: tile_light::TileLightsUniform,
|
||||
// #[uniform(0)]
|
||||
// number_of_lights: u32,
|
||||
// }
|
||||
|
||||
impl Default for CanvasMaterial {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
ambient_color: CANVAS_COLOR,
|
||||
number_of_lights: 0,
|
||||
tile_lights: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
// impl Default for CanvasMaterial {
|
||||
// fn default() -> Self {
|
||||
// Self {
|
||||
// ambient_color: CANVAS_COLOR,
|
||||
// number_of_lights: 0,
|
||||
// tile_lights: Default::default(),
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
#[derive(ShaderType)]
|
||||
struct CanvasMaterialUniform {
|
||||
ambient_color: Color,
|
||||
tile_lights: tile_light::TileLightsUniform,
|
||||
number_of_lights: u32,
|
||||
}
|
||||
// #[derive(ShaderType)]
|
||||
// struct CanvasMaterialUniform {
|
||||
// ambient_color: Color,
|
||||
// tile_lights: tile_light::TileLightsUniform,
|
||||
// number_of_lights: u32,
|
||||
// }
|
||||
|
||||
impl Material2d for CanvasMaterial {
|
||||
fn fragment_shader() -> ShaderRef {
|
||||
"canvas_shader.wgsl".into()
|
||||
}
|
||||
}
|
||||
// impl Material2d for CanvasMaterial {
|
||||
// fn fragment_shader() -> ShaderRef {
|
||||
// "canvas_shader.wgsl".into()
|
||||
// }
|
||||
// }
|
||||
|
||||
fn prepare_canvas_material(
|
||||
materials: Res<RenderMaterials2d<CanvasMaterial>>,
|
||||
canvas_query: Query<&Handle<CanvasMaterial>>,
|
||||
tile_light_query: Query<&TileLight>,
|
||||
render_queue: Res<RenderQueue>,
|
||||
) {
|
||||
let tile_lights = tile_light_query.iter().cloned().collect::<Vec<_>>();
|
||||
let tile_lights_uniform = TileLightsUniform::from_lights(&tile_lights);
|
||||
// fn prepare_canvas_material(
|
||||
// materials: Res<RenderMaterials2d<CanvasMaterial>>,
|
||||
// canvas_query: Query<&Handle<CanvasMaterial>>,
|
||||
// tile_light_query: Query<&TileLight>,
|
||||
// render_queue: Res<RenderQueue>,
|
||||
// ) {
|
||||
// let tile_lights = tile_light_query.iter().cloned().collect::<Vec<_>>();
|
||||
// let tile_lights_uniform = TileLightsUniform::from_lights(&tile_lights);
|
||||
|
||||
for handle in canvas_query.iter() {
|
||||
if let Some(material) = materials.get(handle) {
|
||||
let binding = &material.bindings[0];
|
||||
if let OwnedBindingResource::Buffer(cur_buffer) = binding {
|
||||
let mut buffer = encase::UniformBuffer::new(Vec::new());
|
||||
buffer
|
||||
.write(&CanvasMaterialUniform {
|
||||
ambient_color: CANVAS_COLOR,
|
||||
tile_lights: tile_lights_uniform.clone(),
|
||||
number_of_lights: tile_lights.len() as _,
|
||||
})
|
||||
.unwrap();
|
||||
render_queue.write_buffer(cur_buffer, 0, buffer.as_ref());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// for handle in canvas_query.iter() {
|
||||
// if let Some(material) = materials.get(&handle.id()) {
|
||||
// let binding = &material.bindings[0];
|
||||
// if let (_, OwnedBindingResource::Buffer(cur_buffer)) = binding {
|
||||
// let mut buffer = encase::UniformBuffer::new(Vec::new());
|
||||
// buffer
|
||||
// .write(&CanvasMaterialUniform {
|
||||
// ambient_color: CANVAS_COLOR,
|
||||
// tile_lights: tile_lights_uniform.clone(),
|
||||
// number_of_lights: tile_lights.len() as _,
|
||||
// })
|
||||
// .unwrap();
|
||||
// render_queue.write_buffer(cur_buffer, 0, buffer.as_ref());
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
fn spawn_canvas(
|
||||
mut commands: Commands,
|
||||
mut mesh_assets: ResMut<Assets<Mesh>>,
|
||||
mut canvas_material_assets: ResMut<Assets<CanvasMaterial>>,
|
||||
) {
|
||||
commands
|
||||
.spawn(MaterialMesh2dBundle {
|
||||
mesh: mesh_assets.add(Mesh::from(shape::Quad::default())).into(),
|
||||
material: canvas_material_assets.add(CanvasMaterial::default()),
|
||||
transform: Transform::from_scale(Vec3::splat(
|
||||
grid::SEGMENT_SIZE * f32::from(grid::SIZE),
|
||||
)),
|
||||
// fn extract_canvas(
|
||||
// mut commands: Commands,
|
||||
// canvas_query: Extract<Query<(Entity, &Handle<CanvasMaterial>)>>,
|
||||
// ) {
|
||||
// for (entity, handle) in canvas_query.iter() {
|
||||
// commands.get_or_spawn(entity).insert(handle.clone());
|
||||
// }
|
||||
// }
|
||||
|
||||
// fn spawn_canvas(
|
||||
// mut commands: Commands,
|
||||
// mut mesh_assets: ResMut<Assets<Mesh>>,
|
||||
// mut canvas_material_assets: ResMut<Assets<CanvasMaterial>>,
|
||||
// ) {
|
||||
// commands
|
||||
// .spawn(MaterialMesh2dBundle {
|
||||
// mesh: mesh_assets.add(Mesh::from(Rectangle::default())).into(),
|
||||
// material: canvas_material_assets.add(CanvasMaterial::default()),
|
||||
// transform: Transform::from_scale(Vec3::splat(
|
||||
// grid::SEGMENT_SIZE * f32::from(grid::SIZE),
|
||||
// )),
|
||||
// ..Default::default()
|
||||
// })
|
||||
// .insert(Name::new("Canvas"));
|
||||
// }
|
||||
|
||||
fn spawn_canvas(mut commands: Commands) {
|
||||
commands.spawn((
|
||||
Sprite {
|
||||
color: CANVAS_COLOR.into(),
|
||||
..Default::default()
|
||||
})
|
||||
.insert(Name::new("Canvas"));
|
||||
},
|
||||
Transform::from_scale(Vec3::splat(grid::SEGMENT_SIZE * f32::from(grid::SIZE))),
|
||||
));
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
use bevy::{
|
||||
math::Vec3Swizzles,
|
||||
prelude::*,
|
||||
render::{render_resource::ShaderType, Extract},
|
||||
// render::{render_resource::ShaderType, Extract},
|
||||
};
|
||||
|
||||
const MAX_TILE_LIGHTS: usize = 256;
|
||||
// const MAX_TILE_LIGHTS: usize = 256;
|
||||
|
||||
#[derive(Component, Clone, Copy)]
|
||||
pub(crate) struct EmissiveTile {
|
||||
@@ -17,48 +16,48 @@ impl Default for EmissiveTile {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Default, ShaderType, Component)]
|
||||
pub(super) struct TileLight {
|
||||
color: Color,
|
||||
intensity: f32,
|
||||
position: Vec2,
|
||||
}
|
||||
// #[derive(Copy, Clone, Default, ShaderType, Component)]
|
||||
// pub(super) struct TileLight {
|
||||
// color: Color,
|
||||
// intensity: f32,
|
||||
// position: Vec2,
|
||||
// }
|
||||
|
||||
#[derive(ShaderType, Clone)]
|
||||
pub(super) struct TileLightsUniform {
|
||||
pub data: Box<[TileLight; MAX_TILE_LIGHTS]>,
|
||||
}
|
||||
// #[derive(ShaderType, Clone)]
|
||||
// pub(super) struct TileLightsUniform {
|
||||
// pub data: Box<[TileLight; MAX_TILE_LIGHTS]>,
|
||||
// }
|
||||
|
||||
impl TileLightsUniform {
|
||||
pub(super) fn from_lights(tile_lights: &[TileLight]) -> Self {
|
||||
let mut tile_lights_uniform = TileLightsUniform::default();
|
||||
// impl TileLightsUniform {
|
||||
// pub(super) fn from_lights(tile_lights: &[TileLight]) -> Self {
|
||||
// let mut tile_lights_uniform = TileLightsUniform::default();
|
||||
|
||||
let len = tile_lights.len().min(MAX_TILE_LIGHTS);
|
||||
let src = &tile_lights[..len];
|
||||
let dst = &mut tile_lights_uniform.data[..len];
|
||||
dst.copy_from_slice(src);
|
||||
// let len = tile_lights.len().min(MAX_TILE_LIGHTS);
|
||||
// let src = &tile_lights[..len];
|
||||
// let dst = &mut tile_lights_uniform.data[..len];
|
||||
// dst.copy_from_slice(src);
|
||||
|
||||
tile_lights_uniform
|
||||
}
|
||||
}
|
||||
// tile_lights_uniform
|
||||
// }
|
||||
// }
|
||||
|
||||
impl Default for TileLightsUniform {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
data: Box::new([TileLight::default(); MAX_TILE_LIGHTS]),
|
||||
}
|
||||
}
|
||||
}
|
||||
// impl Default for TileLightsUniform {
|
||||
// fn default() -> Self {
|
||||
// Self {
|
||||
// data: Box::new([TileLight::default(); MAX_TILE_LIGHTS]),
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
pub(super) fn extract_tile_lights(
|
||||
mut commands: Commands,
|
||||
emissive_sprite_query: Extract<Query<(Entity, &EmissiveTile, &Sprite, &GlobalTransform)>>,
|
||||
) {
|
||||
for (entity, emissive_sprite, sprite, global_transform) in emissive_sprite_query.iter() {
|
||||
commands.get_or_spawn(entity).insert(TileLight {
|
||||
color: sprite.color,
|
||||
intensity: emissive_sprite.intensity,
|
||||
position: global_transform.translation().xy(),
|
||||
});
|
||||
}
|
||||
}
|
||||
// pub(super) fn extract_tile_lights(
|
||||
// mut commands: Commands,
|
||||
// emissive_sprite_query: Extract<Query<(Entity, &EmissiveTile, &Sprite, &GlobalTransform)>>,
|
||||
// ) {
|
||||
// for (entity, emissive_sprite, sprite, global_transform) in emissive_sprite_query.iter() {
|
||||
// commands.get_or_spawn(entity).insert(TileLight {
|
||||
// color: sprite.color,
|
||||
// intensity: emissive_sprite.intensity,
|
||||
// position: global_transform.translation().xy(),
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
|
||||
17
src/fruit.rs
17
src/fruit.rs
@@ -31,7 +31,7 @@ pub struct EatenEvent(Option<Entity>);
|
||||
pub struct Fruit;
|
||||
|
||||
pub fn eaten_event_sent(mut eaten_event_reader: EventReader<EatenEvent>) -> bool {
|
||||
eaten_event_reader.iter().count() != 0
|
||||
eaten_event_reader.read().count() != 0
|
||||
}
|
||||
|
||||
fn spawn_fruit_system(mut commands: Commands, coordinate_query: Query<&grid::Coordinate>) {
|
||||
@@ -56,18 +56,17 @@ fn spawn_fruit_system(mut commands: Commands, coordinate_query: Query<&grid::Coo
|
||||
.expect("Should always be found.");
|
||||
|
||||
commands
|
||||
.spawn(SpriteBundle {
|
||||
sprite: Sprite {
|
||||
color: Color::GREEN,
|
||||
.spawn((
|
||||
Sprite {
|
||||
color: Srgba::GREEN.into(),
|
||||
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()
|
||||
})
|
||||
Transform::from_translation(Vec2::from(fruit_coordinate).extend(Z_HEIGHT)),
|
||||
))
|
||||
.insert(fruit_coordinate)
|
||||
.insert(Fruit)
|
||||
.insert(crate::canvas::EmissiveTile { intensity: 1.0 })
|
||||
// .insert(crate::canvas::EmissiveTile { intensity: 1.0 })
|
||||
.insert(Name::new("Fruit"));
|
||||
}
|
||||
|
||||
@@ -86,7 +85,7 @@ fn eat_fruit_system(
|
||||
}
|
||||
|
||||
fn despawn_fruit_system(mut commands: Commands, mut event_reader: EventReader<EatenEvent>) {
|
||||
for &EatenEvent(fruit) in event_reader.iter() {
|
||||
for &EatenEvent(fruit) in event_reader.read() {
|
||||
if let Some(fruit) = fruit {
|
||||
commands.entity(fruit).despawn();
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use bevy::prelude::*;
|
||||
|
||||
// #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default, States)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default, States)]
|
||||
pub enum LevelState {
|
||||
#[default]
|
||||
Begin,
|
||||
@@ -10,12 +9,12 @@ pub enum LevelState {
|
||||
}
|
||||
|
||||
#[derive(Resource)]
|
||||
struct Level(u32);
|
||||
pub struct Level(pub u32);
|
||||
|
||||
pub(super) struct LevelPlugin;
|
||||
|
||||
impl Plugin for LevelPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
// app.add_state::<LevelState>();
|
||||
app.insert_resource(Level(1));
|
||||
}
|
||||
}
|
||||
|
||||
61
src/main.rs
61
src/main.rs
@@ -11,9 +11,6 @@ use bevy_asset_loader::prelude::*;
|
||||
use bevy_tweening::TweeningPlugin;
|
||||
use std::time::Duration;
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
use bevy_editor_pls::prelude::*;
|
||||
|
||||
mod audio;
|
||||
mod canvas;
|
||||
mod fruit;
|
||||
@@ -27,12 +24,22 @@ const WINDOW_WIDTH: f32 = WINDOW_HEIGHT * ASPECT_RATIO;
|
||||
const WINDOW_HEIGHT: f32 = 720.;
|
||||
const TICK_PERIOD: Duration = Duration::from_millis(125);
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, States)]
|
||||
enum AppState {
|
||||
MainMenu,
|
||||
InGame(LevelState),
|
||||
}
|
||||
|
||||
impl Default for AppState {
|
||||
fn default() -> Self {
|
||||
if cfg!(debug_assertions) {
|
||||
Self::InGame(LevelState::Begin)
|
||||
} else {
|
||||
Self::MainMenu
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct AppStateIter {
|
||||
current: Option<AppState>,
|
||||
@@ -60,37 +67,15 @@ impl Iterator for AppStateIter {
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for AppState {
|
||||
fn default() -> Self {
|
||||
if cfg!(debug_assertions) {
|
||||
Self::InGame(LevelState::Begin)
|
||||
} else {
|
||||
Self::MainMenu
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl States for AppState {
|
||||
type Iter = AppStateIter;
|
||||
|
||||
fn variants() -> Self::Iter {
|
||||
AppStateIter::default()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Resource)]
|
||||
struct Score(u32);
|
||||
|
||||
#[derive(Resource)]
|
||||
struct Level(u32);
|
||||
|
||||
fn main() {
|
||||
let mut app = App::new();
|
||||
|
||||
app.insert_resource(ClearColor(Color::BLACK))
|
||||
.insert_resource(FixedTime::new(TICK_PERIOD))
|
||||
.insert_resource(Time::<Fixed>::from_duration(TICK_PERIOD))
|
||||
.insert_resource(Score(0))
|
||||
.insert_resource(Level(0))
|
||||
.add_plugins(DefaultPlugins.set(WindowPlugin {
|
||||
primary_window: Some(Window {
|
||||
title: "Bevy-Snake".into(),
|
||||
@@ -101,7 +86,7 @@ fn main() {
|
||||
..Default::default()
|
||||
}))
|
||||
.init_collection::<audio::Assets>()
|
||||
.add_state::<AppState>()
|
||||
.init_state::<AppState>()
|
||||
.add_plugins((
|
||||
TweeningPlugin,
|
||||
SnakePlugin,
|
||||
@@ -115,8 +100,7 @@ fn main() {
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
app.add_plugins(EditorPlugin::new())
|
||||
.add_systems(Update, (bevy::window::close_on_esc, pause_system));
|
||||
app.add_systems(Update, pause_system);
|
||||
}
|
||||
|
||||
app.run();
|
||||
@@ -126,25 +110,26 @@ fn setup_camera_system(mut commands: Commands) {
|
||||
let grid_dimensions = f32::from(SIZE) * SEGMENT_SIZE * 1.2;
|
||||
|
||||
commands
|
||||
.spawn(Camera2dBundle {
|
||||
projection: OrthographicProjection {
|
||||
.spawn((
|
||||
Camera2d,
|
||||
OrthographicProjection {
|
||||
scaling_mode: ScalingMode::AutoMin {
|
||||
min_width: grid_dimensions,
|
||||
min_height: grid_dimensions,
|
||||
},
|
||||
..Default::default()
|
||||
near: -1000.,
|
||||
..OrthographicProjection::default_2d()
|
||||
},
|
||||
..Default::default()
|
||||
})
|
||||
))
|
||||
.insert(Name::new("Orthographic Camera"));
|
||||
}
|
||||
|
||||
fn update_score_system(mut score: ResMut<Score>, mut eaten_event: EventReader<fruit::EatenEvent>) {
|
||||
score.0 += eaten_event.iter().count() as u32;
|
||||
score.0 += eaten_event.read().count() as u32;
|
||||
}
|
||||
|
||||
fn pause_system(keypress: Res<Input<KeyCode>>, mut time: ResMut<Time>) {
|
||||
if keypress.just_pressed(KeyCode::P) {
|
||||
fn pause_system(keypress: Res<ButtonInput<KeyCode>>, mut time: ResMut<Time<Virtual>>) {
|
||||
if keypress.just_pressed(KeyCode::KeyP) {
|
||||
if time.is_paused() {
|
||||
time.unpause()
|
||||
} else {
|
||||
|
||||
61
src/snake.rs
61
src/snake.rs
@@ -20,7 +20,7 @@ pub struct SnakePlugin;
|
||||
|
||||
impl Plugin for SnakePlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.configure_set(
|
||||
app.configure_sets(
|
||||
Update,
|
||||
SystemSet::Movement.after(SystemSet::CollisionDetection),
|
||||
);
|
||||
@@ -64,7 +64,7 @@ impl Plugin for SnakePlugin {
|
||||
.run_if(in_state(AppState::InGame(LevelState::Begin))),
|
||||
direction::change_direction_system.run_if(
|
||||
in_state(AppState::InGame(LevelState::Begin))
|
||||
.or_else(in_state(AppState::InGame(LevelState::InGame))),
|
||||
.or(in_state(AppState::InGame(LevelState::InGame))),
|
||||
),
|
||||
bulge::add_bulge_system,
|
||||
bulge::propagate_bulge_system,
|
||||
@@ -107,7 +107,8 @@ struct SnakeBundle {
|
||||
snake: Snake,
|
||||
collision: Collision,
|
||||
segments: Segments,
|
||||
spatial_bundle: SpatialBundle,
|
||||
transform: Transform,
|
||||
visibility: Visibility,
|
||||
}
|
||||
|
||||
#[derive(Component, Default, Debug)]
|
||||
@@ -123,22 +124,21 @@ fn create_snake_segment(
|
||||
grid_position: grid::Coordinate,
|
||||
segment_number: u32,
|
||||
) -> Entity {
|
||||
let mut color = Color::RED;
|
||||
let mut color = Srgba::RED;
|
||||
color *= 0.99f32.powi(segment_number as _);
|
||||
|
||||
commands
|
||||
.spawn(SpriteBundle {
|
||||
sprite: Sprite {
|
||||
color,
|
||||
.spawn((
|
||||
Sprite {
|
||||
color: color.into(),
|
||||
custom_size: Some(Vec2::splat(grid::SEGMENT_SIZE) * 0.9),
|
||||
..Default::default()
|
||||
},
|
||||
transform: Transform::from_translation(Vec2::from(grid_position).extend(Z_HEIGHT)),
|
||||
..Default::default()
|
||||
})
|
||||
Transform::from_translation(Vec2::from(grid_position).extend(Z_HEIGHT)),
|
||||
))
|
||||
.insert(SnakeSegment)
|
||||
.insert(grid_position)
|
||||
.insert(crate::canvas::EmissiveTile { intensity: 1.0 })
|
||||
// .insert(crate::canvas::EmissiveTile { intensity: 1.0 })
|
||||
.id()
|
||||
}
|
||||
|
||||
@@ -156,15 +156,16 @@ fn setup_snake_system(mut commands: Commands) {
|
||||
snake: Snake,
|
||||
collision: Default::default(),
|
||||
segments: Segments(vec![snake_head]),
|
||||
spatial_bundle: Default::default(),
|
||||
transform: Default::default(),
|
||||
visibility: Default::default(),
|
||||
})
|
||||
.insert(Name::new("Snake"))
|
||||
.insert(InputManagerBundle::<Direction> {
|
||||
input_map: InputMap::new([
|
||||
(KeyCode::Up, Direction::Up),
|
||||
(KeyCode::Down, Direction::Down),
|
||||
(KeyCode::Left, Direction::Left),
|
||||
(KeyCode::Right, Direction::Right),
|
||||
(Direction::Up, KeyCode::ArrowUp),
|
||||
(Direction::Down, KeyCode::ArrowDown),
|
||||
(Direction::Left, KeyCode::ArrowLeft),
|
||||
(Direction::Right, KeyCode::ArrowRight),
|
||||
]),
|
||||
..Default::default()
|
||||
})
|
||||
@@ -175,9 +176,9 @@ fn eaten_event_system(
|
||||
mut eaten_event_reader: EventReader<fruit::EatenEvent>,
|
||||
mut tail_event_writer: EventWriter<AddTailEvent>,
|
||||
) {
|
||||
eaten_event_reader
|
||||
.iter()
|
||||
.for_each(|_| tail_event_writer.send(AddTailEvent));
|
||||
eaten_event_reader.read().for_each(|_| {
|
||||
tail_event_writer.send(AddTailEvent);
|
||||
});
|
||||
}
|
||||
|
||||
fn add_tail_system(
|
||||
@@ -185,7 +186,7 @@ fn add_tail_system(
|
||||
mut snake_query: Query<(Entity, &mut Segments), With<Snake>>,
|
||||
mut tail_event_reader: EventReader<AddTailEvent>,
|
||||
) {
|
||||
for _ in tail_event_reader.iter() {
|
||||
for _ in tail_event_reader.read() {
|
||||
let (snake_entity, mut segments) = snake_query.single_mut();
|
||||
|
||||
let segment = create_snake_segment(
|
||||
@@ -266,10 +267,10 @@ fn game_over_system(mut next_state: ResMut<NextState<AppState>>) {
|
||||
}
|
||||
|
||||
fn collision_sound_system(mut commands: Commands, audio_assets: Res<audio::Assets>) {
|
||||
commands.spawn(AudioBundle {
|
||||
source: audio_assets.collision.clone(),
|
||||
..Default::default()
|
||||
});
|
||||
commands.spawn((
|
||||
AudioPlayer::<AudioSource>(audio_assets.collision.clone()),
|
||||
PlaybackSettings::DESPAWN,
|
||||
));
|
||||
}
|
||||
|
||||
fn blip_sound_system(
|
||||
@@ -277,11 +278,11 @@ fn blip_sound_system(
|
||||
audio_assets: Res<audio::Assets>,
|
||||
mut eaten_event_reader: EventReader<fruit::EatenEvent>,
|
||||
) {
|
||||
for _ in eaten_event_reader.iter() {
|
||||
commands.spawn(AudioBundle {
|
||||
source: audio_assets.blip.clone(),
|
||||
..Default::default()
|
||||
});
|
||||
for _ in eaten_event_reader.read() {
|
||||
commands.spawn((
|
||||
AudioPlayer::<AudioSource>(audio_assets.blip.clone()),
|
||||
PlaybackSettings::DESPAWN,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -302,7 +303,7 @@ mod debug {
|
||||
use super::*;
|
||||
|
||||
pub(super) fn add_tail(
|
||||
keypress: Res<Input<KeyCode>>,
|
||||
keypress: Res<ButtonInput<KeyCode>>,
|
||||
mut tail_event_writer: EventWriter<AddTailEvent>,
|
||||
) {
|
||||
if keypress.just_pressed(KeyCode::Space) {
|
||||
|
||||
@@ -22,7 +22,7 @@ pub(super) fn add_bulge_system(
|
||||
mut tail_event_reader: EventReader<AddTailEvent>,
|
||||
query: Query<Entity, With<SnakeHead>>,
|
||||
) {
|
||||
for _ in tail_event_reader.iter() {
|
||||
for _ in tail_event_reader.read() {
|
||||
let snake_head_entity = query.single();
|
||||
commands.entity(snake_head_entity).insert(BulgeMarker);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ use crate::{audio, level::LevelState, AppState};
|
||||
use bevy::prelude::*;
|
||||
use leafwing_input_manager::prelude::*;
|
||||
|
||||
#[derive(Actionlike, Reflect, Component, Copy, Clone, Debug, PartialEq, Eq)]
|
||||
#[derive(Actionlike, Reflect, Component, Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub(super) enum Direction {
|
||||
Up,
|
||||
Down,
|
||||
@@ -62,9 +62,10 @@ pub(super) fn change_direction_system(
|
||||
}
|
||||
|
||||
commands.entity(snake).insert(NextDirection(new_direction));
|
||||
commands.spawn(AudioBundle {
|
||||
source: audio_assets.tick.clone(),
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
commands.spawn((
|
||||
AudioPlayer::<AudioSource>(audio_assets.tick.clone()),
|
||||
PlaybackSettings::DESPAWN,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,9 @@ pub struct UiPlugin;
|
||||
impl Plugin for UiPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.add_systems(Startup, status::spawn_status_text)
|
||||
.add_systems(Update, status::update_status_text);
|
||||
.add_systems(
|
||||
Update,
|
||||
(status::update_level_text, status::update_score_text),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,61 +1,48 @@
|
||||
use crate::{Level, Score};
|
||||
use bevy::prelude::*;
|
||||
use crate::{level::Level, Score};
|
||||
use bevy::{color::palettes::css::GOLD, prelude::*};
|
||||
|
||||
#[derive(Component)]
|
||||
pub(super) struct StatusText;
|
||||
pub(super) struct LevelValue;
|
||||
|
||||
#[derive(Component)]
|
||||
pub(super) struct ScoreValue;
|
||||
|
||||
pub(super) fn spawn_status_text(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
let font = asset_server.load("fonts/Audiowide-Regular.ttf");
|
||||
|
||||
commands.spawn((
|
||||
StatusText,
|
||||
TextBundle::from_sections([
|
||||
TextSection::new(
|
||||
"Level: ",
|
||||
TextStyle {
|
||||
font: font.clone(),
|
||||
font_size: 32.0,
|
||||
color: Color::WHITE,
|
||||
},
|
||||
),
|
||||
TextSection::from_style(TextStyle {
|
||||
font: font.clone(),
|
||||
font_size: 32.0,
|
||||
color: Color::WHITE,
|
||||
}),
|
||||
TextSection::new(
|
||||
" Score: ",
|
||||
TextStyle {
|
||||
font: font.clone(),
|
||||
font_size: 32.0,
|
||||
color: Color::WHITE,
|
||||
},
|
||||
),
|
||||
TextSection::from_style(TextStyle {
|
||||
commands
|
||||
.spawn((
|
||||
Text::default(),
|
||||
TextFont {
|
||||
font,
|
||||
font_size: 32.0,
|
||||
color: Color::WHITE,
|
||||
}),
|
||||
])
|
||||
.with_style(Style {
|
||||
position_type: PositionType::Absolute,
|
||||
left: Val::Px(10.0),
|
||||
bottom: Val::Px(10.0),
|
||||
..Default::default()
|
||||
}),
|
||||
));
|
||||
..Default::default()
|
||||
},
|
||||
Node {
|
||||
position_type: PositionType::Absolute,
|
||||
left: Val::Px(10.0),
|
||||
bottom: Val::Px(10.0),
|
||||
..Default::default()
|
||||
},
|
||||
))
|
||||
.with_child((TextSpan::new("Level: "), TextColor::WHITE))
|
||||
.with_child((LevelValue, TextSpan::default(), TextColor(GOLD.into())))
|
||||
.with_child((TextSpan::new(" Score: "), TextColor::WHITE))
|
||||
.with_child((ScoreValue, TextSpan::default(), TextColor(GOLD.into())));
|
||||
}
|
||||
|
||||
pub(super) fn update_status_text(
|
||||
mut query: Query<&mut Text, With<StatusText>>,
|
||||
score: Res<Score>,
|
||||
pub(super) fn update_level_text(
|
||||
mut level_query: Query<&mut TextSpan, With<LevelValue>>,
|
||||
level: Res<Level>,
|
||||
) {
|
||||
let score = score.0;
|
||||
let level = level.0;
|
||||
|
||||
for mut text in query.iter_mut() {
|
||||
text.sections[1].value = format!("{level}");
|
||||
text.sections[3].value = format!("{score}");
|
||||
}
|
||||
level_query.single_mut().0 = format!("{level}");
|
||||
}
|
||||
|
||||
pub(super) fn update_score_text(
|
||||
mut score_query: Query<&mut TextSpan, With<ScoreValue>>,
|
||||
score: Res<Score>,
|
||||
) {
|
||||
let score = score.0;
|
||||
score_query.single_mut().0 = format!("{score}");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user