Editor: first steps
This commit is contained in:
parent
1d22c11cde
commit
09138229ca
8 changed files with 339 additions and 80 deletions
24
Cargo.lock
generated
24
Cargo.lock
generated
|
@ -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",
|
||||
]
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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 <N>`.
|
||||
|
||||
Edit the level `N: u32` with the command `bevyjam <N> e`.
|
||||
|
||||
## License
|
||||
|
||||
GNU AGPL v3, CopyLeft 2022 Pascal Engélibert, Nixon Cheng
|
||||
|
|
|
@ -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
|
||||
|
|
174
src/editor.rs
Normal file
174
src/editor.rs
Normal file
|
@ -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<Assets<Mesh>>,
|
||||
materials: &mut ResMut<Assets<ColorMaterial>>,
|
||||
|
||||
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<Assets<Mesh>>,
|
||||
materials: &mut ResMut<Assets<ColorMaterial>>,
|
||||
asset_server: &Res<AssetServer>,
|
||||
|
||||
stored_level: &StoredLevel,
|
||||
) {
|
||||
let _font = asset_server.get_handle::<Font, _>("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<Assets<Mesh>>,
|
||||
mut materials: ResMut<Assets<ColorMaterial>>,
|
||||
camera_query: Query<Entity, With<Camera>>,
|
||||
level_id: Res<crate::game::FirstLevel>,
|
||||
stored_levels_assets: Res<Assets<StoredLevels>>,
|
||||
stored_levels_handle: Res<Handle<StoredLevels>>,
|
||||
asset_server: Res<AssetServer>,
|
||||
) {
|
||||
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<Input<KeyCode>>,
|
||||
mut drag_query: Query<(&mut Transform, &Selection), With<Draggable>>,
|
||||
) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
114
src/levels.rs
114
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<StoredLevel>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, TypeUuid)]
|
||||
#[uuid = "a1464a30-1f57-a654-d56c-ded41032af0b"]
|
||||
pub struct StoredLevel {
|
||||
pub comment: String,
|
||||
pub characters: Vec<StoredCharacter>,
|
||||
pub platforms: Vec<StoredPlatform>,
|
||||
pub absorbing_filters: Vec<StoredAbsorbingFilter>,
|
||||
pub rotating_filters: Vec<StoredRotatingFilter>,
|
||||
pub texts: Vec<StoredText>,
|
||||
}
|
||||
|
||||
#[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<CharacterMeshes>,
|
||||
|
@ -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<StoredLevel>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, TypeUuid)]
|
||||
#[uuid = "a1464a30-1f57-a654-d56c-ded41032af0b"]
|
||||
pub struct StoredLevel {
|
||||
pub comment: String,
|
||||
pub characters: Vec<StoredCharacter>,
|
||||
pub platforms: Vec<StoredPlatform>,
|
||||
pub absorbing_filters: Vec<StoredAbsorbingFilter>,
|
||||
pub rotating_filters: Vec<StoredRotatingFilter>,
|
||||
pub texts: Vec<StoredText>,
|
||||
}
|
||||
|
||||
#[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,
|
||||
}
|
||||
}
|
||||
|
|
88
src/main.rs
88
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<HandleId>);
|
||||
|
||||
impl LoadingAssets {
|
||||
fn add<T: Asset>(&mut self, handle: Handle<T>) -> Handle<T> {
|
||||
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::<levels::StoredLevels>::new(&[
|
||||
"levels.json",
|
||||
]))
|
||||
.add_plugin(RapierPhysicsPlugin::<NoUserData>::pixels_per_meter(64.0))
|
||||
//.add_plugin(RapierDebugRenderPlugin::default())
|
||||
]));
|
||||
|
||||
if !use_editor {
|
||||
app.add_plugin(RapierPhysicsPlugin::<NoUserData>::pixels_per_meter(64.0))
|
||||
.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)
|
||||
.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<Windows>, asset_server: Res
|
|||
.unwrap()
|
||||
.set_title(String::from("Bevyjam"));
|
||||
|
||||
commands.insert_resource(asset_server.load::<levels::StoredLevels, _>("game.levels.json"));
|
||||
commands.insert_resource(asset_server.load::<Font, _>("UacariLegacy-Thin.ttf"));
|
||||
commands.insert_resource(asset_server.load::<Image, _>("bevy.png"));
|
||||
let mut assets = LoadingAssets(Vec::new());
|
||||
commands.insert_resource(
|
||||
assets.add(asset_server.load::<levels::StoredLevels, _>("game.levels.json")),
|
||||
);
|
||||
commands.insert_resource(assets.add(asset_server.load::<Font, _>("UacariLegacy-Thin.ttf")));
|
||||
commands.insert_resource(assets.add(asset_server.load::<Image, _>("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<Windows>, asset_server: Res
|
|||
});
|
||||
}
|
||||
|
||||
fn loading_system(
|
||||
asset_server: Res<AssetServer>,
|
||||
use_editor: Res<UseEditor>,
|
||||
assets: Res<LoadingAssets>,
|
||||
mut app_state: ResMut<State<AppState>>,
|
||||
) {
|
||||
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<Input<KeyCode>>, mut windows: ResMut<Windows>) {
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
{
|
||||
|
|
|
@ -44,7 +44,6 @@ impl FromWorld for ParticleMesh {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(bevy_inspector_egui::Inspectable)]
|
||||
pub struct ParticleEffectResource {
|
||||
pub translation: Vec3,
|
||||
pub prev_translation: Vec3,
|
||||
|
|
Loading…
Reference in a new issue