~starkingdoms/starkingdoms

6aca948465e18411e666e465693deb03c97f6fd6 — ghostly_zsh 6 days ago 851a890
start ship editor, added mouse navigation
M Cargo.lock => Cargo.lock +9 -0
@@ 5449,6 5449,15 @@ dependencies = [
]

[[package]]
name = "ship_editor"
version = "0.1.0"
dependencies = [
 "bevy",
 "ctrlc",
 "getrandom 0.4.2",
]

[[package]]
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"

A crates/ship_editor/Cargo.toml => crates/ship_editor/Cargo.toml +25 -0
@@ 0,0 1,25 @@
[package]
name = "ship_editor"
version = "0.1.0"
edition = "2024"

[dependencies]
bevy = { workspace = true }
getrandom = { workspace = true }

[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
ctrlc = { workspace = true, optional = true }

[features]
native_dev = [
    "bevy/file_watcher",
    "bevy/hotpatching",
    "bevy/dynamic_linking",
    "native",
]
native = [
    "bevy/x11",
    "bevy/wayland",
    "dep:ctrlc"
]
wasm = ["getrandom/wasm_js", "bevy/webgl2"]

A crates/ship_editor/src/input.rs => crates/ship_editor/src/input.rs +81 -0
@@ 0,0 1,81 @@
use bevy::input::mouse::{MouseScrollUnit, MouseWheel};
use bevy::prelude::*;
use bevy::window::PrimaryWindow;

pub fn input_plugin(app: &mut App) {
    app
        .insert_resource(ShipEditorDrag::default())
        .add_systems(Update, on_scroll)
        .add_systems(Update, on_click)
        .add_systems(Update, drag);
}

#[derive(Resource, Default)]
struct ShipEditorDrag {
    is_dragging: bool,
    init_cursor_pos: Vec2,
    init_camera_pos: Vec2,
}

fn on_scroll(
    mut scroll_events: MessageReader<MouseWheel>,
    mut camera: Single<(&mut Transform, &mut Projection), With<Camera2d>>,
    window: Single<&Window, With<PrimaryWindow>>,
) {
    let Some(cursor_pos) = window.cursor_position() else { return };
    let cursor_pos = cursor_pos.with_y(-cursor_pos.y);

    let mut transform = camera.0.clone();
    let Projection::Orthographic(ref mut projection) = *camera.1 else { return };
    for ev in scroll_events.read() {
        match ev.unit {
            MouseScrollUnit::Line | MouseScrollUnit::Pixel => {
                if ev.y > 0.0 {
                    let mut rel_pos = vec2(window.width() / 2.0, -window.height() / 2.0) - cursor_pos;
                    rel_pos *= projection.scale;
                    projection.scale *= 0.95;
                    let scaled_rel_pos = rel_pos * 0.95;
                    transform.translation += scaled_rel_pos.extend(0.0) - rel_pos.extend(0.0);
                } else {
                    let mut rel_pos = vec2(window.width() / 2.0, -window.height() / 2.0) - cursor_pos;
                    rel_pos *= projection.scale;
                    projection.scale *= 1.05;
                    let scaled_rel_pos = rel_pos * 1.05;
                    transform.translation += scaled_rel_pos.extend(0.0) - rel_pos.extend(0.0);
                }
            }
        }
    }
    *camera.0 = transform;
}

fn on_click(
    ev: Res<ButtonInput<MouseButton>>,
    mut drag: ResMut<ShipEditorDrag>,
    camera: Single<&Transform, With<Camera2d>>,
    window: Single<&Window, With<PrimaryWindow>>,
) {
    let Some(cursor_pos) = window.cursor_position() else { return };
    if ev.just_pressed(MouseButton::Left) {
        drag.is_dragging = true;
        drag.init_cursor_pos = cursor_pos.with_y(-cursor_pos.y);
        drag.init_camera_pos = camera.translation.truncate();
    }
    if ev.just_released(MouseButton::Left) {
        drag.is_dragging = false;
    }
}

fn drag(
    drag: ResMut<ShipEditorDrag>,
    mut camera: Single<(&mut Transform, &Projection), With<Camera2d>>,
    window: Single<&Window, With<PrimaryWindow>>,
) {
    if !drag.is_dragging { return }
    let Some(cursor) = window.cursor_position() else { return };
    let projection = camera.1.clone();
    let Projection::Orthographic(projection) = projection else { return };
    let transform = &mut camera.0;

    transform.translation = drag.init_camera_pos.extend(0.0) - (cursor.with_y(-cursor.y) - drag.init_cursor_pos).extend(0.0)*projection.scale;
}

A crates/ship_editor/src/main.rs => crates/ship_editor/src/main.rs +32 -0
@@ 0,0 1,32 @@
use bevy::{prelude::*};
use crate::input::input_plugin;
use crate::ui::ui_plugin;

pub mod ui;
mod input;

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_systems(Startup, setup)
        .add_plugins(ui_plugin)
        .add_plugins(input_plugin)
        .run();
}

fn setup(
    mut commands: Commands,
    mut meshes: ResMut<Assets<Mesh>>,
    mut materials: ResMut<Assets<ColorMaterial>>,
) {
    commands.spawn((
        Camera2d::default(),
        Transform::from_xyz(0.0, 0.0, 0.0),
    ));
    let rectangle = meshes.add(Rectangle::new(50.0, 50.0));
    commands.spawn((
        Mesh2d(rectangle),
        MeshMaterial2d(materials.add(Color::linear_rgb(0.0, 0.0, 0.0))),
        Transform::from_xyz(0.0, 0.0, 0.0)
    ));
}

A crates/ship_editor/src/ui.rs => crates/ship_editor/src/ui.rs +23 -0
@@ 0,0 1,23 @@
use bevy::prelude::*;

pub fn ui_plugin(app: &mut App) {
    app.add_systems(Startup, setup_ui);
}

fn setup_ui(mut commands: Commands) {
    commands.spawn((
        Node {
            width: Val::Percent(100.),
            height: Val::Percent(100.),
            ..Default::default()
        },
        children![(
            Node {
                width: Val::Px(32.0),
                height: Val::Px(32.0),
                ..Default::default()
            },
            BackgroundColor(Color::LinearRgba(LinearRgba::rgb(1.0, 1.0, 1.0))),
        )],
    ));
}