From 22147c818aafa46a0f4b8001a136e32bc96a208e Mon Sep 17 00:00:00 2001 From: tuxmain Date: Wed, 24 Aug 2022 12:21:08 +0200 Subject: [PATCH 1/8] Platform material & filter bundle --- src/filters.rs | 20 +++++++++++++++++ src/game.rs | 12 ++++++---- src/levels.rs | 3 +++ src/levels/level0.rs | 17 +++++++++++--- src/levels/level1.rs | 53 ++++++++++++++++++++++++++++++++++++++------ src/main.rs | 17 +++++++------- 6 files changed, 99 insertions(+), 23 deletions(-) create mode 100644 src/filters.rs diff --git a/src/filters.rs b/src/filters.rs new file mode 100644 index 0000000..a4439c4 --- /dev/null +++ b/src/filters.rs @@ -0,0 +1,20 @@ +use bevy::prelude::*; +use bevy_rapier2d::prelude::*; + +#[derive(Component, Default)] +pub struct FilterColor(pub Color); + +#[derive(Component)] +pub enum PassThroughFilter { + Absorbing, +} + +#[derive(Bundle)] +pub struct AbsorbingFilter { + pub color: FilterColor, + #[bundle] + pub mesh: ColorMesh2dBundle, + pub collider: Collider, + pub sensor: Sensor, + pub filter_type: PassThroughFilter, +} diff --git a/src/game.rs b/src/game.rs index ef051cb..0b0d139 100644 --- a/src/game.rs +++ b/src/game.rs @@ -1,6 +1,8 @@ #![allow(clippy::precedence)] #![allow(clippy::too_many_arguments)] +pub use crate::filters::*; + use crate::AppState; use bevy::{ @@ -322,14 +324,16 @@ fn move_camera( .iter() .find(|(character_id, _transform)| *character_id == selected_character_id) { - let (camera, mut camera_transform) = - camera_query.single_mut(); + let (camera, mut camera_transform) = camera_query.single_mut(); let size: Vec2 = camera.logical_viewport_size().unwrap(); let half_height: f32 = size.y * 0.5; - camera_transform.translation = camera_transform.translation.lerp(transform.translation, time.delta_seconds() * FOLLOW_SPEED); + camera_transform.translation = camera_transform + .translation + .lerp(transform.translation, time.delta_seconds() * FOLLOW_SPEED); // prevent camera from going too low - camera_transform.translation.y = camera_transform.translation.y.max(half_height-MARGIN); + camera_transform.translation.y = + camera_transform.translation.y.max(half_height - MARGIN); camera_transform.translation.z = 999.0; } } diff --git a/src/levels.rs b/src/levels.rs index b34c23f..76c35ea 100644 --- a/src/levels.rs +++ b/src/levels.rs @@ -39,6 +39,7 @@ pub fn despawn_level(mut commands: Commands, level_query: Query, + mut meshes: ResMut>, mut materials: ResMut>, current_level: Res, mut level_query: Query<(&mut SelectedCharacterId, &mut CharacterIdList)>, @@ -54,6 +55,7 @@ pub fn post_setup_level( match level_id.0 { 0 => level0::setup( &mut commands, + &mut meshes, &character_meshes, &mut materials, &mut selected_character_id, @@ -62,6 +64,7 @@ pub fn post_setup_level( ), 1 => level1::setup( &mut commands, + &mut meshes, &character_meshes, &mut materials, &mut selected_character_id, diff --git a/src/levels/level0.rs b/src/levels/level0.rs index e95849d..92e9618 100644 --- a/src/levels/level0.rs +++ b/src/levels/level0.rs @@ -1,10 +1,11 @@ use crate::game::*; -use bevy::prelude::*; +use bevy::prelude::{shape::Quad, *}; use bevy_rapier2d::prelude::*; pub fn setup( commands: &mut Commands, + meshes: &mut ResMut>, character_meshes: &Res, materials: &mut ResMut>, selected_character_id: &mut Mut, @@ -12,8 +13,18 @@ pub fn setup( audio: &Res>, ) { commands - .spawn_bundle(TransformBundle::from(Transform::from_xyz(0.0, -256.0, 0.0))) - .insert(Collider::cuboid(400., 10.)) + .spawn_bundle(ColorMesh2dBundle { + mesh: meshes + .add(Mesh::from(Quad { + size: Vec2 { x: 800.0, y: 16.0 }, + flip: false, + })) + .into(), + material: materials.add(ColorMaterial::from(Color::GRAY)), + transform: Transform::from_xyz(0.0, -256.0, 0.0), + ..default() + }) + .insert(Collider::cuboid(400., 8.)) .insert(Level); spawn_character( diff --git a/src/levels/level1.rs b/src/levels/level1.rs index 4c29b4c..5e86ff0 100644 --- a/src/levels/level1.rs +++ b/src/levels/level1.rs @@ -1,10 +1,11 @@ use crate::game::*; -use bevy::prelude::*; +use bevy::prelude::{shape::Quad, *}; use bevy_rapier2d::prelude::*; pub fn setup( commands: &mut Commands, + meshes: &mut ResMut>, character_meshes: &Res, materials: &mut ResMut>, selected_character_id: &mut Mut, @@ -12,14 +13,32 @@ pub fn setup( audio: &Res>, ) { commands - .spawn_bundle(TransformBundle::from(Transform::from_xyz(0.0, -256.0, 0.0))) - .insert(Collider::cuboid(400., 10.)) + .spawn_bundle(ColorMesh2dBundle { + mesh: meshes + .add(Mesh::from(Quad { + size: Vec2 { x: 800.0, y: 16.0 }, + flip: false, + })) + .into(), + material: materials.add(ColorMaterial::from(Color::GRAY)), + transform: Transform::from_xyz(0.0, -256.0, 0.0), + ..default() + }) + .insert(Collider::cuboid(400., 8.)) .insert(Level); commands - .spawn_bundle(TransformBundle::from(Transform::from_xyz( - 256.0, -128.0, 0.0, - ))) - .insert(Collider::cuboid(200., 10.)) + .spawn_bundle(ColorMesh2dBundle { + mesh: meshes + .add(Mesh::from(Quad { + size: Vec2 { x: 400.0, y: 16.0 }, + flip: false, + })) + .into(), + material: materials.add(ColorMaterial::from(Color::GRAY)), + transform: Transform::from_xyz(256.0, -128.0, 0.0), + ..default() + }) + .insert(Collider::cuboid(200., 8.)) .insert(Level); spawn_character( @@ -52,4 +71,24 @@ pub fn setup( Transform::from_xyz(0., -128., 0.), Color::GREEN, ); + + commands + .spawn_bundle(AbsorbingFilter { + color: FilterColor(Color::RED), + mesh: ColorMesh2dBundle { + mesh: meshes + .add(Mesh::from(Quad { + size: Vec2 { x: 128.0, y: 16.0 }, + flip: false, + })) + .into(), + material: materials.add(Color::rgba(1., 0., 0., 0.5).into()), + transform: Transform::from_xyz(0., 0., 2.), + ..Default::default() + }, + collider: Collider::cuboid(64., 8.), + sensor: Sensor, + filter_type: PassThroughFilter::Absorbing, + }) + .insert(Level); } diff --git a/src/main.rs b/src/main.rs index 7a19dab..b7fbbc2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,11 +1,12 @@ #[cfg(not(target_arch = "wasm32"))] mod audio; +mod filters; mod game; mod levels; mod menu; mod particle_effect; -use bevy::{core_pipeline::clear_color::ClearColorConfig, prelude::*}; +use bevy::prelude::*; use bevy_rapier2d::prelude::*; #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] @@ -23,15 +24,18 @@ fn main() { std::thread::spawn(move || audio::setup(audio_event_receiver)); App::new() + .insert_resource(Msaa { samples: 4 }) .insert_resource(audio_event_sender) .add_state(AppState::Menu) + .insert_resource(ClearColor(Color::BLACK)) .add_plugins(DefaultPlugins) .add_plugin(RapierPhysicsPlugin::::pixels_per_meter(64.0)) - .add_plugin(RapierDebugRenderPlugin::default()) + //.add_plugin(RapierDebugRenderPlugin::default()) .add_plugin(menu::MenuPlugin) .add_plugin(game::GamePlugin) .add_plugin(particle_effect::ParticleEffectPlugin) - .add_plugin(bevy_inspector_egui::WorldInspectorPlugin::new()) + //.add_plugin(bevy_inspector_egui::WorldInspectorPlugin::new()) + .add_system(bevy::window::close_on_esc) .add_startup_system(setup) .run(); } @@ -43,12 +47,7 @@ fn setup(mut commands: Commands, asset_server: Res) { let font: Handle = asset_server.load("UacariLegacy-Thin.ttf"); commands.insert_resource(font); - commands.spawn_bundle(Camera2dBundle { - camera_2d: Camera2d { - clear_color: ClearColorConfig::Custom(Color::BLACK), - }, - ..Default::default() - }); + commands.spawn_bundle(Camera2dBundle::default()); commands.insert_resource(AmbientLight { color: Color::WHITE, brightness: 0.6, From e86ca82bc36e9ccd830c163a9cc3a3e423ea72b4 Mon Sep 17 00:00:00 2001 From: Nixon Date: Wed, 24 Aug 2022 19:26:25 +0800 Subject: [PATCH 2/8] complete switch from character id to Player component as 'tag' --- src/game.rs | 243 ++++++++++++++++++++++--------------------- src/levels.rs | 42 +++----- src/levels/level0.rs | 13 +-- src/levels/level1.rs | 11 +- 4 files changed, 149 insertions(+), 160 deletions(-) diff --git a/src/game.rs b/src/game.rs index ef051cb..8e9f5da 100644 --- a/src/game.rs +++ b/src/game.rs @@ -6,7 +6,7 @@ use crate::AppState; use bevy::{ input::{keyboard::KeyCode, Input}, prelude::{shape::Quad, *}, - sprite::Mesh2dHandle, + sprite::Mesh2dHandle, ecs::system::EntityCommands, }; use bevy_rapier2d::prelude::*; use std::collections::BTreeSet; @@ -34,13 +34,14 @@ impl Plugin for GamePlugin { .add_system_set( SystemSet::on_update(AppState::Game) .with_system(crate::levels::post_setup_level) - .with_system(keyboard_input_system) + .with_system(player_movement_system) .with_system(move_camera) .with_system(character_particle_effect_system), ) .add_system_set( SystemSet::on_update(AppState::Win) - .with_system(keyboard_input_system) + .with_system(change_character_system) + .with_system(player_movement_system) .with_system(move_camera) .with_system(character_particle_effect_system), ) @@ -79,18 +80,12 @@ impl FromWorld for CharacterMeshes { #[derive(Component)] pub struct Level; -#[derive(Clone, Component, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct CharacterId(pub u32); - -#[derive(Clone, Component, Copy, Eq, Hash, PartialEq)] -pub struct SelectedCharacterId(pub Option); - -#[derive(Component)] -pub struct CharacterIdList(pub BTreeSet); - #[derive(Clone, Component, PartialEq)] pub struct CharacterColor(pub Color); +#[derive(Component)] +pub struct Player; + // Systems fn setup( @@ -109,36 +104,60 @@ fn setup( ); } +pub fn spawn_characters( + commands: &mut Commands, + character_meshes: &Res, + materials: &mut ResMut>, + audio: &Res>, + + transforms: &mut Vec, + colors: &Vec, +) { + spawn_character( + commands, + character_meshes, + materials, + audio, + transforms[0], + colors[0], + true, + ); + + for c in 1 .. transforms.len() { + spawn_character( + commands, + character_meshes, + materials, + audio, + transforms[c], + colors[c], + false, + ); + } +} + pub fn spawn_character( commands: &mut Commands, character_meshes: &Res, materials: &mut ResMut>, - selected_character_id: &mut Mut, - character_id_list: &mut Mut, audio: &Res>, mut transform: Transform, color: Color, + is_player: bool, ) { transform.translation.z = transform.translation.z.max(1.0); - let character_id = CharacterId( - character_id_list - .0 - .iter() - .last() - .map_or(0, |last_character_id| last_character_id.0 + 1), - ); - character_id_list.0.insert(character_id); + let color_mesh_2d_bundle: ColorMesh2dBundle = ColorMesh2dBundle { + mesh: character_meshes.square.clone(), + material: materials.add(ColorMaterial::from(color)), + transform, + ..default() + }; - commands - .spawn_bundle(ColorMesh2dBundle { - mesh: character_meshes.square.clone(), - material: materials.add(ColorMaterial::from(color)), - transform, - ..default() - }) + let mut entity_commands: EntityCommands = commands.spawn_bundle(color_mesh_2d_bundle); + + entity_commands .insert(Level) - .insert(character_id) .insert(CharacterColor(color)) .insert(RigidBody::Dynamic) .insert(Collider::cuboid(32., 32.)) @@ -154,9 +173,8 @@ pub fn spawn_character( .insert(ExternalImpulse::default()) .insert(ActiveEvents::COLLISION_EVENTS); - // If no character is selected, then select this one - if selected_character_id.0.is_none() { - selected_character_id.0 = Some(character_id); + if is_player { + entity_commands.insert(Player); audio .send(AudioMsg::Color([color.r(), color.g(), color.b()])) .ok(); @@ -168,23 +186,16 @@ fn collision_event_system( character_meshes: Res, mut materials: ResMut>, mut collision_events: EventReader, - character_query: Query<(&CharacterId, &CharacterColor, &Transform)>, - mut level_query: Query<(&mut SelectedCharacterId, &mut CharacterIdList)>, + character_query: Query<(&CharacterColor, &Transform)>, mut app_state: ResMut>, audio: Res>, ) { for collision_event in collision_events.iter() { if let CollisionEvent::Started(e1, e2, flags) = collision_event { if flags.is_empty() { - if let (Ok((c1_id, c1_color, c1_transform)), Ok((c2_id, c2_color, _c2_transform))) = + if let (Ok((c1_color, c1_transform)), Ok((c2_color, _c2_transform))) = (character_query.get(*e1), character_query.get(*e2)) { - let (mut selected_character_id, mut character_id_list) = - level_query.single_mut(); - character_id_list.0.remove(c1_id); - character_id_list.0.remove(c2_id); - selected_character_id.0 = None; - // TODO completely remove particles commands.entity(*e1).despawn_recursive(); commands.entity(*e2).despawn_recursive(); @@ -203,11 +214,10 @@ fn collision_event_system( &mut commands, &character_meshes, &mut materials, - &mut selected_character_id, - &mut character_id_list, &audio, *c1_transform, new_color.into(), + true, ); } } @@ -215,56 +225,67 @@ fn collision_event_system( } } -fn keyboard_input_system( +fn change_character_system( + mut commands: Commands, + keyboard_input: Res>, - mut characters: Query<(&CharacterId, &mut Velocity, &CharacterColor)>, - mut level_query: Query<(&mut SelectedCharacterId, &CharacterIdList)>, + characters: Query<(Entity, &Transform, &CharacterColor, Option<&Player>)>, + audio: Res>, +) { + if !keyboard_input.just_pressed(KeyCode::Tab) { return; } + + let mut player_idx: usize = 0; + let mut player_count: usize = 0; + + // find player idx + for (_entity, _transform, _color, player) in characters.iter() { + if let Some(_player) = player { + player_idx = player_count; + } + player_count += 1; + } + + // calculate next player index + let next_player_idx = (player_idx + 1) % player_count; + player_count = 0; + + // exchange `Player` component from old `player_idx` to new `next_player_idx` + for (entity, _transform, color, _player) in characters.iter() { + if player_count == player_idx { + commands.entity(entity).remove::(); + } + + if player_count == next_player_idx { + commands.entity(entity).insert(Player); + audio + .send(AudioMsg::Color([color.0.r(), color.0.g(), color.0.b()])) + .ok(); + } + + player_count += 1; + } +} + +fn player_movement_system( + keyboard_input: Res>, + mut characters: Query<&mut Velocity, With>, mut app_state: ResMut>, audio: Res>, ) { - if let Ok((mut selected_character_id, character_id_list)) = level_query.get_single_mut() { - if keyboard_input.just_pressed(KeyCode::Tab) { - let selected = if let Some(selected_character_id) = &mut selected_character_id.0 { - *selected_character_id = *character_id_list - .0 - .range(*selected_character_id..) - .nth(1) - .unwrap_or_else(|| character_id_list.0.iter().next().unwrap()); - *selected_character_id - } else { - selected_character_id.0 = Some(CharacterId(0)); - CharacterId(0) - }; - if let Some((_character_id, _velocity, color)) = characters - .iter_mut() - .find(|(character_id, _velocity, _color)| **character_id == selected) - { - audio - .send(AudioMsg::Color([color.0.r(), color.0.g(), color.0.b()])) - .ok(); - } - } + let right_pressed: bool = + keyboard_input.pressed(KeyCode::Right) || keyboard_input.pressed(KeyCode::D); + let left_pressed: bool = + keyboard_input.pressed(KeyCode::Left) || keyboard_input.pressed(KeyCode::A); - let right_pressed: bool = - keyboard_input.pressed(KeyCode::Right) || keyboard_input.pressed(KeyCode::D); - let left_pressed: bool = - keyboard_input.pressed(KeyCode::Left) || keyboard_input.pressed(KeyCode::A); + for mut velocity in characters.iter_mut() { + velocity.linvel.x = 200. * (right_pressed as i8 - left_pressed as i8) as f32; - if let Some(selected_character_id) = &selected_character_id.0 { - if let Some((_character_id, mut velocity, _color)) = characters - .iter_mut() - .find(|(character_id, _velocity, _color)| *character_id == selected_character_id) - { - velocity.linvel.x = 200. * (right_pressed as i8 - left_pressed as i8) as f32; - - if keyboard_input.just_pressed(KeyCode::Space) { - audio.send(AudioMsg::Jump).ok(); - velocity.linvel.y = 500.; - } - } - } - } + if keyboard_input.just_pressed(KeyCode::Space) { + audio.send(AudioMsg::Jump).ok(); + velocity.linvel.y = 500.; + } + } if app_state.current() == &AppState::Win && keyboard_input.just_pressed(KeyCode::Return) { app_state.replace(AppState::Game).unwrap(); @@ -272,20 +293,12 @@ fn keyboard_input_system( } fn character_particle_effect_system( - mut characters: Query<(&CharacterId, &Transform, &CharacterColor)>, + characters: Query<(&Transform, &CharacterColor), With>, mut particle_effect: ResMut, - mut level_query: Query<&SelectedCharacterId>, ) { - if let Ok(selected_character_id) = level_query.get_single_mut() { - if let Some(selected_character_id) = &selected_character_id.0 { - if let Some((_character_id, transform, color)) = characters - .iter_mut() - .find(|(character_id, _transform, _color)| *character_id == selected_character_id) - { - particle_effect.translation = transform.translation; - particle_effect.color = color.0; - } - } + for (transform, color) in characters.iter() { + particle_effect.translation = transform.translation; + particle_effect.color = color.0; } } @@ -309,29 +322,25 @@ fn win_setup(mut commands: Commands, asset_server: Res) { fn move_camera( mut camera_query: Query<(&Camera, &mut Transform)>, - characters: Query<(&CharacterId, &Transform), Without>, - level_query: Query<&SelectedCharacterId>, + characters: Query<&Transform, (Without, With)>, time: Res