-
-
Notifications
You must be signed in to change notification settings - Fork 16
Open
Description
Summary
The isometric game uses a two-stage render pipeline (scene camera renders to an offscreen texture, not the window). Bevy's built-in MeshPickingPlugin doesn't work with this setup — hover events never fire.
This replaces it with a custom Rapier raycast-based hover system that casts rays from the orthographic scene camera through the cursor position.
Changes
main.rs
- Remove
MeshPickingPlugin
object_registry.rs
- Remove per-entity
.observe(on_pointer_over)/.observe(on_pointer_out)from all object spawns
scene_objects.rs
- Remove
on_pointer_over/on_pointer_outevent handlers - Add
raycast_hover_detectionsystem using Rapiercast_raythrough the orthographic camera - Fix
update_occlusionto queryIsometricCamerainstead ofCamera3d
Diff
diff --git a/apps/kbve/isometric/src-tauri/src/game/object_registry.rs b/apps/kbve/isometric/src-tauri/src/game/object_registry.rs
index 4c2ef8d42..0d529f564 100644
--- a/apps/kbve/isometric/src-tauri/src/game/object_registry.rs
+++ b/apps/kbve/isometric/src-tauri/src/game/object_registry.rs
@@ -11,8 +11,7 @@ use std::sync::LazyLock;
use bevy_rapier3d::prelude::*;
use super::scene_objects::{
- AnimatedCrystal, HoverOutline, Occludable, OriginalEmissive, RotatingBox, on_pointer_out,
- on_pointer_over,
+ AnimatedCrystal, HoverOutline, Occludable, OriginalEmissive, RotatingBox,
};
use super::terrain::TerrainMap;
@@ -339,8 +338,6 @@ fn spawn_object_entity(
half_extents: Vec3::splat(half),
},
))
- .observe(on_pointer_over)
- .observe(on_pointer_out)
.id()
}
ObjectKind::DarkCrate => {
@@ -367,8 +364,6 @@ fn spawn_object_entity(
half_extents: Vec3::splat(half),
},
))
- .observe(on_pointer_over)
- .observe(on_pointer_out)
.id()
}
ObjectKind::Crystal => {
@@ -393,8 +388,6 @@ fn spawn_object_entity(
half_extents: Vec3::splat(1.0),
},
))
- .observe(on_pointer_over)
- .observe(on_pointer_out)
.id()
}
ObjectKind::Pillar => commands
@@ -417,8 +410,6 @@ fn spawn_object_entity(
half_extents: Vec3::new(0.4, 2.0, 0.4),
},
))
- .observe(on_pointer_over)
- .observe(on_pointer_out)
.id(),
ObjectKind::MetallicSphere => {
let radius = 0.8;
@@ -442,8 +433,6 @@ fn spawn_object_entity(
half_extents: Vec3::splat(radius),
},
))
- .observe(on_pointer_over)
- .observe(on_pointer_out)
.id()
}
ObjectKind::SpotLight => commands
diff --git a/apps/kbve/isometric/src-tauri/src/game/scene_objects.rs b/apps/kbve/isometric/src-tauri/src/game/scene_objects.rs
index 39c1af598..98345160a 100644
--- a/apps/kbve/isometric/src-tauri/src/game/scene_objects.rs
+++ b/apps/kbve/isometric/src-tauri/src/game/scene_objects.rs
@@ -1,11 +1,10 @@
-use bevy::picking::events::{Out, Over, Pointer};
use bevy::prelude::*;
+use bevy::window::PrimaryWindow;
+use bevy_rapier3d::prelude::*;
+use super::camera::IsometricCamera;
use super::player::Player;
-// Re-export EntityEvent so event_target() is available
-use bevy::ecs::event::EntityEvent;
-
/// Marker for objects that become semi-transparent when occluding the player.
#[derive(Component)]
pub struct Occludable;
@@ -39,6 +38,7 @@ impl Plugin for SceneObjectsPlugin {
app.add_systems(
Update,
(
+ raycast_hover_detection,
animate_crystal,
rotate_boxes,
update_occlusion,
@@ -49,12 +49,81 @@ impl Plugin for SceneObjectsPlugin {
}
}
-pub(crate) fn on_pointer_over(trigger: On<Pointer<Over>>, mut commands: Commands) {
- commands.entity(trigger.event_target()).insert(Hovered);
-}
+fn raycast_hover_detection(
+ windows: Query<&Window, With<PrimaryWindow>>,
+ camera_query: Query<(&GlobalTransform, &Projection), With<IsometricCamera>>,
+ rapier_context: ReadRapierContext,
+ occludable: Query<(), With<Occludable>>,
+ current_hovered: Query<Entity, With<Hovered>>,
+ player_query: Query<Entity, With<Player>>,
+ mut commands: Commands,
+) {
+ let Ok(window) = windows.single() else { return };
+ let Ok((cam_gt, projection)) = camera_query.single() else { return };
+
+ let Some(cursor_pos) = window.cursor_position() else {
+ for entity in ¤t_hovered {
+ commands.entity(entity).remove::<Hovered>();
+ }
+ return;
+ };
+
+ let Projection::Orthographic(ortho) = projection else { return };
+ let viewport_height = match ortho.scaling_mode {
+ bevy::camera::ScalingMode::FixedVertical { viewport_height } => viewport_height,
+ _ => return,
+ };
+ let half_h = viewport_height / 2.0;
+ let aspect = window.width() / window.height();
+ let half_w = half_h * aspect;
+
+ let ndc_x = (cursor_pos.x / window.width()) * 2.0 - 1.0;
+ let ndc_y = 1.0 - (cursor_pos.y / window.height()) * 2.0;
+
+ let cam_tf = cam_gt.compute_transform();
+ let right = cam_tf.right().as_vec3();
+ let up = cam_tf.up().as_vec3();
+ let forward = cam_tf.forward().as_vec3();
+
+ let ray_origin = cam_tf.translation + right * (ndc_x * half_w) + up * (ndc_y * half_h);
+ let ray_dir = forward;
+
+ let mut filter = QueryFilter::new();
+ if let Ok(player_entity) = player_query.single() {
+ filter = filter.exclude_rigid_body(player_entity);
+ }
+
+ let Ok(context) = rapier_context.single() else { return };
+ let new_hovered = context
+ .cast_ray(ray_origin, ray_dir, 1000.0, false, filter)
+ .and_then(|(entity, _)| {
+ if occludable.get(entity).is_ok() {
+ Some(entity)
+ } else {
+ None
+ }
+ });
-pub(crate) fn on_pointer_out(trigger: On<Pointer<Out>>, mut commands: Commands) {
- commands.entity(trigger.event_target()).remove::<Hovered>();
+ for entity in ¤t_hovered {
+ if Some(entity) != new_hovered {
+ commands.entity(entity).remove::<Hovered>();
+ }
+ }
+ if let Some(entity) = new_hovered {
+ if current_hovered.get(entity).is_err() {
+ commands.entity(entity).insert(Hovered);
+ }
+ }
}
fn update_occlusion(
- camera_query: Query<&GlobalTransform, With<Camera3d>>,
+ camera_query: Query<&GlobalTransform, With<IsometricCamera>>,
player_query: Query<&GlobalTransform, With<Player>>,
diff --git a/apps/kbve/isometric/src-tauri/src/main.rs b/apps/kbve/isometric/src-tauri/src/main.rs
index 6000c145c..52deaaefa 100644
--- a/apps/kbve/isometric/src-tauri/src/main.rs
+++ b/apps/kbve/isometric/src-tauri/src/main.rs
@@ -3,7 +3,6 @@
use bevy::DefaultPlugins;
use bevy::app::App;
-use bevy::picking::mesh_picking::MeshPickingPlugin;
use bevy::prelude::*;
use bevy_rapier3d::prelude::*;
@@ -38,9 +37,6 @@ fn main() {
}));
- // Mesh picking backend for mouse hover detection on 3D objects
- app.add_plugins(MeshPickingPlugin);
-
// Rapier physics engine
app.add_plugins(RapierPhysicsPlugin::<NoUserData>::default());Context
Found in worktree kbve-isometric-ui-test during cleanup. Needs review against current feat/isometric-rocks branch before applying.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels