Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Picking doesnt work on adjacent 2d meshes (backend_raycast) #341

Open
Zizico2 opened this issue Jun 23, 2024 · 2 comments
Open

Picking doesnt work on adjacent 2d meshes (backend_raycast) #341

Zizico2 opened this issue Jun 23, 2024 · 2 comments

Comments

@Zizico2
Copy link

Zizico2 commented Jun 23, 2024

In 2d, when I have 2 Meshes that are exactly adjacent to each other, I can position the mouse right on the edge between them, and the click won't be captured. Is this how it is supposed to work? User error?

For example, creating a chessboard like this (every square is exactly 50 wide, and 50 away from each other, you can put the mouse right between the squares and the click won't be recognized.

use bevy::{
    prelude::*,
    sprite::{MaterialMesh2dBundle, Mesh2dHandle},
    window::WindowResolution,
};
use bevy_mod_picking::prelude::*;
#[derive(Component)]
struct MainCamera;

fn main() {
    App::new()
        .add_plugins(DefaultPlugins.set(WindowPlugin {
            primary_window: Some(Window {
                resolution: WindowResolution::new(400., 400.).with_scale_factor_override(1.0),
                resizable: false,
                ..default()
            }),
            ..default()
        }))
        .add_plugins(DefaultPickingPlugins)
        .add_systems(Startup, setup)
        .run();
}

fn setup(
    mut commands: Commands,
    mut meshes: ResMut<Assets<Mesh>>,
    mut materials: ResMut<Assets<ColorMaterial>>,
) {
    commands.spawn((
        Camera2dBundle {
            transform: Transform {
                translation: Vec3 {
                    x: 200.,
                    y: 200.,
                    z: 0.,
                },
                ..default()
            },
            ..default()
        },
        MainCamera,
    ));

    let mut color = Color::BEIGE;
    for x in 0..8_usize {
        for y in 0..8_usize {
            commands.spawn((
                PickableBundle::default(),
                On::<Pointer<Click>>::run(move || {
                    info!("({x}, {y})");
                }),
                MaterialMesh2dBundle {
                    mesh: Mesh2dHandle(meshes.add(Rectangle::new(50.0, 50.0))),
                    transform: Transform::from_translation(Vec3 {
                        x: ((x * 50) + 25) as f32,
                        y: ((y * 50) + 25) as f32,
                        z: 0.,
                    }),
                    material: materials.add(color),
                    ..default()
                },
            ));
            match color {
                Color::BEIGE => color = Color::DARK_GREEN,
                Color::DARK_GREEN => color = Color::BEIGE,
                _ => panic!("wtf is going on"),
            }
        }
        match color {
            Color::BEIGE => color = Color::DARK_GREEN,
            Color::DARK_GREEN => color = Color::BEIGE,
            _ => panic!("wtf is going on"),
        }
    }
}
[dependencies]
bevy = { version = "0.13.2" }
bevy_mod_picking = { version = "0.19.0", features = [
    "backend_raycast",
], default-features = false }
@Zizico2 Zizico2 changed the title Picking doesnt work on adjacent meshes Picking doesnt work on adjacent meshes (backend_raycast) Jun 23, 2024
@Zizico2 Zizico2 changed the title Picking doesnt work on adjacent meshes (backend_raycast) Picking doesnt work on adjacent 2d meshes (backend_raycast) Jun 23, 2024
@StrikeForceZero
Copy link
Contributor

StrikeForceZero commented Jul 12, 2024

I can reproduce it with your example. If I had to guess, it's some kind of floating-point math error, but when logging the cursor position and x/y, I get whole numbers, so I'm not entirely sure.

When the stars align, the ray cast appears like it's failing to resolve anything on some of the squares (note "some" as it only happens to the right-hand side of the board for me, so maybe as x increases, the margin of error increases?). Still, if you move everything by 1, it no longer has an issue AFAICT. Alternatively, if you set your camera translation to (0,0,0), it appears to be fine, at least until you maximize the window. Maybe it's something to when coordinates get to a specific size?

Vertical adjacencies don't seem to be an issue, or maybe you need a larger grid to see them.

Modified Example with input mapped to moving the camera and boards
use std::fmt::Debug;

use bevy::{
    prelude::*,
    sprite::{MaterialMesh2dBundle, Mesh2dHandle},
    window::WindowResolution,
};
use bevy::color::palettes::css::{BEIGE, DARK_GREEN};
use bevy::window::PrimaryWindow;
use bevy_mod_picking::prelude::*;

#[derive(Component, Debug)]
struct MainCamera;

#[derive(Component, Debug)]
struct Board;

#[derive(Component)]
struct Moveable;

fn is_key_pressed_factory(key_code: KeyCode) -> impl Fn(Res<ButtonInput<KeyCode>>) -> bool {
    move |input: Res<ButtonInput<KeyCode>>| -> bool { input.just_pressed(key_code) }
}

fn main() {
    App::new()
        .add_plugins(DefaultPlugins.set(WindowPlugin {
            primary_window: Some(Window {
                resolution: WindowResolution::new(800., 400.).with_scale_factor_override(1.0),
                // resizable: false,
                ..default()
            }),
            ..default()
        }))
        .add_plugins(DefaultPickingPlugins)
        .add_systems(Startup, setup)
        .add_systems(
            Update,
            move_left::<Board>.run_if(is_key_pressed_factory(KeyCode::KeyA)),
        )
        .add_systems(
            Update,
            move_right::<Board>.run_if(is_key_pressed_factory(KeyCode::KeyD)),
        )
        .add_systems(
            Update,
            move_left::<MainCamera>.run_if(is_key_pressed_factory(KeyCode::KeyQ)),
        )
        .add_systems(
            Update,
            move_right::<MainCamera>.run_if(is_key_pressed_factory(KeyCode::KeyE)),
        )
        .add_systems(Update, log_click)
        .run();
}

fn move_right<T: Component + Debug>(mut q: Query<(&T, &mut Transform), With<Moveable>>) {
    for (comp, mut transform) in q.iter_mut() {
        println!("moving {comp:?} right");
        transform.translation.x += 1.0;
    }
}

fn move_left<T: Component + Debug>(mut q: Query<(&T, &mut Transform), With<Moveable>>) {
    for (comp, mut transform) in q.iter_mut() {
        println!("moving {comp:?} left");
        transform.translation.x -= 1.0;
    }
}

fn setup(
    mut commands: Commands,
    mut meshes: ResMut<Assets<Mesh>>,
    mut materials: ResMut<Assets<ColorMaterial>>,
) {
    let camera_entity = commands
        .spawn((
            Camera2dBundle {
                transform: Transform::from_xyz(200.0, 200.0, 0.0),
                ..default()
            },
            MainCamera,
            Moveable,
        ))
        .id();

    let square_size = 50.0;
    let width = 8usize;
    let height = 8usize;

    let mesh = Mesh2dHandle(meshes.add(Rectangle::new(square_size, square_size)));
    let dark_mat = materials.add(Color::from(BEIGE));
    let light_mat = materials.add(Color::from(DARK_GREEN));

    fn is_dark_square(x: usize, y: usize) -> bool {
        x % 2 == 0 && y % 2 == 0 || x % 2 != 0 && y % 2 != 0
    }

    let mut render_board = |offset: Vec2, movable: bool| {
        let mut entity = commands.spawn((Board, SpatialBundle::default()));
        entity.with_children(|parent| {
            for x in 0..width {
                for y in 0..height {
                    let material = if is_dark_square(x, y) {
                        dark_mat.clone()
                    } else {
                        light_mat.clone()
                    };
                    parent.spawn((
                        PickableBundle::default(),
                        On::<Pointer<Click>>::run(move || {
                            info!("({x}, {y})");
                        }),
                        MaterialMesh2dBundle {
                            mesh: mesh.clone(),
                            transform: Transform::from_translation(Vec3 {
                                x: x as f32 * square_size + offset.x,
                                y: y as f32 * square_size + offset.y,
                                z: 1.0,
                            }),
                            material,
                            ..default()
                        },
                    ));
                }
            }
        });
        if movable {
            entity.insert(Moveable);
        }
    };

    render_board(Vec2::new(0.0, 0.0), true);
    // render_board(Vec2::new(-square_size * width as f32, 0.0), false);
    // render_board(Vec2::new(square_size * width as f32 / 2.0, 0.0), true);

    let mut ui = commands.spawn((
        TargetCamera(camera_entity),
        NodeBundle {
            style: Style {
                width: Val::Percent(100.0),
                flex_direction: FlexDirection::Row,
                ..default()
            },
            ..default()
        },
    ));
    ui.with_children(|parent| {
        parent.spawn(TextBundle {
            text: Text::from_section(
                "KeyMap: A/D = board, Q/E = camera",
                TextStyle {
                    font_size: 20.0,
                    color: Color::WHITE,
                    ..default()
                },
            ),
            ..default()
        });
    });
}

fn log_click(input: Res<ButtonInput<MouseButton>>, q_windows: Query<&Window, With<PrimaryWindow>>) {
    if input.just_pressed(MouseButton::Left) {
        if let Some(position) = q_windows.single().cursor_position() {
            println!("Cursor is inside the primary window, at {:?}", position);
        } else {
            println!("Cursor is not in the game window.");
        }
    }
}

@StrikeForceZero
Copy link
Contributor

StrikeForceZero commented Jul 13, 2024

I believe I have found the potential root cause and opened aevyrie/bevy_mod_raycast#118

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants