Compare commits
3 commits
09138229ca
...
da5f9b0820
Author | SHA1 | Date | |
---|---|---|---|
da5f9b0820 | |||
5d2289d1c0 | |||
02b29bd093 |
2 changed files with 200 additions and 46 deletions
|
@ -51,6 +51,12 @@ Skip to level `N: u32` with the command `bevyjam <N>`.
|
|||
|
||||
Edit the level `N: u32` with the command `bevyjam <N> 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
|
||||
|
|
238
src/editor.rs
238
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,40 @@ pub struct EditorPlugin;
|
|||
|
||||
impl Plugin for EditorPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.add_plugins(DefaultPickingPlugins)
|
||||
app.add_event::<DragEndEvent>()
|
||||
.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(move_system)
|
||||
.with_system(follow_ends_system),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Events
|
||||
|
||||
struct DragEndEvent(Entity);
|
||||
|
||||
// Resources
|
||||
|
||||
// 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,
|
||||
}
|
||||
|
||||
#[derive(Bundle)]
|
||||
|
@ -47,6 +61,17 @@ struct PlatformEndBundle {
|
|||
end: End,
|
||||
}
|
||||
|
||||
#[derive(Bundle)]
|
||||
struct CharacterBundle {
|
||||
#[bundle]
|
||||
mesh: ColorMesh2dBundle,
|
||||
#[bundle]
|
||||
pickable: PickableBundle,
|
||||
draggable: Draggable,
|
||||
}
|
||||
|
||||
// Functions
|
||||
|
||||
fn spawn_platform(
|
||||
commands: &mut Commands,
|
||||
meshes: &mut ResMut<Assets<Mesh>>,
|
||||
|
@ -55,7 +80,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,40 +88,97 @@ fn spawn_platform(
|
|||
transform,
|
||||
..default()
|
||||
},
|
||||
platform: Platform,
|
||||
})
|
||||
.id();
|
||||
let ends = Platform(
|
||||
commands
|
||||
.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(
|
||||
transform.translation.x - size.x / 2.,
|
||||
transform.translation.y - size.y / 2.,
|
||||
0.5,
|
||||
),
|
||||
..default()
|
||||
},
|
||||
pickable: PickableBundle::default(),
|
||||
draggable: Draggable,
|
||||
end: End(platform),
|
||||
})
|
||||
.id(),
|
||||
commands
|
||||
.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(
|
||||
transform.translation.x + size.x / 2.,
|
||||
transform.translation.y + size.y / 2.,
|
||||
0.5,
|
||||
),
|
||||
..default()
|
||||
},
|
||||
pickable: PickableBundle::default(),
|
||||
draggable: Draggable,
|
||||
end: End(platform),
|
||||
})
|
||||
.id(),
|
||||
);
|
||||
commands.entity(platform).insert(ends);
|
||||
}
|
||||
|
||||
fn spawn_character(
|
||||
commands: &mut Commands,
|
||||
meshes: &mut ResMut<Assets<Mesh>>,
|
||||
materials: &mut ResMut<Assets<ColorMaterial>>,
|
||||
asset_server: &Res<AssetServer>,
|
||||
|
||||
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(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()
|
||||
c.spawn_bundle(Text2dBundle {
|
||||
text: Text::from_section(
|
||||
&index.to_string(),
|
||||
TextStyle {
|
||||
font: font.clone(),
|
||||
font_size: 32.,
|
||||
color: Color::WHITE,
|
||||
},
|
||||
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,
|
||||
)
|
||||
.with_alignment(TextAlignment::CENTER),
|
||||
transform: Transform::from_xyz(0., 0., 1.),
|
||||
..Default::default()
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -109,8 +191,6 @@ pub fn spawn_stored_level(
|
|||
|
||||
stored_level: &StoredLevel,
|
||||
) {
|
||||
let _font = asset_server.get_handle::<Font, _>("UacariLegacy-Thin.ttf");
|
||||
|
||||
for platform in stored_level.platforms.iter() {
|
||||
spawn_platform(
|
||||
commands,
|
||||
|
@ -120,7 +200,21 @@ 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
|
||||
|
||||
fn setup(
|
||||
mut commands: Commands,
|
||||
|
@ -152,22 +246,76 @@ fn setup(
|
|||
}
|
||||
}
|
||||
|
||||
fn keyboard_input_system(
|
||||
fn move_system(
|
||||
keyboard_input: Res<Input<KeyCode>>,
|
||||
mut drag_query: Query<(&mut Transform, &Selection), With<Draggable>>,
|
||||
mut camera_query: Query<&mut Transform, (With<Camera>, Without<Draggable>)>,
|
||||
mut drag_query: Query<(&mut Transform, &Selection, Option<&End>), With<Draggable>>,
|
||||
mut drag_end_event: EventWriter<DragEndEvent>,
|
||||
) {
|
||||
let drag = 8.
|
||||
* Vec3 {
|
||||
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 {
|
||||
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<Assets<Mesh>>,
|
||||
mut platform_query: Query<(&mut Transform, &mut Mesh2dHandle, &Platform)>,
|
||||
end_query: Query<&Transform, Without<Platform>>,
|
||||
mut drag_end_event: EventReader<DragEndEvent>,
|
||||
) {
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue