rust - 用Bevy 0.4拖动 Sprite 可以接受的方法是什么?

标签 rust drag-and-drop sprite drag bevy

在尝试Bevy时,我需要拖放 Sprite 。
不幸的是,这似乎尚未准备就绪,或者我没有在文档中找到它。
实现这个目标的最惯用的方式是什么?
到目前为止,我尝试过的是in my answer,但是我很乐意接受另一个更好/更快/更惯用的解决方案。

最佳答案

不幸的是,我没有足够的经验来了解什么是惯用的,但是,这是我如何在应用程序中实现 Sprite 拖动的概述,这对我来说是一种好方法:

  • 我有一个“光标位置”实体,它带有一个转换组件(以及用于标识的Cursor组件),我在系统中每帧将其更新到光标的位置。
  • 每个可拖动对象都有一个HoverableDraggable组件。我在一个系统中遍历那些对象,每个对象都在其中向实体添加/删除HoveredDragged组件,以指示它们是悬停还是拖动。
  • 我有一个检查对象是否被丢弃的系统,如果有,它会给它一个Dropped组件。
  • 我有一个系统,当实体获取“Dragged”组件(使用Added<C>过滤器)时运行,该系统将父对象设置为“光标位置”实体。
  • 和另一个系统,用于当实体获取“已删除”组件时清除父组件。

  • 对我来说,拥有许多责任范围较小的系统感觉很好。由于我缺乏经验,我很想听到反对意见。
    当然,在本概述中我遗漏了很多东西,所以这是我的代码供引用。由于这是根据我的实际代码改编而成的,因此对于一个最小的示例,有些奇怪和不必要的代码:
    #![allow(clippy::type_complexity)]
    
    use bevy::{prelude::*, render::camera::Camera};
    
    fn main() {
        App::build()
            .init_resource::<State>()
            .add_resource(WindowDescriptor {
                title: "Bevy".to_string(),
                width: 1024.0,
                height: 768.0,
                vsync: true,
                ..Default::default()
            })
            .add_plugins(DefaultPlugins)
            .add_plugin(MyPlugin)
            .run();
    }
    
    pub struct MyPlugin;
    
    impl Plugin for MyPlugin {
        fn build(&self, app: &mut AppBuilder) {
            app.add_startup_system(setup.system())
                .add_system_to_stage(stage::PRE_UPDATE, cursor_state.system())
                .add_system_to_stage(stage::UPDATE, cursor_transform.system())
                .add_system_to_stage(stage::UPDATE, draggable.system())
                .add_system_to_stage(stage::UPDATE, hoverable.system())
                .add_system_to_stage(stage::POST_UPDATE, drag.system())
                .add_system_to_stage(stage::POST_UPDATE, drop.system())
                .add_system_to_stage(stage::POST_UPDATE, material.system());
        }
    }
    
    const SPRITE_SIZE: f32 = 55.0;
    
    fn setup(
        commands: &mut Commands,
        asset_server: Res<AssetServer>,
        mut materials: ResMut<Assets<ColorMaterial>>,
    ) {
        let bevy_texture = asset_server.load("sprites/bevy-icon.png");
    
        commands
            .spawn(Camera2dBundle::default())
            .spawn(())
            .with(CursorState::default())
            .spawn((Transform::default(), GlobalTransform::default(), Cursor));
    
        for _ in 0..4 {
            commands
                .spawn(SpriteBundle {
                    material: materials.add(bevy_texture.clone().into()),
                    sprite: Sprite::new(Vec2::new(SPRITE_SIZE, SPRITE_SIZE)),
                    ..Default::default()
                })
                .with(Hoverable)
                .with(Draggable);
        }
    }
    
    #[derive(Default)]
    struct CursorState {
        cursor_world: Vec2,
        cursor_moved: bool,
    }
    
    struct Cursor;
    
    struct Draggable;
    struct Dragged;
    struct Dropped;
    
    struct Hoverable;
    struct Hovered;
    
    fn cursor_state(
        mut state: ResMut<State>,
        e_cursor_moved: Res<Events<CursorMoved>>,
        windows: Res<Windows>,
        mut q_cursor_state: Query<&mut CursorState>,
        q_camera: Query<&Transform, With<Camera>>,
    ) {
        let event_cursor_screen = state.er_cursor_moved.latest(&e_cursor_moved);
    
        for mut cursor_state in q_cursor_state.iter_mut() {
            if let Some(event_cursor_screen) = event_cursor_screen {
                let window = windows.get_primary().unwrap();
                let cam_transform = q_camera.iter().last().unwrap();
                cursor_state.cursor_world =
                    cursor_to_world(window, cam_transform, event_cursor_screen.position);
    
                cursor_state.cursor_moved = true;
            } else {
                cursor_state.cursor_moved = false;
            }
        }
    }
    
    fn cursor_transform(
        commands: &mut Commands,
        q_cursor_state: Query<&CursorState>,
        mut q_cursor: Query<(Entity, &mut Transform), With<Cursor>>,
    ) {
        let cursor_state = q_cursor_state.iter().next().unwrap();
    
        for (cursor_e, mut transform) in q_cursor.iter_mut() {
            transform.translation.x = cursor_state.cursor_world.x;
            transform.translation.y = cursor_state.cursor_world.y;
            commands.remove_one::<Parent>(cursor_e);
        }
    }
    
    fn hoverable(
        commands: &mut Commands,
        q_cursor_state: Query<&CursorState>,
        q_hoverable: Query<(Entity, &Transform, &Sprite), (With<Hoverable>, Without<Dragged>)>,
    ) {
        let cursor_state = q_cursor_state.iter().next().unwrap();
    
        if cursor_state.cursor_moved {
            for (entity, transform, sprite) in q_hoverable.iter() {
                let half_width = sprite.size.x / 2.0;
                let half_height = sprite.size.y / 2.0;
    
                if transform.translation.x - half_width < cursor_state.cursor_world.x
                    && transform.translation.x + half_width > cursor_state.cursor_world.x
                    && transform.translation.y - half_height < cursor_state.cursor_world.y
                    && transform.translation.y + half_height > cursor_state.cursor_world.y
                {
                    commands.insert_one(entity, Hovered);
                } else {
                    commands.remove_one::<Hovered>(entity);
                }
            }
        }
    }
    
    fn material(
        mut materials: ResMut<Assets<ColorMaterial>>,
        q_hoverable: Query<
            (&Handle<ColorMaterial>, Option<&Hovered>, Option<&Dragged>),
            With<Hoverable>,
        >,
    ) {
        let mut first = true;
    
        for (material, hovered, dragged) in q_hoverable.iter() {
            let (red, green, alpha) = if dragged.is_some() {
                (0.0, 1.0, 1.0)
            } else if first && hovered.is_some() {
                first = false;
                (1.0, 0.0, 1.0)
            } else if hovered.is_some() {
                (1.0, 1.0, 0.5)
            } else {
                (1.0, 1.0, 1.0)
            };
    
            materials.get_mut(material).unwrap().color.set_r(red);
            materials.get_mut(material).unwrap().color.set_g(green);
            materials.get_mut(material).unwrap().color.set_a(alpha);
        }
    }
    
    fn cursor_to_world(window: &Window, cam_transform: &Transform, cursor_pos: Vec2) -> Vec2 {
        // get the size of the window
        let size = Vec2::new(window.width() as f32, window.height() as f32);
    
        // the default orthographic projection is in pixels from the center;
        // just undo the translation
        let screen_pos = cursor_pos - size / 2.0;
    
        // apply the camera transform
        let out = cam_transform.compute_matrix() * screen_pos.extend(0.0).extend(1.0);
        Vec2::new(out.x, out.y)
    }
    
    fn draggable(
        commands: &mut Commands,
        i_mouse_button: Res<Input<MouseButton>>,
        q_pressed: Query<Entity, (With<Hovered>, With<Draggable>)>,
        q_released: Query<Entity, With<Dragged>>,
    ) {
        if i_mouse_button.just_pressed(MouseButton::Left) {
            if let Some(entity) = q_pressed.iter().next() {
                commands.insert_one(entity, Dragged);
            }
        } else if i_mouse_button.just_released(MouseButton::Left) {
            for entity in q_released.iter() {
                commands.remove_one::<Dragged>(entity);
    
                commands.insert_one(entity, Dropped);
            }
        }
    }
    
    fn drag(
        commands: &mut Commands,
        mut q_dragged: Query<(Entity, &mut Transform, &GlobalTransform), Added<Dragged>>,
        q_cursor: Query<(Entity, &GlobalTransform), With<Cursor>>,
    ) {
        if let Some((cursor_e, cursor_transform)) = q_cursor.iter().next() {
            for (entity, mut transform, global_transform) in q_dragged.iter_mut() {
                let global_pos = global_transform.translation - cursor_transform.translation;
    
                commands.insert_one(entity, Parent(cursor_e));
    
                transform.translation.x = global_pos.x;
                transform.translation.y = global_pos.y;
            }
        }
    }
    
    fn drop(
        commands: &mut Commands,
        mut q_dropped: Query<(Entity, &mut Transform, &GlobalTransform), Added<Dropped>>,
    ) {
        for (entity, mut transform, global_transform) in q_dropped.iter_mut() {
            let global_pos = global_transform.translation;
    
            transform.translation.x = global_pos.x;
            transform.translation.y = global_pos.y;
    
            commands.remove_one::<Parent>(entity);
            commands.remove_one::<Dropped>(entity);
        }
    }
    
    #[derive(Default)]
    struct State {
        er_cursor_moved: EventReader<CursorMoved>,
    }
    
    此代码适用于bevy 0.4。

    关于rust - 用Bevy 0.4拖动 Sprite 可以接受的方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65396065/

    相关文章:

    concurrency - 为什么 'if let' 会阻止使用 Mutex 的执行?

    rust - 我怎样才能更好地记住何时使用哪种生命周期语法?

    c# - MouseButton.LeftButton 在拖放过程中错误地报告为释放 GiveFeedback

    asp.net-mvc - 在mvc中拖放文件

    rust - Rust 中的空指针优化是什么?

    rust - Vec::iter() 转换为借用 Option

    java - Primefaces 树 : don't remove the node after drag and drop

    html - 使用 CSS Sprite ?

    android - 使用 LibGDX 制作静态动画的有效方法?

    iphone - Cocos2D 我应该为每个 Sprite 使用一张图像