From 09138229ca9554b2e86586368ace1182ad610a84 Mon Sep 17 00:00:00 2001 From: tuxmain Date: Fri, 26 Aug 2022 00:33:44 +0200 Subject: [PATCH 01/37] Editor: first steps --- Cargo.lock | 24 +++++- Cargo.toml | 3 +- README.md | 6 ++ build-wasm.sh | 5 +- src/editor.rs | 174 +++++++++++++++++++++++++++++++++++++++++ src/levels.rs | 114 ++++++++++++++------------- src/main.rs | 92 +++++++++++++++++----- src/particle_effect.rs | 1 - 8 files changed, 339 insertions(+), 80 deletions(-) create mode 100644 src/editor.rs diff --git a/Cargo.lock b/Cargo.lock index f26df36..8c52cf9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -588,6 +588,25 @@ dependencies = [ "glam", ] +[[package]] +name = "bevy_mod_picking" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19f97d740fcd9d089a768399e902e741f45f8d671e756c939d2f1ce8ad14d63a" +dependencies = [ + "bevy", + "bevy_mod_raycast", +] + +[[package]] +name = "bevy_mod_raycast" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7aead49a20f5e694f4fb59c7312f9a1813b65a2a0ac2c385d53d40f25cae896f" +dependencies = [ + "bevy", +] + [[package]] name = "bevy_pbr" version = "0.8.1" @@ -911,6 +930,7 @@ dependencies = [ "bevy", "bevy-inspector-egui", "bevy_common_assets", + "bevy_mod_picking", "bevy_rapier2d", "cpal 0.14.0", "crossbeam-channel", @@ -1562,9 +1582,9 @@ dependencies = [ [[package]] name = "erased-serde" -version = "0.3.22" +version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "003000e712ad0f95857bd4d2ef8d1890069e06554101697d12050668b2f6f020" +checksum = "54558e0ba96fbe24280072642eceb9d7d442e32c7ec0ea9e7ecd7b4ea2cf4e11" dependencies = [ "serde", ] diff --git a/Cargo.toml b/Cargo.toml index df12e56..9b47967 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,6 @@ edition = "2021" [dependencies] bevy = "0.8.1" bevy_common_assets = { version = "0.3.0", features = ["json"] } -bevy-inspector-egui = "0.12.1" bevy_rapier2d = "0.16.2" crossbeam-channel = "0.5.6" rand = "0.8.5" @@ -17,6 +16,8 @@ rapier2d = "0.14.0" serde = { version = "1.0.144", features = ["derive"] } [target."cfg(not(target_arch = \"wasm32\"))".dependencies] +bevy-inspector-egui = "0.12.1" +bevy_mod_picking = "0.8.2" cpal = "0.14.0" hexodsp = { git = "https://github.com/WeirdConstructor/HexoDSP", default-features = false } ticktock = "0.8.0" diff --git a/README.md b/README.md index 17112e6..70716d5 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,12 @@ This game uses [HexoDSP](https://github.com/WeirdConstructor/HexoDSP) for audio The synthetizer matrix can be edited using [HexoSynth](https://github.com/WeirdConstructor/HexoSynth) visual editor. +## Develop + +Skip to level `N: u32` with the command `bevyjam `. + +Edit the level `N: u32` with the command `bevyjam e`. + ## License GNU AGPL v3, CopyLeft 2022 Pascal Engélibert, Nixon Cheng diff --git a/build-wasm.sh b/build-wasm.sh index b9643dc..0a2d039 100644 --- a/build-wasm.sh +++ b/build-wasm.sh @@ -1,2 +1,3 @@ -cargo build --release --target wasm32-unknown-unknown -wasm-bindgen --out-name bevyjam --out-dir target --target web target/wasm32-unknown-unknown/release/bevyjam.wasm +cargo build --release --target wasm32-unknown-unknown || exit 1 +echo "==> wasm-bindgen..." +wasm-bindgen --out-name bevyjam --out-dir target --target web target/wasm32-unknown-unknown/release/bevyjam.wasm || exit 1 diff --git a/src/editor.rs b/src/editor.rs new file mode 100644 index 0000000..9d1749a --- /dev/null +++ b/src/editor.rs @@ -0,0 +1,174 @@ +use crate::{levels::stored::*, AppState}; + +use bevy::{ + input::{keyboard::KeyCode, Input}, + prelude::{ + shape::{Circle, Quad}, + *, + }, +}; +use bevy_mod_picking::*; + +pub struct EditorPlugin; + +impl Plugin for EditorPlugin { + fn build(&self, app: &mut App) { + app.add_plugins(DefaultPickingPlugins) + .add_system_set(SystemSet::on_enter(AppState::Editor).with_system(setup)) + .add_system_set( + SystemSet::on_update(AppState::Editor).with_system(keyboard_input_system), + ); + } +} + +#[derive(Component)] +struct Platform; + +#[derive(Component)] +struct Draggable; + +#[derive(Component)] +struct End; + +#[derive(Bundle)] +struct PlatformBundle { + #[bundle] + mesh: ColorMesh2dBundle, + platform: Platform, +} + +#[derive(Bundle)] +struct PlatformEndBundle { + #[bundle] + mesh: ColorMesh2dBundle, + #[bundle] + pickable: PickableBundle, + draggable: Draggable, + end: End, +} + +fn spawn_platform( + commands: &mut Commands, + meshes: &mut ResMut>, + materials: &mut ResMut>, + + transform: Transform, + size: Vec2, +) { + commands + .spawn_bundle(PlatformBundle { + mesh: ColorMesh2dBundle { + mesh: meshes.add(Mesh::from(Quad { size, flip: false })).into(), + material: materials.add(ColorMaterial::from(Color::GRAY)), + transform, + ..default() + }, + platform: Platform, + }) + .with_children(|c| { + c.spawn_bundle(PlatformEndBundle { + mesh: ColorMesh2dBundle { + mesh: meshes + .add(Mesh::from(Circle { + radius: 8., + vertices: 12, + })) + .into(), + material: materials.add(ColorMaterial::from(Color::rgba(1., 1., 0., 0.7))), + transform: Transform::from_xyz(-size.x / 2., -size.y / 2., 0.5), + ..default() + }, + pickable: PickableBundle::default(), + draggable: Draggable, + end: End, + }); + c.spawn_bundle(PlatformEndBundle { + mesh: ColorMesh2dBundle { + mesh: meshes + .add(Mesh::from(Circle { + radius: 8., + vertices: 12, + })) + .into(), + material: materials.add(ColorMaterial::from(Color::rgba(1., 1., 0., 0.7))), + transform: Transform::from_xyz(size.x / 2., size.y / 2., 0.5), + ..default() + }, + pickable: PickableBundle::default(), + draggable: Draggable, + end: End, + }); + }); +} + +pub fn spawn_stored_level( + commands: &mut Commands, + meshes: &mut ResMut>, + materials: &mut ResMut>, + asset_server: &Res, + + stored_level: &StoredLevel, +) { + let _font = asset_server.get_handle::("UacariLegacy-Thin.ttf"); + + for platform in stored_level.platforms.iter() { + spawn_platform( + commands, + meshes, + materials, + Transform::from_xyz(platform.pos.x, platform.pos.y, 0.), + platform.size, + ); + } +} + +fn setup( + mut commands: Commands, + mut meshes: ResMut>, + mut materials: ResMut>, + camera_query: Query>, + level_id: Res, + stored_levels_assets: Res>, + stored_levels_handle: Res>, + asset_server: Res, +) { + commands + .entity(camera_query.single()) + .insert_bundle(PickingCameraBundle::default()); + + if let Some(stored_level) = stored_levels_assets + .get(&stored_levels_handle) + .unwrap() + .levels + .get(level_id.0 .0 as usize) + { + spawn_stored_level( + &mut commands, + &mut meshes, + &mut materials, + &asset_server, + stored_level, + ); + } +} + +fn keyboard_input_system( + keyboard_input: Res>, + mut drag_query: Query<(&mut Transform, &Selection), With>, +) { + let drag = 8. + * Vec3 { + x: (keyboard_input.pressed(KeyCode::Right) as i8 + - keyboard_input.pressed(KeyCode::Left) as i8) as _, + y: (keyboard_input.pressed(KeyCode::Up) as i8 + - keyboard_input.pressed(KeyCode::Down) as i8) as _, + z: 0., + }; + if drag != Vec3::ZERO { + for (mut transform, selection) in drag_query.iter_mut() { + if selection.selected() { + transform.translation += drag; + } + } + } +} diff --git a/src/levels.rs b/src/levels.rs index c131e63..f99b429 100644 --- a/src/levels.rs +++ b/src/levels.rs @@ -1,5 +1,7 @@ #![allow(clippy::too_many_arguments)] +pub use stored::*; + use crate::game::*; use bevy::{prelude::*, reflect::TypeUuid}; @@ -55,60 +57,6 @@ pub fn post_setup_level( } } -#[derive(Deserialize, Serialize, TypeUuid)] -#[uuid = "1fbba930-644b-0d62-2514-4b302b945327"] -pub struct StoredLevels { - levels: Vec, -} - -#[derive(Deserialize, Serialize, TypeUuid)] -#[uuid = "a1464a30-1f57-a654-d56c-ded41032af0b"] -pub struct StoredLevel { - pub comment: String, - pub characters: Vec, - pub platforms: Vec, - pub absorbing_filters: Vec, - pub rotating_filters: Vec, - pub texts: Vec, -} - -#[derive(Deserialize, Serialize, TypeUuid)] -#[uuid = "1c798f8c-ef15-c528-693e-76becdef6b10"] -pub struct StoredCharacter { - pub pos: Vec2, - pub color: Vec4, -} - -#[derive(Deserialize, Serialize, TypeUuid)] -#[uuid = "31696095-59de-93be-b5e9-333c2afbc900"] -pub struct StoredPlatform { - pub pos: Vec2, - pub size: Vec2, -} - -#[derive(Deserialize, Serialize, TypeUuid)] -#[uuid = "bcad7fff-0605-c4e3-3cd4-42d5bbaad926"] -pub struct StoredAbsorbingFilter { - pub pos: Vec2, - pub size: Vec2, - pub color: Vec4, -} - -#[derive(Deserialize, Serialize, TypeUuid)] -#[uuid = "fa2843f2-6e34-601b-6c46-4827b0370b3f"] -pub struct StoredRotatingFilter { - pub pos: Vec2, - pub angle: f32, -} - -#[derive(Deserialize, Serialize, TypeUuid)] -#[uuid = "72f6321a-f01f-6eea-9b17-3159837a2fd3"] -pub struct StoredText { - pub pos: Vec2, - pub font_size: f32, - pub text: String, -} - pub fn spawn_stored_level( commands: &mut Commands, character_meshes: &Res, @@ -179,3 +127,61 @@ pub fn spawn_stored_level( .insert(Level); } } + +pub mod stored { + use super::*; + + #[derive(Deserialize, Serialize, TypeUuid)] + #[uuid = "1fbba930-644b-0d62-2514-4b302b945327"] + pub struct StoredLevels { + pub levels: Vec, + } + + #[derive(Deserialize, Serialize, TypeUuid)] + #[uuid = "a1464a30-1f57-a654-d56c-ded41032af0b"] + pub struct StoredLevel { + pub comment: String, + pub characters: Vec, + pub platforms: Vec, + pub absorbing_filters: Vec, + pub rotating_filters: Vec, + pub texts: Vec, + } + + #[derive(Deserialize, Serialize, TypeUuid)] + #[uuid = "1c798f8c-ef15-c528-693e-76becdef6b10"] + pub struct StoredCharacter { + pub pos: Vec2, + pub color: Vec4, + } + + #[derive(Deserialize, Serialize, TypeUuid)] + #[uuid = "31696095-59de-93be-b5e9-333c2afbc900"] + pub struct StoredPlatform { + pub pos: Vec2, + pub size: Vec2, + } + + #[derive(Deserialize, Serialize, TypeUuid)] + #[uuid = "bcad7fff-0605-c4e3-3cd4-42d5bbaad926"] + pub struct StoredAbsorbingFilter { + pub pos: Vec2, + pub size: Vec2, + pub color: Vec4, + } + + #[derive(Deserialize, Serialize, TypeUuid)] + #[uuid = "fa2843f2-6e34-601b-6c46-4827b0370b3f"] + pub struct StoredRotatingFilter { + pub pos: Vec2, + pub angle: f32, + } + + #[derive(Deserialize, Serialize, TypeUuid)] + #[uuid = "72f6321a-f01f-6eea-9b17-3159837a2fd3"] + pub struct StoredText { + pub pos: Vec2, + pub font_size: f32, + pub text: String, + } +} diff --git a/src/main.rs b/src/main.rs index 410492a..7976caa 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,9 @@ +#![allow(clippy::too_many_arguments)] + #[cfg(not(target_arch = "wasm32"))] mod audio; +#[cfg(not(target_arch = "wasm32"))] +mod editor; mod filters; mod game; mod levels; @@ -7,6 +11,7 @@ mod menu; mod particle_effect; use bevy::{ + asset::{Asset, HandleId, LoadState}, prelude::*, window::{WindowId, WindowMode}, }; @@ -15,9 +20,22 @@ use bevy_rapier2d::prelude::*; #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] enum AppState { + Loading, Menu, Game, Win, + Editor, +} + +struct UseEditor(bool); + +struct LoadingAssets(Vec); + +impl LoadingAssets { + fn add(&mut self, handle: Handle) -> Handle { + self.0.push(handle.id); + handle + } } fn main() { @@ -28,32 +46,45 @@ fn main() { std::thread::spawn(move || audio::setup(audio_event_receiver)); #[cfg(not(target_arch = "wasm32"))] - let first_level = game::LevelId( - std::env::args() - .nth(1) - .map_or(0, |s| s.parse().unwrap_or(0)), - ); + let (first_level, use_editor) = { + let mut args = std::env::args().skip(1); + ( + game::LevelId(args.next().map_or(0, |s| s.parse().unwrap_or(0))), + args.next().map_or(false, |s| s == "e"), + ) + }; #[cfg(target_arch = "wasm32")] - let first_level = game::LevelId(0); + let (first_level, use_editor) = (game::LevelId(0), false); - App::new() - .insert_resource(Msaa { samples: 4 }) + let mut app = App::new(); + app.insert_resource(Msaa { samples: 4 }) .insert_resource(audio_event_sender) - .add_state(AppState::Menu) + .insert_resource(UseEditor(use_editor)) + .add_state(AppState::Loading) .insert_resource(game::FirstLevel(first_level)) .insert_resource(ClearColor(Color::BLACK)) .add_plugins(DefaultPlugins) + //.add_plugin(RapierDebugRenderPlugin::default()) + //.add_plugin(bevy_inspector_egui::WorldInspectorPlugin::new()) .add_plugin(JsonAssetPlugin::::new(&[ "levels.json", - ])) - .add_plugin(RapierPhysicsPlugin::::pixels_per_meter(64.0)) - //.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_system(keyboard_util_system) + ])); + + if !use_editor { + app.add_plugin(RapierPhysicsPlugin::::pixels_per_meter(64.0)) + .add_plugin(menu::MenuPlugin) + .add_plugin(game::GamePlugin) + .add_plugin(particle_effect::ParticleEffectPlugin); + } + + #[cfg(not(target_arch = "wasm32"))] + if use_editor { + app.add_plugin(editor::EditorPlugin); + } + + app.add_system(keyboard_util_system) .add_startup_system(setup) + .add_system_set(SystemSet::on_update(AppState::Loading).with_system(loading_system)) .run(); } @@ -63,9 +94,13 @@ fn setup(mut commands: Commands, mut windows: ResMut, asset_server: Res .unwrap() .set_title(String::from("Bevyjam")); - commands.insert_resource(asset_server.load::("game.levels.json")); - commands.insert_resource(asset_server.load::("UacariLegacy-Thin.ttf")); - commands.insert_resource(asset_server.load::("bevy.png")); + let mut assets = LoadingAssets(Vec::new()); + commands.insert_resource( + assets.add(asset_server.load::("game.levels.json")), + ); + commands.insert_resource(assets.add(asset_server.load::("UacariLegacy-Thin.ttf"))); + commands.insert_resource(assets.add(asset_server.load::("bevy.png"))); + commands.insert_resource(assets); commands.spawn_bundle(Camera2dBundle::default()); commands.insert_resource(AmbientLight { @@ -74,6 +109,23 @@ fn setup(mut commands: Commands, mut windows: ResMut, asset_server: Res }); } +fn loading_system( + asset_server: Res, + use_editor: Res, + assets: Res, + mut app_state: ResMut>, +) { + if asset_server.get_group_load_state(assets.0.iter().copied()) == LoadState::Loaded { + app_state + .replace(if use_editor.0 { + AppState::Editor + } else { + AppState::Menu + }) + .ok(); + } +} + fn keyboard_util_system(keyboard_input: Res>, mut windows: ResMut) { #[cfg(not(target_arch = "wasm32"))] { diff --git a/src/particle_effect.rs b/src/particle_effect.rs index c1ee094..b711548 100644 --- a/src/particle_effect.rs +++ b/src/particle_effect.rs @@ -44,7 +44,6 @@ impl FromWorld for ParticleMesh { } } -#[derive(bevy_inspector_egui::Inspectable)] pub struct ParticleEffectResource { pub translation: Vec3, pub prev_translation: Vec3, From 02b29bd093dfefd4609099b602ce13346cf68f3b Mon Sep 17 00:00:00 2001 From: tuxmain Date: Fri, 26 Aug 2022 10:05:32 +0200 Subject: [PATCH 02/37] editor: move platforms --- src/editor.rs | 114 ++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 92 insertions(+), 22 deletions(-) diff --git a/src/editor.rs b/src/editor.rs index 9d1749a..08ebb43 100644 --- a/src/editor.rs +++ b/src/editor.rs @@ -1,3 +1,4 @@ +#![allow(clippy::type_complexity)] use crate::{levels::stored::*, AppState}; use bevy::{ @@ -6,6 +7,7 @@ use bevy::{ shape::{Circle, Quad}, *, }, + sprite::Mesh2dHandle, }; use bevy_mod_picking::*; @@ -13,28 +15,39 @@ pub struct EditorPlugin; impl Plugin for EditorPlugin { fn build(&self, app: &mut App) { - app.add_plugins(DefaultPickingPlugins) + app.add_event::() + .add_plugins(DefaultPickingPlugins) .add_system_set(SystemSet::on_enter(AppState::Editor).with_system(setup)) .add_system_set( - SystemSet::on_update(AppState::Editor).with_system(keyboard_input_system), + SystemSet::on_update(AppState::Editor) + .with_system(keyboard_input_system) + .with_system(follow_ends_system), ); } } +// Events + +struct DragEndEvent(Entity); + +// Components + #[derive(Component)] -struct Platform; +struct Platform(Entity, Entity); #[derive(Component)] struct Draggable; #[derive(Component)] -struct End; +struct End(Entity); + +// Bundles #[derive(Bundle)] struct PlatformBundle { #[bundle] mesh: ColorMesh2dBundle, - platform: Platform, + //platform: Platform, } #[derive(Bundle)] @@ -47,6 +60,8 @@ struct PlatformEndBundle { end: End, } +// Functions + fn spawn_platform( commands: &mut Commands, meshes: &mut ResMut>, @@ -55,7 +70,7 @@ fn spawn_platform( transform: Transform, size: Vec2, ) { - commands + let platform = commands .spawn_bundle(PlatformBundle { mesh: ColorMesh2dBundle { mesh: meshes.add(Mesh::from(Quad { size, flip: false })).into(), @@ -63,10 +78,11 @@ fn spawn_platform( transform, ..default() }, - platform: Platform, }) - .with_children(|c| { - c.spawn_bundle(PlatformEndBundle { + .id(); + let ends = Platform( + commands + .spawn_bundle(PlatformEndBundle { mesh: ColorMesh2dBundle { mesh: meshes .add(Mesh::from(Circle { @@ -75,14 +91,20 @@ fn spawn_platform( })) .into(), material: materials.add(ColorMaterial::from(Color::rgba(1., 1., 0., 0.7))), - transform: Transform::from_xyz(-size.x / 2., -size.y / 2., 0.5), + transform: Transform::from_xyz( + transform.translation.x - size.x / 2., + transform.translation.y - size.y / 2., + 0.5, + ), ..default() }, pickable: PickableBundle::default(), draggable: Draggable, - end: End, - }); - c.spawn_bundle(PlatformEndBundle { + end: End(platform), + }) + .id(), + commands + .spawn_bundle(PlatformEndBundle { mesh: ColorMesh2dBundle { mesh: meshes .add(Mesh::from(Circle { @@ -91,14 +113,20 @@ fn spawn_platform( })) .into(), material: materials.add(ColorMaterial::from(Color::rgba(1., 1., 0., 0.7))), - transform: Transform::from_xyz(size.x / 2., size.y / 2., 0.5), + transform: Transform::from_xyz( + transform.translation.x + size.x / 2., + transform.translation.y + size.y / 2., + 0.5, + ), ..default() }, pickable: PickableBundle::default(), draggable: Draggable, - end: End, - }); - }); + end: End(platform), + }) + .id(), + ); + commands.entity(platform).insert(ends); } pub fn spawn_stored_level( @@ -122,6 +150,8 @@ pub fn spawn_stored_level( } } +// Systems + fn setup( mut commands: Commands, mut meshes: ResMut>, @@ -154,20 +184,60 @@ fn setup( fn keyboard_input_system( keyboard_input: Res>, - mut drag_query: Query<(&mut Transform, &Selection), With>, + mut drag_query: Query<(&mut Transform, &Selection, Option<&End>), With>, + mut drag_end_event: EventWriter, ) { - let drag = 8. - * Vec3 { + let drag = if keyboard_input.pressed(KeyCode::LShift) || keyboard_input.pressed(KeyCode::RShift) + { + Vec3 { x: (keyboard_input.pressed(KeyCode::Right) as i8 - keyboard_input.pressed(KeyCode::Left) as i8) as _, y: (keyboard_input.pressed(KeyCode::Up) as i8 - keyboard_input.pressed(KeyCode::Down) as i8) as _, z: 0., - }; + } + } else { + Vec3 { + x: (keyboard_input.just_pressed(KeyCode::Right) as i8 + - keyboard_input.just_pressed(KeyCode::Left) as i8) as _, + y: (keyboard_input.just_pressed(KeyCode::Up) as i8 + - keyboard_input.just_pressed(KeyCode::Down) as i8) as _, + z: 0., + } + } * 8.; if drag != Vec3::ZERO { - for (mut transform, selection) in drag_query.iter_mut() { + for (mut transform, selection, end) in drag_query.iter_mut() { if selection.selected() { transform.translation += drag; + if let Some(End(entity)) = end { + drag_end_event.send(DragEndEvent(*entity)); + } + } + } + } +} + +fn follow_ends_system( + mut meshes: ResMut>, + mut platform_query: Query<(&mut Transform, &mut Mesh2dHandle, &Platform)>, + end_query: Query<&Transform, Without>, + mut drag_end_event: EventReader, +) { + for DragEndEvent(entity) in drag_end_event.iter() { + if let Ok((mut transform, mut mesh, Platform(end1, end2))) = platform_query.get_mut(*entity) + { + if let (Ok(end1), Ok(end2)) = (end_query.get(*end1), end_query.get(*end2)) { + transform.translation.x = (end1.translation.x + end2.translation.x) / 2.; + transform.translation.y = (end1.translation.y + end2.translation.y) / 2.; + *mesh = meshes + .add(Mesh::from(Quad { + size: Vec2 { + x: (end2.translation.x - end1.translation.x).abs(), + y: (end2.translation.y - end1.translation.y).abs(), + }, + flip: false, + })) + .into(); } } } From 5d2289d1c0eeab5610d10a7af35dbbb6fbb0474a Mon Sep 17 00:00:00 2001 From: tuxmain Date: Fri, 26 Aug 2022 10:16:40 +0200 Subject: [PATCH 03/37] editor: move camera --- README.md | 6 ++++++ src/editor.rs | 14 ++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/README.md b/README.md index 70716d5..c29d372 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,12 @@ Skip to level `N: u32` with the command `bevyjam `. Edit the level `N: u32` with the command `bevyjam e`. +### Editor controls + +* **Select handles**: left click to select, click in void to deselect, CTRL+click to select many, CTRL+A to select all +* **Move handles**: arrows to move one step, Shift+arrows to move continuously +* **Move camera**: CTRL+arrows + ## License GNU AGPL v3, CopyLeft 2022 Pascal Engélibert, Nixon Cheng diff --git a/src/editor.rs b/src/editor.rs index 08ebb43..5404140 100644 --- a/src/editor.rs +++ b/src/editor.rs @@ -184,9 +184,23 @@ fn setup( fn keyboard_input_system( keyboard_input: Res>, + mut camera_query: Query<&mut Transform, (With, Without)>, mut drag_query: Query<(&mut Transform, &Selection, Option<&End>), With>, mut drag_end_event: EventWriter, ) { + if keyboard_input.pressed(KeyCode::LControl) || keyboard_input.pressed(KeyCode::RControl) { + let mut transform = camera_query.single_mut(); + let drag = Vec3 { + x: (keyboard_input.pressed(KeyCode::Right) as i8 + - keyboard_input.pressed(KeyCode::Left) as i8) as _, + y: (keyboard_input.pressed(KeyCode::Up) as i8 + - keyboard_input.pressed(KeyCode::Down) as i8) as _, + z: 0., + } * 8.; + transform.translation += drag; + return; + } + let drag = if keyboard_input.pressed(KeyCode::LShift) || keyboard_input.pressed(KeyCode::RShift) { Vec3 { From da5f9b0820a15de142b8cd7106c69825a7c9807b Mon Sep 17 00:00:00 2001 From: tuxmain Date: Fri, 26 Aug 2022 12:00:36 +0200 Subject: [PATCH 04/37] editor: characters --- src/editor.rs | 74 +++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 69 insertions(+), 5 deletions(-) diff --git a/src/editor.rs b/src/editor.rs index 5404140..382b585 100644 --- a/src/editor.rs +++ b/src/editor.rs @@ -20,7 +20,7 @@ impl Plugin for EditorPlugin { .add_system_set(SystemSet::on_enter(AppState::Editor).with_system(setup)) .add_system_set( SystemSet::on_update(AppState::Editor) - .with_system(keyboard_input_system) + .with_system(move_system) .with_system(follow_ends_system), ); } @@ -30,6 +30,8 @@ impl Plugin for EditorPlugin { struct DragEndEvent(Entity); +// Resources + // Components #[derive(Component)] @@ -47,7 +49,6 @@ struct End(Entity); struct PlatformBundle { #[bundle] mesh: ColorMesh2dBundle, - //platform: Platform, } #[derive(Bundle)] @@ -60,6 +61,15 @@ struct PlatformEndBundle { end: End, } +#[derive(Bundle)] +struct CharacterBundle { + #[bundle] + mesh: ColorMesh2dBundle, + #[bundle] + pickable: PickableBundle, + draggable: Draggable, +} + // Functions fn spawn_platform( @@ -129,6 +139,50 @@ fn spawn_platform( commands.entity(platform).insert(ends); } +fn spawn_character( + commands: &mut Commands, + meshes: &mut ResMut>, + materials: &mut ResMut>, + asset_server: &Res, + + transform: Transform, + color: Color, + index: usize, +) { + let font = asset_server.get_handle("UacariLegacy-Thin.ttf"); + commands + .spawn_bundle(CharacterBundle { + mesh: ColorMesh2dBundle { + mesh: meshes + .add(Mesh::from(Quad { + size: Vec2 { x: 64., y: 64. }, + flip: false, + })) + .into(), + material: materials.add(ColorMaterial::from(color)), + transform, + ..default() + }, + pickable: PickableBundle::default(), + draggable: Draggable, + }) + .with_children(|c| { + c.spawn_bundle(Text2dBundle { + text: Text::from_section( + &index.to_string(), + TextStyle { + font: font.clone(), + font_size: 32., + color: Color::WHITE, + }, + ) + .with_alignment(TextAlignment::CENTER), + transform: Transform::from_xyz(0., 0., 1.), + ..Default::default() + }); + }); +} + pub fn spawn_stored_level( commands: &mut Commands, meshes: &mut ResMut>, @@ -137,8 +191,6 @@ pub fn spawn_stored_level( stored_level: &StoredLevel, ) { - let _font = asset_server.get_handle::("UacariLegacy-Thin.ttf"); - for platform in stored_level.platforms.iter() { spawn_platform( commands, @@ -148,6 +200,18 @@ pub fn spawn_stored_level( platform.size, ); } + + for (i, character) in stored_level.characters.iter().enumerate() { + spawn_character( + commands, + meshes, + materials, + asset_server, + Transform::from_xyz(character.pos.x, character.pos.y, 0.), + character.color.into(), + i, + ); + } } // Systems @@ -182,7 +246,7 @@ fn setup( } } -fn keyboard_input_system( +fn move_system( keyboard_input: Res>, mut camera_query: Query<&mut Transform, (With, Without)>, mut drag_query: Query<(&mut Transform, &Selection, Option<&End>), With>, From 7d2c68a0556114f304e66ba63c7cc7e42be95a15 Mon Sep 17 00:00:00 2001 From: tuxmain Date: Fri, 26 Aug 2022 14:57:53 +0200 Subject: [PATCH 05/37] editor: save --- Cargo.lock | 1 + Cargo.toml | 1 + README.md | 5 +- assets/game.levels.json | 461 ++++++++++++++++++++++++++++++---------- src/editor.rs | 123 +++++++++-- 5 files changed, 458 insertions(+), 133 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8c52cf9..91bb56d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -939,6 +939,7 @@ dependencies = [ "rand_distr", "rapier2d", "serde", + "serde_json", "ticktock", ] diff --git a/Cargo.toml b/Cargo.toml index 9b47967..22905a7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,7 @@ bevy-inspector-egui = "0.12.1" bevy_mod_picking = "0.8.2" cpal = "0.14.0" hexodsp = { git = "https://github.com/WeirdConstructor/HexoDSP", default-features = false } +serde_json = "1.0.85" ticktock = "0.8.0" [target."cfg(target_arch = \"wasm32\")".dependencies] diff --git a/README.md b/README.md index c29d372..c452b76 100644 --- a/README.md +++ b/README.md @@ -53,9 +53,10 @@ Edit the level `N: u32` with the command `bevyjam e`. ### Editor controls -* **Select handles**: left click to select, click in void to deselect, CTRL+click to select many, CTRL+A to select all -* **Move handles**: arrows to move one step, Shift+arrows to move continuously +* **Select**: left click to select, click in void to deselect, CTRL+click to select many, CTRL+A to select all +* **Move selection**: arrows to move one step, Shift+arrows to move continuously * **Move camera**: CTRL+arrows +* **Save**: CTRL+S ## License diff --git a/assets/game.levels.json b/assets/game.levels.json index 549c17b..bca10de 100644 --- a/assets/game.levels.json +++ b/assets/game.levels.json @@ -1,119 +1,346 @@ { - "levels": [ - { - "comment": "Movement tutorial", - "platforms": [ - {"pos": [0, -256], "size": [800, 16]} - ], - "characters": [ - {"pos": [0, -192], "color": [1,0,0,1]}, - {"pos": [-128, -192], "color": [0,1,0,1]}, - {"pos": [128, -192], "color": [0,0,1,1]} - ], - "absorbing_filters": [], - "rotating_filters": [], - "texts": [ - { - "pos": [0, 0], - "font_size": 32, - "text": "Combine the colors to synthetize a white light.\nUse arrows to move." - } - ] - }, - { - "comment": "Switch tutorial", - "platforms": [ - {"pos": [0, -256], "size": [800, 16]}, - {"pos": [128, 256], "size": [96, 16]} - ], - "characters": [ - {"pos": [0, -192], "color": [0,1,0,1]}, - {"pos": [-128, -192], "color": [1,0,0,1]}, - {"pos": [128, 320], "color": [0,0,1,1]} - ], - "absorbing_filters": [], - "rotating_filters": [], - "texts": [ - { - "pos": [0, 0], - "font_size": 32, - "text": "Press Tab to switch." - } - ] - }, - { - "comment": "Absorbing filter tutorial", - "platforms": [ - {"pos": [0, -256], "size": [800, 16]}, - {"pos": [0, -128], "size": [800, 16]} - ], - "characters": [ - {"pos": [-128, -192], "color": [1,0.64,0,1]}, - {"pos": [128, -192], "color": [0,0.37,1,1]} - ], - "absorbing_filters": [ - { - "pos": [0, -192], - "size": [16, 112], - "color": [1,0,0,1] - } - ], - "rotating_filters": [], - "texts": [ - { - "pos": [0, 0], - "font_size": 32, - "text": "Press R to reset." - } - ] - }, - { - "comment": "Rotating filter tutorial", - "platforms": [ - {"pos": [0, -256], "size": [800, 16]} - ], - "characters": [ - {"pos": [0, -192], "color": [1,0,0,1]}, - {"pos": [-128, -192], "color": [1,0,0,1]}, - {"pos": [128, -192], "color": [1,0,0,1]} - ], - "absorbing_filters": [], - "rotating_filters": [ - { - "pos": [0, -64], - "angle": 45 - } - ], - "texts": [ - { - "pos": [0, 0], - "font_size": 32, - "text": "Let's rotate the hue!" - } - ] - }, - { - "comment": "Game over", - "platforms": [ - {"pos": [0, -256], "size": [800, 16]} - ], - "characters": [ - {"pos": [0, -64], "color": [1,0,0,1]} - ], - "absorbing_filters": [], - "rotating_filters": [], - "texts": [ - { - "pos": [0, 128], - "font_size": 48, - "text": "Thank you for playing!" - }, - { - "pos": [0, 0], - "font_size": 32, - "text": "There is no more light to combine." - } - ] - } - ] + "levels": [ + { + "comment": "Movement tutorial", + "characters": [ + { + "pos": [ + 0.0, + -192.0 + ], + "color": [ + 1.0, + 0.0, + 0.0, + 1.0 + ] + }, + { + "pos": [ + -128.0, + -192.0 + ], + "color": [ + 0.0, + 1.0, + 0.0, + 1.0 + ] + }, + { + "pos": [ + 128.0, + -192.0 + ], + "color": [ + 0.0, + 0.0, + 1.0, + 1.0 + ] + } + ], + "platforms": [ + { + "pos": [ + 0.0, + -256.0 + ], + "size": [ + 800.0, + 16.0 + ] + } + ], + "absorbing_filters": [], + "rotating_filters": [], + "texts": [ + { + "pos": [ + 0.0, + 0.0 + ], + "font_size": 32.0, + "text": "Combine the colors to synthetize a white light.\nUse arrows to move." + } + ] + }, + { + "comment": "Switch tutorial", + "characters": [ + { + "pos": [ + 0.0, + -192.0 + ], + "color": [ + 0.0, + 1.0, + 0.0, + 1.0 + ] + }, + { + "pos": [ + -128.0, + -192.0 + ], + "color": [ + 1.0, + 0.0, + 0.0, + 1.0 + ] + }, + { + "pos": [ + 128.0, + 320.0 + ], + "color": [ + 0.0, + 0.0, + 1.0, + 1.0 + ] + } + ], + "platforms": [ + { + "pos": [ + 0.0, + -256.0 + ], + "size": [ + 800.0, + 16.0 + ] + }, + { + "pos": [ + 128.0, + 256.0 + ], + "size": [ + 96.0, + 16.0 + ] + } + ], + "absorbing_filters": [], + "rotating_filters": [], + "texts": [ + { + "pos": [ + 0.0, + 0.0 + ], + "font_size": 32.0, + "text": "Press Tab to switch." + } + ] + }, + { + "comment": "Absorbing filter tutorial", + "characters": [ + { + "pos": [ + -128.0, + -192.0 + ], + "color": [ + 1.0, + 0.64, + 0.0, + 1.0 + ] + }, + { + "pos": [ + 128.0, + -192.0 + ], + "color": [ + 0.0, + 0.37, + 1.0, + 1.0 + ] + } + ], + "platforms": [ + { + "pos": [ + 0.0, + -256.0 + ], + "size": [ + 800.0, + 16.0 + ] + }, + { + "pos": [ + 0.0, + -128.0 + ], + "size": [ + 800.0, + 16.0 + ] + } + ], + "absorbing_filters": [ + { + "pos": [ + 0.0, + -192.0 + ], + "size": [ + 16.0, + 112.0 + ], + "color": [ + 1.0, + 0.0, + 0.0, + 1.0 + ] + } + ], + "rotating_filters": [], + "texts": [ + { + "pos": [ + 0.0, + 0.0 + ], + "font_size": 32.0, + "text": "Press R to reset." + } + ] + }, + { + "comment": "Rotating filter tutorial", + "characters": [ + { + "pos": [ + 0.0, + -192.0 + ], + "color": [ + 1.0, + 0.0, + 0.0, + 1.0 + ] + }, + { + "pos": [ + -128.0, + -192.0 + ], + "color": [ + 1.0, + 0.0, + 0.0, + 1.0 + ] + }, + { + "pos": [ + 128.0, + -192.0 + ], + "color": [ + 1.0, + 0.0, + 0.0, + 1.0 + ] + } + ], + "platforms": [ + { + "pos": [ + 0.0, + -256.0 + ], + "size": [ + 800.0, + 16.0 + ] + } + ], + "absorbing_filters": [], + "rotating_filters": [ + { + "pos": [ + 0.0, + -64.0 + ], + "angle": 45.0 + } + ], + "texts": [ + { + "pos": [ + 0.0, + 0.0 + ], + "font_size": 32.0, + "text": "Let's rotate the hue!" + } + ] + }, + { + "comment": "Game over", + "characters": [ + { + "pos": [ + 0.0, + -64.0 + ], + "color": [ + 1.0, + 0.0, + 0.0, + 1.0 + ] + } + ], + "platforms": [ + { + "pos": [ + 0.0, + -256.0 + ], + "size": [ + 800.0, + 16.0 + ] + } + ], + "absorbing_filters": [], + "rotating_filters": [], + "texts": [ + { + "pos": [ + 0.0, + 128.0 + ], + "font_size": 48.0, + "text": "Thank you for playing!" + }, + { + "pos": [ + 0.0, + 0.0 + ], + "font_size": 32.0, + "text": "There is no more light to combine." + } + ] + } + ] } \ No newline at end of file diff --git a/src/editor.rs b/src/editor.rs index 382b585..91c24fe 100644 --- a/src/editor.rs +++ b/src/editor.rs @@ -21,6 +21,7 @@ impl Plugin for EditorPlugin { .add_system_set( SystemSet::on_update(AppState::Editor) .with_system(move_system) + .with_system(input_control_system) .with_system(follow_ends_system), ); } @@ -32,10 +33,15 @@ struct DragEndEvent(Entity); // Resources +struct CharacterList(Vec); + // Components #[derive(Component)] -struct Platform(Entity, Entity); +struct Platform; + +#[derive(Component)] +struct Ends(Entity, Entity); #[derive(Component)] struct Draggable; @@ -43,12 +49,20 @@ struct Draggable; #[derive(Component)] struct End(Entity); +#[derive(Component)] +struct CharacterColor(Color); + +#[derive(Component)] +struct Size(Vec2); + // Bundles #[derive(Bundle)] struct PlatformBundle { #[bundle] mesh: ColorMesh2dBundle, + size: Size, + platform: Platform, } #[derive(Bundle)] @@ -65,6 +79,7 @@ struct PlatformEndBundle { struct CharacterBundle { #[bundle] mesh: ColorMesh2dBundle, + color: CharacterColor, #[bundle] pickable: PickableBundle, draggable: Draggable, @@ -88,9 +103,11 @@ fn spawn_platform( transform, ..default() }, + size: Size(size), + platform: Platform, }) .id(); - let ends = Platform( + let ends = Ends( commands .spawn_bundle(PlatformEndBundle { mesh: ColorMesh2dBundle { @@ -148,7 +165,7 @@ fn spawn_character( transform: Transform, color: Color, index: usize, -) { +) -> Entity { let font = asset_server.get_handle("UacariLegacy-Thin.ttf"); commands .spawn_bundle(CharacterBundle { @@ -163,6 +180,7 @@ fn spawn_character( transform, ..default() }, + color: CharacterColor(color), pickable: PickableBundle::default(), draggable: Draggable, }) @@ -180,10 +198,11 @@ fn spawn_character( transform: Transform::from_xyz(0., 0., 1.), ..Default::default() }); - }); + }) + .id() } -pub fn spawn_stored_level( +fn spawn_stored_level( commands: &mut Commands, meshes: &mut ResMut>, materials: &mut ResMut>, @@ -201,8 +220,9 @@ pub fn spawn_stored_level( ); } + let mut character_list = Vec::new(); for (i, character) in stored_level.characters.iter().enumerate() { - spawn_character( + character_list.push(spawn_character( commands, meshes, materials, @@ -210,7 +230,57 @@ pub fn spawn_stored_level( Transform::from_xyz(character.pos.x, character.pos.y, 0.), character.color.into(), i, - ); + )); + } + commands.insert_resource(CharacterList(character_list)); +} + +fn save_level( + level_id: &Res, + stored_levels_assets: &mut ResMut>, + stored_levels_handle: &Res>, + character_list: &Res, + character_query: &Query<(&Transform, &CharacterColor), Without>, + platform_query: &Query<(&Transform, &Size), With>, +) { + let stored_levels = stored_levels_assets.get_mut(stored_levels_handle).unwrap(); + if let Some(stored_level) = stored_levels.levels.get_mut(level_id.0 .0 as usize) { + stored_level.platforms.clear(); + for (transform, size) in platform_query.iter() { + stored_level.platforms.push(StoredPlatform { + pos: Vec2 { + x: transform.translation.x, + y: transform.translation.y, + }, + size: size.0, + }) + } + + stored_level.characters.clear(); + for entity in character_list.0.iter() { + if let Ok((transform, color)) = character_query.get(*entity) { + stored_level.characters.push(StoredCharacter { + pos: Vec2 { + x: transform.translation.x, + y: transform.translation.y, + }, + color: color.0.as_rgba_f32().into(), + }) + } + } + } else { + return; + } + match std::fs::OpenOptions::new() + .write(true) + .truncate(true) + .open("assets/game.levels.json") + { + Ok(mut file) => { + serde_json::to_writer_pretty(&mut file, stored_levels).unwrap(); + println!("Saved!"); + } + Err(e) => eprintln!("Error writing levels file: {:?}", e), } } @@ -246,6 +316,29 @@ fn setup( } } +fn input_control_system( + keyboard_input: Res>, + level_id: Res, + mut stored_levels_assets: ResMut>, + stored_levels_handle: Res>, + character_list: Res, + character_query: Query<(&Transform, &CharacterColor), Without>, + platform_query: Query<(&Transform, &Size), With>, +) { + if keyboard_input.just_released(KeyCode::S) + && (keyboard_input.pressed(KeyCode::LControl) || keyboard_input.pressed(KeyCode::RControl)) + { + save_level( + &level_id, + &mut stored_levels_assets, + &stored_levels_handle, + &character_list, + &character_query, + &platform_query, + ); + } +} + fn move_system( keyboard_input: Res>, mut camera_query: Query<&mut Transform, (With, Without)>, @@ -297,22 +390,24 @@ fn move_system( fn follow_ends_system( mut meshes: ResMut>, - mut platform_query: Query<(&mut Transform, &mut Mesh2dHandle, &Platform)>, - end_query: Query<&Transform, Without>, + mut follower_query: Query<(&mut Transform, &mut Mesh2dHandle, &mut Size, &Ends)>, + end_query: Query<&Transform, Without>, mut drag_end_event: EventReader, ) { for DragEndEvent(entity) in drag_end_event.iter() { - if let Ok((mut transform, mut mesh, Platform(end1, end2))) = platform_query.get_mut(*entity) + if let Ok((mut transform, mut mesh, mut size, Ends(end1, end2))) = + follower_query.get_mut(*entity) { if let (Ok(end1), Ok(end2)) = (end_query.get(*end1), end_query.get(*end2)) { transform.translation.x = (end1.translation.x + end2.translation.x) / 2.; transform.translation.y = (end1.translation.y + end2.translation.y) / 2.; + size.0 = Vec2 { + x: (end2.translation.x - end1.translation.x).abs(), + y: (end2.translation.y - end1.translation.y).abs(), + }; *mesh = meshes .add(Mesh::from(Quad { - size: Vec2 { - x: (end2.translation.x - end1.translation.x).abs(), - y: (end2.translation.y - end1.translation.y).abs(), - }, + size: size.0, flip: false, })) .into(); From f20979f86dc3558f01ffdfe066ee46fa5b04effc Mon Sep 17 00:00:00 2001 From: Nixon Date: Fri, 26 Aug 2022 17:08:49 +0800 Subject: [PATCH 06/37] uses target translation for camera movement (prevent hard hit when moving beyond lower margin) --- Cargo.lock | 1 + Cargo.toml | 1 + src/game.rs | 8 +++++--- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 91bb56d..2396d32 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1333,6 +1333,7 @@ dependencies = [ "parking_lot 0.12.1", "stdweb", "thiserror", + "wasm-bindgen", "web-sys", "windows", ] diff --git a/Cargo.toml b/Cargo.toml index 22905a7..c0de684 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,7 @@ serde_json = "1.0.85" ticktock = "0.8.0" [target."cfg(target_arch = \"wasm32\")".dependencies] +cpal = { version = "0.14.0", features = ["wasm-bindgen"] } [profile.dev.package."*"] opt-level = 3 diff --git a/src/game.rs b/src/game.rs index eba3d44..102a44e 100644 --- a/src/game.rs +++ b/src/game.rs @@ -471,13 +471,15 @@ fn move_camera( let size: Vec2 = camera.logical_viewport_size().unwrap(); let half_height: f32 = size.y * 0.5; + let mut target_translation = character_transform.translation; + // prevent camera from going too low + target_translation.y = target_translation.y.max(half_height - MARGIN); + camera_transform.translation = camera_transform.translation.lerp( - character_transform.translation, + target_translation, time.delta_seconds() * FOLLOW_SPEED, ); - // prevent camera from going too low - camera_transform.translation.y = camera_transform.translation.y.max(half_height - MARGIN); // always make sure that camera is away from the object in order to render them camera_transform.translation.z = 999.0; } From 1e2ecd76c7dba8536aac78025b2f1cde30dec33c Mon Sep 17 00:00:00 2001 From: Nixon Date: Fri, 26 Aug 2022 18:27:08 +0800 Subject: [PATCH 07/37] directly host server after building with run-wasm.sh script --- run-wasm.sh | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 run-wasm.sh diff --git a/run-wasm.sh b/run-wasm.sh new file mode 100644 index 0000000..417b6b0 --- /dev/null +++ b/run-wasm.sh @@ -0,0 +1,2 @@ +source build-wasm.sh +python -m http.server \ No newline at end of file From 99084066234e94531c1919347b0a56fa4e772953 Mon Sep 17 00:00:00 2001 From: tuxmain Date: Fri, 26 Aug 2022 15:38:13 +0200 Subject: [PATCH 08/37] editor: filters --- Cargo.lock | 5 +- Cargo.toml | 3 +- src/editor.rs | 204 +++++++++++++++++++++++++++++++++++++++++++++++++- src/game.rs | 9 +-- 4 files changed, 207 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2396d32..2e34d56 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -590,9 +590,9 @@ dependencies = [ [[package]] name = "bevy_mod_picking" -version = "0.8.2" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19f97d740fcd9d089a768399e902e741f45f8d671e756c939d2f1ce8ad14d63a" +checksum = "db42ac84b1409452bbfa696d9071b9a7a2505c73620c939b758b5bf23573976a" dependencies = [ "bevy", "bevy_mod_raycast", @@ -1333,7 +1333,6 @@ dependencies = [ "parking_lot 0.12.1", "stdweb", "thiserror", - "wasm-bindgen", "web-sys", "windows", ] diff --git a/Cargo.toml b/Cargo.toml index c0de684..742804f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,14 +17,13 @@ serde = { version = "1.0.144", features = ["derive"] } [target."cfg(not(target_arch = \"wasm32\"))".dependencies] bevy-inspector-egui = "0.12.1" -bevy_mod_picking = "0.8.2" +bevy_mod_picking = "0.9.0" cpal = "0.14.0" hexodsp = { git = "https://github.com/WeirdConstructor/HexoDSP", default-features = false } serde_json = "1.0.85" ticktock = "0.8.0" [target."cfg(target_arch = \"wasm32\")".dependencies] -cpal = { version = "0.14.0", features = ["wasm-bindgen"] } [profile.dev.package."*"] opt-level = 3 diff --git a/src/editor.rs b/src/editor.rs index 91c24fe..55c1592 100644 --- a/src/editor.rs +++ b/src/editor.rs @@ -55,6 +55,12 @@ struct CharacterColor(Color); #[derive(Component)] struct Size(Vec2); +#[derive(Component)] +struct RotatingFilterAngle(f32); + +#[derive(Component)] +struct AbsorbingFilterColor(Color); + // Bundles #[derive(Bundle)] @@ -66,7 +72,7 @@ struct PlatformBundle { } #[derive(Bundle)] -struct PlatformEndBundle { +struct EndBundle { #[bundle] mesh: ColorMesh2dBundle, #[bundle] @@ -85,6 +91,24 @@ struct CharacterBundle { draggable: Draggable, } +#[derive(Bundle)] +struct AbsorbingFilterBundle { + #[bundle] + mesh: ColorMesh2dBundle, + size: Size, + color: AbsorbingFilterColor, +} + +#[derive(Bundle)] +struct RotatingFilterBundle { + #[bundle] + mesh: ColorMesh2dBundle, + angle: RotatingFilterAngle, + #[bundle] + pickable: PickableBundle, + draggable: Draggable, +} + // Functions fn spawn_platform( @@ -109,7 +133,7 @@ fn spawn_platform( .id(); let ends = Ends( commands - .spawn_bundle(PlatformEndBundle { + .spawn_bundle(EndBundle { mesh: ColorMesh2dBundle { mesh: meshes .add(Mesh::from(Circle { @@ -131,7 +155,7 @@ fn spawn_platform( }) .id(), commands - .spawn_bundle(PlatformEndBundle { + .spawn_bundle(EndBundle { mesh: ColorMesh2dBundle { mesh: meshes .add(Mesh::from(Circle { @@ -202,6 +226,121 @@ fn spawn_character( .id() } +fn spawn_absorbing_filter( + commands: &mut Commands, + meshes: &mut ResMut>, + materials: &mut ResMut>, + + transform: Transform, + size: Vec2, + color: Color, +) { + let absorbing_filter = commands + .spawn_bundle(AbsorbingFilterBundle { + mesh: ColorMesh2dBundle { + mesh: meshes.add(Mesh::from(Quad { size, flip: false })).into(), + material: materials.add(ColorMaterial::from(color)), + transform, + ..default() + }, + size: Size(size), + color: AbsorbingFilterColor(color), + }) + .id(); + let ends = Ends( + commands + .spawn_bundle(EndBundle { + mesh: ColorMesh2dBundle { + mesh: meshes + .add(Mesh::from(Circle { + radius: 8., + vertices: 12, + })) + .into(), + material: materials.add(ColorMaterial::from(Color::rgba(1., 1., 0., 0.7))), + transform: Transform::from_xyz( + transform.translation.x - size.x / 2., + transform.translation.y - size.y / 2., + 0.5, + ), + ..default() + }, + pickable: PickableBundle::default(), + draggable: Draggable, + end: End(absorbing_filter), + }) + .id(), + commands + .spawn_bundle(EndBundle { + mesh: ColorMesh2dBundle { + mesh: meshes + .add(Mesh::from(Circle { + radius: 8., + vertices: 12, + })) + .into(), + material: materials.add(ColorMaterial::from(Color::rgba(1., 1., 0., 0.7))), + transform: Transform::from_xyz( + transform.translation.x + size.x / 2., + transform.translation.y + size.y / 2., + 0.5, + ), + ..default() + }, + pickable: PickableBundle::default(), + draggable: Draggable, + end: End(absorbing_filter), + }) + .id(), + ); + commands.entity(absorbing_filter).insert(ends); +} + +fn spawn_rotating_filter( + commands: &mut Commands, + meshes: &mut ResMut>, + materials: &mut ResMut>, + asset_server: &Res, + + transform: Transform, + angle: f32, +) -> Entity { + let font = asset_server.get_handle("UacariLegacy-Thin.ttf"); + commands + .spawn_bundle(RotatingFilterBundle { + mesh: ColorMesh2dBundle { + mesh: meshes + .add(Mesh::from(Circle { + radius: 32., + vertices: 36, + })) + .into(), + material: materials.add(ColorMaterial::from(Color::WHITE)), + transform, + ..default() + }, + angle: RotatingFilterAngle(angle), + pickable: PickableBundle::default(), + draggable: Draggable, + }) + .with_children(|c| { + c.spawn_bundle(Text2dBundle { + text: Text::from_section( + &angle.to_string(), + TextStyle { + font: font.clone(), + font_size: 32., + color: Color::RED, + }, + ) + .with_alignment(TextAlignment::CENTER), + transform: Transform::from_xyz(0., 0., 1.), + ..Default::default() + }); + }) + .id() +} + fn spawn_stored_level( commands: &mut Commands, meshes: &mut ResMut>, @@ -233,6 +372,28 @@ fn spawn_stored_level( )); } commands.insert_resource(CharacterList(character_list)); + + for absorbing_filter in stored_level.absorbing_filters.iter() { + spawn_absorbing_filter( + commands, + meshes, + materials, + Transform::from_xyz(absorbing_filter.pos.x, absorbing_filter.pos.y, 0.), + absorbing_filter.size, + absorbing_filter.color.into(), + ); + } + + for rotating_filter in stored_level.rotating_filters.iter() { + spawn_rotating_filter( + commands, + meshes, + materials, + asset_server, + Transform::from_xyz(rotating_filter.pos.x, rotating_filter.pos.y, 0.), + rotating_filter.angle, + ); + } } fn save_level( @@ -242,6 +403,11 @@ fn save_level( character_list: &Res, character_query: &Query<(&Transform, &CharacterColor), Without>, platform_query: &Query<(&Transform, &Size), With>, + absorbing_filter_query: &Query<(&Transform, &Size, &AbsorbingFilterColor), Without>, + rotating_filter_query: &Query< + (&Transform, &RotatingFilterAngle), + (Without, Without), + >, ) { let stored_levels = stored_levels_assets.get_mut(stored_levels_handle).unwrap(); if let Some(stored_level) = stored_levels.levels.get_mut(level_id.0 .0 as usize) { @@ -264,10 +430,33 @@ fn save_level( x: transform.translation.x, y: transform.translation.y, }, - color: color.0.as_rgba_f32().into(), + color: color.0.into(), }) } } + + stored_level.absorbing_filters.clear(); + for (transform, size, color) in absorbing_filter_query.iter() { + stored_level.absorbing_filters.push(StoredAbsorbingFilter { + pos: Vec2 { + x: transform.translation.x, + y: transform.translation.y, + }, + size: size.0, + color: color.0.into(), + }) + } + + stored_level.rotating_filters.clear(); + for (transform, angle) in rotating_filter_query.iter() { + stored_level.rotating_filters.push(StoredRotatingFilter { + pos: Vec2 { + x: transform.translation.x, + y: transform.translation.y, + }, + angle: angle.0, + }) + } } else { return; } @@ -324,6 +513,11 @@ fn input_control_system( character_list: Res, character_query: Query<(&Transform, &CharacterColor), Without>, platform_query: Query<(&Transform, &Size), With>, + absorbing_filter_query: Query<(&Transform, &Size, &AbsorbingFilterColor), Without>, + rotating_filter_query: Query< + (&Transform, &RotatingFilterAngle), + (Without, Without), + >, ) { if keyboard_input.just_released(KeyCode::S) && (keyboard_input.pressed(KeyCode::LControl) || keyboard_input.pressed(KeyCode::RControl)) @@ -335,6 +529,8 @@ fn input_control_system( &character_list, &character_query, &platform_query, + &absorbing_filter_query, + &rotating_filter_query, ); } } diff --git a/src/game.rs b/src/game.rs index 102a44e..32f3bb0 100644 --- a/src/game.rs +++ b/src/game.rs @@ -471,14 +471,13 @@ fn move_camera( let size: Vec2 = camera.logical_viewport_size().unwrap(); let half_height: f32 = size.y * 0.5; - let mut target_translation = character_transform.translation; + let mut target_translation = character_transform.translation; // prevent camera from going too low target_translation.y = target_translation.y.max(half_height - MARGIN); - camera_transform.translation = camera_transform.translation.lerp( - target_translation, - time.delta_seconds() * FOLLOW_SPEED, - ); + camera_transform.translation = camera_transform + .translation + .lerp(target_translation, time.delta_seconds() * FOLLOW_SPEED); // always make sure that camera is away from the object in order to render them camera_transform.translation.z = 999.0; From 139e75786b69bbb246a687c9e743955cdc22b780 Mon Sep 17 00:00:00 2001 From: tuxmain Date: Fri, 26 Aug 2022 15:57:32 +0200 Subject: [PATCH 09/37] editor: delete --- README.md | 1 + src/editor.rs | 38 +++++++++++++++++++++++++++++++++++++- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c452b76..1bee3c3 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,7 @@ Edit the level `N: u32` with the command `bevyjam e`. * **Select**: left click to select, click in void to deselect, CTRL+click to select many, CTRL+A to select all * **Move selection**: arrows to move one step, Shift+arrows to move continuously +* **Delete selection**: delete * **Move camera**: CTRL+arrows * **Save**: CTRL+S diff --git a/src/editor.rs b/src/editor.rs index 55c1592..8474c1d 100644 --- a/src/editor.rs +++ b/src/editor.rs @@ -22,7 +22,8 @@ impl Plugin for EditorPlugin { SystemSet::on_update(AppState::Editor) .with_system(move_system) .with_system(input_control_system) - .with_system(follow_ends_system), + .with_system(follow_ends_system) + .with_system(remove_system), ); } } @@ -61,6 +62,9 @@ struct RotatingFilterAngle(f32); #[derive(Component)] struct AbsorbingFilterColor(Color); +#[derive(Component)] +struct Removable; + // Bundles #[derive(Bundle)] @@ -69,6 +73,9 @@ struct PlatformBundle { mesh: ColorMesh2dBundle, size: Size, platform: Platform, + #[bundle] + pickable: PickableBundle, + removable: Removable, } #[derive(Bundle)] @@ -89,6 +96,7 @@ struct CharacterBundle { #[bundle] pickable: PickableBundle, draggable: Draggable, + removable: Removable, } #[derive(Bundle)] @@ -97,6 +105,9 @@ struct AbsorbingFilterBundle { mesh: ColorMesh2dBundle, size: Size, color: AbsorbingFilterColor, + #[bundle] + pickable: PickableBundle, + removable: Removable, } #[derive(Bundle)] @@ -107,6 +118,7 @@ struct RotatingFilterBundle { #[bundle] pickable: PickableBundle, draggable: Draggable, + removable: Removable, } // Functions @@ -129,6 +141,8 @@ fn spawn_platform( }, size: Size(size), platform: Platform, + pickable: PickableBundle::default(), + removable: Removable, }) .id(); let ends = Ends( @@ -207,6 +221,7 @@ fn spawn_character( color: CharacterColor(color), pickable: PickableBundle::default(), draggable: Draggable, + removable: Removable, }) .with_children(|c| { c.spawn_bundle(Text2dBundle { @@ -245,6 +260,8 @@ fn spawn_absorbing_filter( }, size: Size(size), color: AbsorbingFilterColor(color), + pickable: PickableBundle::default(), + removable: Removable, }) .id(); let ends = Ends( @@ -322,6 +339,7 @@ fn spawn_rotating_filter( angle: RotatingFilterAngle(angle), pickable: PickableBundle::default(), draggable: Draggable, + removable: Removable, }) .with_children(|c| { c.spawn_bundle(Text2dBundle { @@ -584,6 +602,24 @@ fn move_system( } } +fn remove_system( + mut commands: Commands, + keyboard_input: Res>, + mut removable_query: Query<(Entity, &Selection, Option<&Ends>), With>, +) { + if keyboard_input.just_released(KeyCode::Delete) { + for (entity, selection, ends) in removable_query.iter_mut() { + if selection.selected() { + commands.entity(entity).despawn_recursive(); + if let Some(ends) = ends { + commands.entity(ends.0).despawn_recursive(); + commands.entity(ends.1).despawn_recursive(); + } + } + } + } +} + fn follow_ends_system( mut meshes: ResMut>, mut follower_query: Query<(&mut Transform, &mut Mesh2dHandle, &mut Size, &Ends)>, From 585b74bf727d9ae983caf8c6f6eabc070c7834b4 Mon Sep 17 00:00:00 2001 From: tuxmain Date: Fri, 26 Aug 2022 16:43:10 +0200 Subject: [PATCH 10/37] editor: add objects --- README.md | 3 ++- src/editor.rs | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 66 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1bee3c3..ed73763 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ * more filters * despawn black characters * despawn character when too far -* level design +* more levels * (?) multiplayer * more audio * "jumpable" component to avoid jumping on sensors @@ -56,6 +56,7 @@ Edit the level `N: u32` with the command `bevyjam e`. * **Select**: left click to select, click in void to deselect, CTRL+click to select many, CTRL+A to select all * **Move selection**: arrows to move one step, Shift+arrows to move continuously * **Delete selection**: delete +* **Add objects**: P=platform, C=character, A=absorbing filter, R=rotating filter * **Move camera**: CTRL+arrows * **Save**: CTRL+S diff --git a/src/editor.rs b/src/editor.rs index 8474c1d..28dd89d 100644 --- a/src/editor.rs +++ b/src/editor.rs @@ -23,7 +23,8 @@ impl Plugin for EditorPlugin { .with_system(move_system) .with_system(input_control_system) .with_system(follow_ends_system) - .with_system(remove_system), + .with_system(remove_system) + .with_system(spawn_system), ); } } @@ -620,6 +621,68 @@ fn remove_system( } } +fn spawn_system( + mut commands: Commands, + mut meshes: ResMut>, + mut materials: ResMut>, + asset_server: Res, + camera_query: Query<&Transform, With>, + keyboard_input: Res>, + mut character_list: ResMut, +) { + if keyboard_input.pressed(KeyCode::LControl) + || keyboard_input.pressed(KeyCode::RControl) + || keyboard_input.pressed(KeyCode::LAlt) + || keyboard_input.pressed(KeyCode::RAlt) + { + return; + } + + let camera_pos = camera_query.single().translation; + + if keyboard_input.just_released(KeyCode::P) { + spawn_platform( + &mut commands, + &mut meshes, + &mut materials, + Transform::from_xyz(camera_pos.x, camera_pos.y, 0.), + Vec2 { x: 96., y: 16. }, + ); + } + if keyboard_input.just_released(KeyCode::C) { + let index = character_list.0.len(); + character_list.0.push(spawn_character( + &mut commands, + &mut meshes, + &mut materials, + &asset_server, + Transform::from_xyz(camera_pos.x, camera_pos.y, 0.), + Color::RED, + index, + )); + } + if keyboard_input.just_released(KeyCode::A) { + spawn_absorbing_filter( + &mut commands, + &mut meshes, + &mut materials, + Transform::from_xyz(camera_pos.x, camera_pos.y, 0.), + Vec2 { x: 16., y: 96. }, + Color::RED, + ); + } + if keyboard_input.just_released(KeyCode::R) { + spawn_rotating_filter( + &mut commands, + &mut meshes, + &mut materials, + &asset_server, + Transform::from_xyz(camera_pos.x, camera_pos.y, 0.), + 90., + ); + } +} + fn follow_ends_system( mut meshes: ResMut>, mut follower_query: Query<(&mut Transform, &mut Mesh2dHandle, &mut Size, &Ends)>, From a859d72da33769e0ce1f57cdc045ffe595021ac7 Mon Sep 17 00:00:00 2001 From: tuxmain Date: Fri, 26 Aug 2022 23:54:49 +0200 Subject: [PATCH 11/37] melting platform, level 4 --- Cargo.lock | 18 +-- Cargo.toml | 3 + README.md | 2 +- assets/game.levels.json | 285 +++++++++++++++++++++++++++++++++++++++- assets/melty.png | Bin 0 -> 1127 bytes src/editor.rs | 103 ++++++++++++++- src/game.rs | 53 +++++++- src/levels.rs | 18 +++ src/main.rs | 5 +- 9 files changed, 471 insertions(+), 16 deletions(-) create mode 100644 assets/melty.png diff --git a/Cargo.lock b/Cargo.lock index 2e34d56..4a6e782 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3753,14 +3753,13 @@ checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb" [[package]] name = "wgpu" version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "277e967bf8b7820a76852645a6bce8bbd31c32fda2042e82d8e3ea75fda8892d" +source = "git+https://github.com/mockersf/wgpu/?branch=unconditional-clear-workaround#a703a78644bc277f8b93870297bb3734e25f2be9" dependencies = [ "arrayvec", "js-sys", "log", "naga", - "parking_lot 0.12.1", + "parking_lot 0.11.2", "raw-window-handle 0.4.3", "smallvec", "wasm-bindgen", @@ -3774,8 +3773,7 @@ dependencies = [ [[package]] name = "wgpu-core" version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b92788dec9d0c1bed849a1b83f01b2ee12819bf04a79c90f68e4173f7b5ba2" +source = "git+https://github.com/mockersf/wgpu/?branch=unconditional-clear-workaround#a703a78644bc277f8b93870297bb3734e25f2be9" dependencies = [ "arrayvec", "bit-vec", @@ -3786,7 +3784,7 @@ dependencies = [ "fxhash", "log", "naga", - "parking_lot 0.12.1", + "parking_lot 0.11.2", "profiling", "raw-window-handle 0.4.3", "smallvec", @@ -3799,8 +3797,7 @@ dependencies = [ [[package]] name = "wgpu-hal" version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20cbdfc3d0637dba3d5536b93adef3d26023a0b96f0e1ee5ee9560a401d9f646" +source = "git+https://github.com/mockersf/wgpu/?branch=unconditional-clear-workaround#a703a78644bc277f8b93870297bb3734e25f2be9" dependencies = [ "android_system_properties", "arrayvec", @@ -3823,7 +3820,7 @@ dependencies = [ "metal", "naga", "objc", - "parking_lot 0.12.1", + "parking_lot 0.11.2", "profiling", "range-alloc", "raw-window-handle 0.4.3", @@ -3838,8 +3835,7 @@ dependencies = [ [[package]] name = "wgpu-types" version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f762cbc08e1a51389859cf9c199c7aef544789cf3510889aab12c607f701604" +source = "git+https://github.com/mockersf/wgpu/?branch=unconditional-clear-workaround#a703a78644bc277f8b93870297bb3734e25f2be9" dependencies = [ "bitflags", ] diff --git a/Cargo.toml b/Cargo.toml index 742804f..3eac3b7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,3 +27,6 @@ ticktock = "0.8.0" [profile.dev.package."*"] opt-level = 3 + +[patch.crates-io] +wgpu = { git = "https://github.com/mockersf/wgpu/", branch = "unconditional-clear-workaround" } diff --git a/README.md b/README.md index ed73763..bcce2c6 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ Edit the level `N: u32` with the command `bevyjam e`. * **Select**: left click to select, click in void to deselect, CTRL+click to select many, CTRL+A to select all * **Move selection**: arrows to move one step, Shift+arrows to move continuously * **Delete selection**: delete -* **Add objects**: P=platform, C=character, A=absorbing filter, R=rotating filter +* **Add objects**: P=platform, C=character, A=absorbing filter, R=rotating filter, M=melty platform * **Move camera**: CTRL+arrows * **Save**: CTRL+S diff --git a/assets/game.levels.json b/assets/game.levels.json index bca10de..d52eafa 100644 --- a/assets/game.levels.json +++ b/assets/game.levels.json @@ -54,6 +54,7 @@ ], "absorbing_filters": [], "rotating_filters": [], + "melty_platforms": [], "texts": [ { "pos": [ @@ -129,6 +130,7 @@ ], "absorbing_filters": [], "rotating_filters": [], + "melty_platforms": [], "texts": [ { "pos": [ @@ -209,6 +211,7 @@ } ], "rotating_filters": [], + "melty_platforms": [], "texts": [ { "pos": [ @@ -279,9 +282,10 @@ 0.0, -64.0 ], - "angle": 45.0 + "angle": 120.0 } ], + "melty_platforms": [], "texts": [ { "pos": [ @@ -293,6 +297,284 @@ } ] }, + { + "comment": "First puzzle", + "characters": [ + { + "pos": [ + 184.0, + 168.0 + ], + "color": [ + 0.85, + 0.5, + 0.0, + 1.0 + ] + }, + { + "pos": [ + -184.0, + 168.0 + ], + "color": [ + 0.0, + 0.5, + 0.1, + 1.0 + ] + }, + { + "pos": [ + -1376.0, + -184.0 + ], + "color": [ + 1.0, + 0.0, + 0.0, + 1.0 + ] + }, + { + "pos": [ + -1512.0, + -184.0 + ], + "color": [ + 0.0, + 0.0, + 0.9, + 1.0 + ] + }, + { + "pos": [ + 0.0, + 368.0 + ], + "color": [ + 0.15, + 0.0, + 0.5, + 1.0 + ] + } + ], + "platforms": [ + { + "pos": [ + -12.0, + -264.0 + ], + "size": [ + 456.0, + 16.0 + ] + }, + { + "pos": [ + -148.0, + 120.0 + ], + "size": [ + 200.0, + 16.0 + ] + }, + { + "pos": [ + 148.0, + 120.0 + ], + "size": [ + 200.0, + 16.0 + ] + }, + { + "pos": [ + -1336.0, + -256.0 + ], + "size": [ + 576.0, + 16.0 + ] + }, + { + "pos": [ + -240.0, + 292.0 + ], + "size": [ + 16.0, + 328.0 + ] + }, + { + "pos": [ + 240.0, + 292.0 + ], + "size": [ + 16.0, + 328.0 + ] + }, + { + "pos": [ + 0.0, + 20.0 + ], + "size": [ + 176.0, + 24.0 + ] + }, + { + "pos": [ + -200.0, + 60.0 + ], + "size": [ + 16.0, + 104.0 + ] + }, + { + "pos": [ + 200.0, + 60.0 + ], + "size": [ + 16.0, + 104.0 + ] + }, + { + "pos": [ + 0.0, + 320.0 + ], + "size": [ + 96.0, + 16.0 + ] + } + ], + "absorbing_filters": [ + { + "pos": [ + -1176.0, + -96.0 + ], + "size": [ + 16.0, + 304.0 + ], + "color": [ + 0.0, + 0.5, + 0.5, + 1.0 + ] + }, + { + "pos": [ + -140.0, + 16.0 + ], + "size": [ + 104.0, + 16.0 + ], + "color": [ + 0.6, + 0.0, + 0.0, + 1.0 + ] + }, + { + "pos": [ + 140.0, + 16.0 + ], + "size": [ + 104.0, + 16.0 + ], + "color": [ + 0.0, + 1.0, + 0.0, + 1.0 + ] + } + ], + "rotating_filters": [], + "melty_platforms": [ + { + "pos": [ + 0.0, + 120.0 + ], + "color": [ + 0.7, + 0.7, + 0.0, + 1.0 + ] + }, + { + "pos": [ + -616.0, + -256.0 + ], + "color": [ + 0.45, + 0.0, + 0.0, + 1.0 + ] + }, + { + "pos": [ + -856.0, + -256.0 + ], + "color": [ + 0.0, + 0.0, + 0.5, + 1.0 + ] + }, + { + "pos": [ + -400.0, + -256.0 + ], + "color": [ + 0.0, + 0.0, + 0.6, + 1.0 + ] + } + ], + "texts": [ + { + "pos": [ + 0.0, + -64.0 + ], + "font_size": 32.0, + "text": "Too much light\ncan cause some platforms to melt." + } + ] + }, { "comment": "Game over", "characters": [ @@ -323,6 +605,7 @@ ], "absorbing_filters": [], "rotating_filters": [], + "melty_platforms": [], "texts": [ { "pos": [ diff --git a/assets/melty.png b/assets/melty.png new file mode 100644 index 0000000000000000000000000000000000000000..5d5716fc397b7dee2a0c60996219ecadc6cf89ce GIT binary patch literal 1127 zcmV-t1ep7YP)AHIP0004mX+uL$Nkc;* zaB^>EX>4Tx04R}tkv&MmKpe$iTct%R3U-j$0B8Y^bWHa-`5n`d##c~(3vY`@B5yuo&qkMnP zWrgz=XSG^q?R)YUh6~!tGS_L2AcaLNL4*JqbyQG=g*dGmDJC+spY-rY9luB}nOqex zax9<*6_Voz|AXJ%n#IY4n-oq0y)U-?F$x5CfmXw|zmILZbpiyQfh(=!uQh?0PtqG5 zEp`M9Yy%h99ZlW?E_Z<8CtWfmM+(sN7mL9A8GTb87`g@e*4*CO`#607veZ@j1~@nb z#!8gE?(yzWcW?imY4`U7dX{pk%auSL00007bV*G`2j&PG5Dx-&t#FnA00N6iL_t(o z!?l>dYg17a$3KZ%nt)qT2Z>`yhg8U)Ar4h5U8L2iS-QCti%w2LCl?nd2kV$w=oT9~ z3N8xjA_YN3!D^q&R|w&r``&r?<_l-YkN569=X<{Ae&2&~JWzPhaDMN_7Eq86U0@&B z3H;Up-TZhGM-0?k#`>OYPTy zi@>LZd!NnR>w@>UcwIj=hcRs$-yo2g*Al@L?=SFN?1xW=721jIJiRagX^0)qev zYZ(f*=^&uW{2i&$LTc}+i%&D`yIEJBe*$wd<|6(Vd&6-Scn>PifxHZyOHlh`;4knt zk?3aKq^*N$Cqlq0;FmFqwldk(M&NVkJ~woBEG3Mh-%`SejPN9F6+7l4roacuxXPL# z8ynmAxXiQ8N_&_sO*LEu2 zD`aCmjWZC5lL!f|gj}yELbzE+1sfTjE@Ljj<8op)*3&q#7jA_LwFtCCu&bUuS;pxC tUv7D=Z^*`a8mA``rwIpgU9>F{{sA}vu;HTda^C;|002ovPDHLkV1iPP{(}Gj literal 0 HcmV?d00001 diff --git a/src/editor.rs b/src/editor.rs index 28dd89d..73356e6 100644 --- a/src/editor.rs +++ b/src/editor.rs @@ -63,6 +63,9 @@ struct RotatingFilterAngle(f32); #[derive(Component)] struct AbsorbingFilterColor(Color); +#[derive(Component)] +struct MeltyPlatformColor(Color); + #[derive(Component)] struct Removable; @@ -122,6 +125,17 @@ struct RotatingFilterBundle { removable: Removable, } +#[derive(Bundle)] +struct MeltyPlatformBundle { + #[bundle] + mesh: ColorMesh2dBundle, + color: MeltyPlatformColor, + #[bundle] + pickable: PickableBundle, + draggable: Draggable, + removable: Removable, +} + // Functions fn spawn_platform( @@ -360,6 +374,52 @@ fn spawn_rotating_filter( .id() } +fn spawn_melty_platform( + commands: &mut Commands, + meshes: &mut ResMut>, + materials: &mut ResMut>, + asset_server: &Res, + + transform: Transform, + color: Color, +) -> Entity { + let font = asset_server.get_handle("UacariLegacy-Thin.ttf"); + commands + .spawn_bundle(MeltyPlatformBundle { + mesh: ColorMesh2dBundle { + mesh: meshes + .add(Mesh::from(Quad { + size: Vec2 { x: 96., y: 16. }, + flip: false, + })) + .into(), + material: materials.add(ColorMaterial::from(color)), + transform, + ..default() + }, + color: MeltyPlatformColor(color), + pickable: PickableBundle::default(), + draggable: Draggable, + removable: Removable, + }) + .with_children(|c| { + c.spawn_bundle(Text2dBundle { + text: Text::from_section( + "MELTY", + TextStyle { + font: font.clone(), + font_size: 16., + color: Color::BLACK, + }, + ) + .with_alignment(TextAlignment::CENTER), + transform: Transform::from_xyz(0., 0., 1.), + ..Default::default() + }); + }) + .id() +} + fn spawn_stored_level( commands: &mut Commands, meshes: &mut ResMut>, @@ -413,6 +473,17 @@ fn spawn_stored_level( rotating_filter.angle, ); } + + for melty_platform in stored_level.melty_platforms.iter() { + spawn_melty_platform( + commands, + meshes, + materials, + asset_server, + Transform::from_xyz(melty_platform.pos.x, melty_platform.pos.y, 0.), + melty_platform.color.into(), + ); + } } fn save_level( @@ -427,6 +498,10 @@ fn save_level( (&Transform, &RotatingFilterAngle), (Without, Without), >, + melty_platform_query: &Query< + (&Transform, &MeltyPlatformColor), + (Without, Without), + >, ) { let stored_levels = stored_levels_assets.get_mut(stored_levels_handle).unwrap(); if let Some(stored_level) = stored_levels.levels.get_mut(level_id.0 .0 as usize) { @@ -476,6 +551,17 @@ fn save_level( angle: angle.0, }) } + + stored_level.melty_platforms.clear(); + for (transform, color) in melty_platform_query.iter() { + stored_level.melty_platforms.push(StoredMeltyPlatform { + pos: Vec2 { + x: transform.translation.x, + y: transform.translation.y, + }, + color: color.0.into(), + }) + } } else { return; } @@ -537,8 +623,12 @@ fn input_control_system( (&Transform, &RotatingFilterAngle), (Without, Without), >, + melty_platform_query: Query< + (&Transform, &MeltyPlatformColor), + (Without, Without), + >, ) { - if keyboard_input.just_released(KeyCode::S) + if keyboard_input.just_pressed(KeyCode::S) && (keyboard_input.pressed(KeyCode::LControl) || keyboard_input.pressed(KeyCode::RControl)) { save_level( @@ -550,6 +640,7 @@ fn input_control_system( &platform_query, &absorbing_filter_query, &rotating_filter_query, + &melty_platform_query, ); } } @@ -681,6 +772,16 @@ fn spawn_system( 90., ); } + if keyboard_input.just_released(KeyCode::M) { + spawn_melty_platform( + &mut commands, + &mut meshes, + &mut materials, + &asset_server, + Transform::from_xyz(camera_pos.x, camera_pos.y, 0.), + Color::RED, + ); + } } fn follow_ends_system( diff --git a/src/game.rs b/src/game.rs index 32f3bb0..2463d05 100644 --- a/src/game.rs +++ b/src/game.rs @@ -98,6 +98,9 @@ pub struct Player; #[derive(Component)] pub struct CollisionCount(usize); +#[derive(Component)] +pub struct Melty(pub Color); + // Systems fn setup( @@ -217,6 +220,39 @@ pub fn spawn_platform( .insert(Level); } +pub fn spawn_melty_platform( + commands: &mut Commands, + meshes: &mut ResMut>, + materials: &mut ResMut>, + asset_server: &Res, + + transform: Transform, + color: Color, +) { + commands + .spawn_bundle(ColorMesh2dBundle { + mesh: meshes + .add(Mesh::from(Quad { + size: Vec2 { x: 96., y: 16. }, + flip: false, + })) + .into(), + material: materials.add(ColorMaterial::from(color)), + transform, + ..default() + }) + .insert(Collider::cuboid(48., 8.)) + .insert(Melty(color)) + .insert(Level) + .with_children(|c| { + c.spawn_bundle(SpriteBundle { + texture: asset_server.get_handle("melty.png"), + transform: Transform::from_xyz(0., 0., 0.5), + ..default() + }); + }); +} + fn collision_event_system( mut commands: Commands, character_meshes: Res, @@ -229,6 +265,7 @@ fn collision_event_system( Option<&Player>, )>, pass_through_filter_query: Query<&PassThroughFilter>, + melty_query: Query<&Melty>, mut collision_counter_query: Query<&mut CollisionCount>, mut app_state: ResMut>, audio: Res>, @@ -250,7 +287,7 @@ fn collision_event_system( // If color approximately white if app_state.current() == &AppState::Game - && 4. - new_color.length_squared() < 0.1 + && Vec4::from(new_color).min_element() >= 0.9 { app_state.replace(AppState::Win).ok(); } @@ -275,6 +312,18 @@ fn collision_event_system( ); audio.send(AudioMsg::Fusion).ok(); + } else if let (Ok((c_color, _c_transform, _c_material, _c_player)), Ok(melty)) = + (character_query.get_mut(*e1), melty_query.get(*e2)) + { + if (Vec4::from(melty.0) - Vec4::from(c_color.0)).max_element() <= 0. { + commands.entity(*e2).despawn_recursive(); + } + } else if let (Ok((c_color, _c_transform, _c_material, _c_player)), Ok(melty)) = + (character_query.get_mut(*e2), melty_query.get(*e1)) + { + if (Vec4::from(melty.0) - Vec4::from(c_color.0)).max_element() <= 0. { + commands.entity(*e1).despawn_recursive(); + } } } else if *flags == CollisionEventFlags::SENSOR { if let (Ok((mut c_color, _c_transform, mut c_material, c_player)), Ok(filter)) = ( @@ -292,6 +341,7 @@ fn collision_event_system( c_color.0.b(), ])) .ok(); + audio.send(AudioMsg::Switch).ok(); } } else if let ( Ok((mut c_color, _c_transform, mut c_material, c_player)), @@ -311,6 +361,7 @@ fn collision_event_system( c_color.0.b(), ])) .ok(); + audio.send(AudioMsg::Switch).ok(); } } else if let (Ok(mut collision_count), Err(_)) = ( collision_counter_query.get_mut(*e1), diff --git a/src/levels.rs b/src/levels.rs index f99b429..240abdb 100644 --- a/src/levels.rs +++ b/src/levels.rs @@ -109,6 +109,16 @@ pub fn spawn_stored_level( rotating_filter.angle, ); } + for melty_platform in stored_level.melty_platforms.iter() { + spawn_melty_platform( + commands, + meshes, + materials, + asset_server, + Transform::from_xyz(melty_platform.pos.x, melty_platform.pos.y, 2.), + melty_platform.color.into(), + ); + } for text in stored_level.texts.iter() { commands .spawn_bundle(Text2dBundle { @@ -145,6 +155,7 @@ pub mod stored { pub platforms: Vec, pub absorbing_filters: Vec, pub rotating_filters: Vec, + pub melty_platforms: Vec, pub texts: Vec, } @@ -177,6 +188,13 @@ pub mod stored { pub angle: f32, } + #[derive(Deserialize, Serialize, TypeUuid)] + #[uuid = "cb0773ef-eca6-9b96-dcba-f4240ebdcf40"] + pub struct StoredMeltyPlatform { + pub pos: Vec2, + pub color: Vec4, + } + #[derive(Deserialize, Serialize, TypeUuid)] #[uuid = "72f6321a-f01f-6eea-9b17-3159837a2fd3"] pub struct StoredText { diff --git a/src/main.rs b/src/main.rs index 7976caa..d26e9f1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -99,7 +99,10 @@ fn setup(mut commands: Commands, mut windows: ResMut, asset_server: Res assets.add(asset_server.load::("game.levels.json")), ); commands.insert_resource(assets.add(asset_server.load::("UacariLegacy-Thin.ttf"))); - commands.insert_resource(assets.add(asset_server.load::("bevy.png"))); + commands.insert_resource([ + assets.add(asset_server.load::("bevy.png")), + assets.add(asset_server.load("melty.png")), + ]); commands.insert_resource(assets); commands.spawn_bundle(Camera2dBundle::default()); From e5b041c2c9603ebdb51f85f41ae87acf3f6c8dab Mon Sep 17 00:00:00 2001 From: tuxmain Date: Sat, 27 Aug 2022 09:08:14 +0200 Subject: [PATCH 12/37] Win text follows camera --- README.md | 5 ++++- src/game.rs | 25 +++++++++++++++++++++---- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index bcce2c6..ca17f04 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,10 @@ * (?) multiplayer * more audio * "jumpable" component to avoid jumping on sensors -* bug: in level2, move the blue character to win, then reset. The characters are lighter than expected. +* bug: in level2, move the blue character to win, then reset. The characters are lighter than expected. (also level 4) +* wasm warning +* redshift warning +* itchio test ## Build diff --git a/src/game.rs b/src/game.rs index 2463d05..3c7c8fd 100644 --- a/src/game.rs +++ b/src/game.rs @@ -52,7 +52,8 @@ impl Plugin for GamePlugin { .with_system(player_movement_system) .with_system(level_keyboard_system) .with_system(move_camera) - .with_system(character_particle_effect_system), + .with_system(character_particle_effect_system) + .with_system(move_win_text_system), ) .add_system_to_stage(CoreStage::PostUpdate, collision_event_system); } @@ -101,6 +102,9 @@ pub struct CollisionCount(usize); #[derive(Component)] pub struct Melty(pub Color); +#[derive(Component)] +pub struct WinText; + // Systems fn setup( @@ -287,7 +291,7 @@ fn collision_event_system( // If color approximately white if app_state.current() == &AppState::Game - && Vec4::from(new_color).min_element() >= 0.9 + && new_color.min_element() >= 0.9 { app_state.replace(AppState::Win).ok(); } @@ -491,7 +495,8 @@ fn win_setup( transform: Transform::from_xyz(0., 0., 3.), ..default() }) - .insert(Level); + .insert(Level) + .insert(WinText); commands .spawn_bundle(Text2dBundle { text: Text::from_section( @@ -506,7 +511,8 @@ fn win_setup( transform: Transform::from_xyz(0., 0., 4.), ..Default::default() }) - .insert(Level); + .insert(Level) + .insert(WinText); } fn move_camera( @@ -535,6 +541,17 @@ fn move_camera( } } +fn move_win_text_system( + camera_query: Query<&Transform, With>, + mut win_text_query: Query<&mut Transform, (With, Without)>, +) { + let camera_pos = camera_query.single(); + for mut pos in win_text_query.iter_mut() { + pos.translation.x = camera_pos.translation.x; + pos.translation.y = camera_pos.translation.y; + } +} + fn level_keyboard_system( mut commands: Commands, mut current_level: ResMut, From f3f74f668adda4ff01a6baae9653357426b13555 Mon Sep 17 00:00:00 2001 From: tuxmain Date: Sat, 27 Aug 2022 09:34:21 +0200 Subject: [PATCH 13/37] Warn wasm users about audio not working --- README.md | 1 - src/menu.rs | 16 ++++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ca17f04..b53f02d 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,6 @@ * more audio * "jumpable" component to avoid jumping on sensors * bug: in level2, move the blue character to win, then reset. The characters are lighter than expected. (also level 4) -* wasm warning * redshift warning * itchio test diff --git a/src/menu.rs b/src/menu.rs index fff9778..f35f024 100644 --- a/src/menu.rs +++ b/src/menu.rs @@ -20,6 +20,22 @@ impl Plugin for MenuPlugin { fn setup(mut commands: Commands, asset_server: Res) { let font = asset_server.get_handle("UacariLegacy-Thin.ttf"); + #[cfg(target_arch = "wasm32")] + commands + .spawn_bundle(Text2dBundle { + text: Text::from_section( + "Note:\nAudio is NOT available in the WASM build.", + TextStyle { + font: font.clone(), + font_size: 24.0, + color: Color::rgba(1., 0.4, 0.4, 1.), + }, + ) + .with_alignment(TextAlignment::CENTER), + transform: Transform::from_xyz(0., -128.0, 0.), + ..Default::default() + }) + .insert(Menu); commands .spawn_bundle(Text2dBundle { text: Text::from_section( From 9f04ec861ed535d543bb3079df9ccb059431c42a Mon Sep 17 00:00:00 2001 From: tuxmain Date: Sat, 27 Aug 2022 09:35:01 +0200 Subject: [PATCH 14/37] opti(particles): fetch ThreadRng only once in a system --- src/game.rs | 3 +-- src/particle_effect.rs | 25 +++++++++++++++++-------- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/game.rs b/src/game.rs index 3c7c8fd..c98f48e 100644 --- a/src/game.rs +++ b/src/game.rs @@ -290,8 +290,7 @@ fn collision_event_system( .clamp(Vec4::ZERO, Vec4::ONE); // If color approximately white - if app_state.current() == &AppState::Game - && new_color.min_element() >= 0.9 + if app_state.current() == &AppState::Game && new_color.min_element() >= 0.9 { app_state.replace(AppState::Win).ok(); } diff --git a/src/particle_effect.rs b/src/particle_effect.rs index b711548..26e275a 100644 --- a/src/particle_effect.rs +++ b/src/particle_effect.rs @@ -1,5 +1,5 @@ use bevy::{prelude::*, sprite::Mesh2dHandle}; -use rand::Rng; +use rand::{rngs::ThreadRng, Rng}; use rand_distr::{Distribution, UnitCircle}; #[cfg(not(target_arch = "wasm32"))] @@ -68,15 +68,20 @@ pub struct ParticleComponent { } impl ParticleComponent { - pub fn new() -> Self { + pub fn new(rng: &mut ThreadRng) -> Self { let mut particle_component: Self = Self::default(); - particle_component.randomize_velocity(MIN_VELOCITY, MAX_VELOCITY); + particle_component.randomize_velocity(rng, MIN_VELOCITY, MAX_VELOCITY); particle_component } - pub fn randomize_velocity(&mut self, min_velocity: f32, max_velocity: f32) { - let random_direction: [f32; 2] = UnitCircle.sample(&mut rand::thread_rng()); - let random_magnitude: f32 = rand::thread_rng().gen_range(min_velocity..max_velocity); + pub fn randomize_velocity( + &mut self, + rng: &mut ThreadRng, + min_velocity: f32, + max_velocity: f32, + ) { + let random_direction: [f32; 2] = UnitCircle.sample(rng); + let random_magnitude: f32 = rng.gen_range(min_velocity..max_velocity); self.velocity = Vec3::new(random_direction[0], random_direction[1], 0.0) * random_magnitude; } } @@ -87,6 +92,8 @@ fn particle_effect_startup( particle_mesh: Res, mut materials: ResMut>, ) { + let mut rng = rand::thread_rng(); + for _p in 0..POOL_COUNT { let color_mesh = ColorMesh2dBundle { mesh: particle_mesh.square.clone(), @@ -96,7 +103,7 @@ fn particle_effect_startup( commands .spawn_bundle(color_mesh) - .insert(ParticleComponent::new()); + .insert(ParticleComponent::new(&mut rng)); } } @@ -110,6 +117,8 @@ fn particle_effect_system( mut particle_effect: ResMut, time: Res