Rust 3D with Fyrox - Select an element
byOnce we have the basics done, and I assume you also have an official tutorial done, time to dive deep into some complex stuff.
I'm making an RTS/RPG game with a perspective camera, so everything the official Fyrox source can give me is far from what I need. Even documentation is not a helper here.
After messing around and reading, I assumed I could have mouse position from engine.user_interface.cursor_position()
. It clearly states the cursor position of the user interface. Documentation doesn't exist here despite being a public method, so I tried it.
Unfortunately, it was always (0,0)
, so this wasn't what I was looking for. The correct way to handle mouse movement is via Event.
event_loop.run(move |event, _, control_flow| {
match event {
Event::WindowEvent { event: WindowEvent::CursorMoved { position, .. } , .. } => {
game.mouse.x = position.x as f32;
game.mouse.y = position.y as f32;
}
}
});
To simplify everything and easily use it later on, it's good to store the mouse position as Vector2
.
struct Game {
// Current Scene handle
pub scene: Handle< Scene>,
// Camera handle
pub camera: Handle<Node>,
pub mouse: Vector2<f32>,
}
Once I have the cursor position, I can create a ray projected from Camera to the mouse position. We can find how to create such a ray using the mouse position in the official Fyrox book in the section camera Camera.
fn make_picking_ray(camera: &Camera, point: Vector2<f32>, renderer: &Renderer) -> Ray {
camera.make_ray(point, renderer.get_frame_bounds())
}
The Camera makes the Ray with its own position as the start position and the end position as a mouse position in 3D space where depth is the farthest Camera can see. Now time to use the Ray.
1 // Take reference to elements graph
2 let graph = &engine.scenes[self.scene].graph;
3
4 // Create the Ray
5 let ray = Self::make_picking_ray(
6 graph[self.player.camera].as_camera(),
7 self.player.controller.mouse,
8 &engine.renderer,
9 );
10
11 // Allocate the list of elements which Ray hit
12 let mut buffer = Vec::default();
13
14 // Cast the ray in the target position
15 graph.physics.cast_ray(
16 RayCastOptions {
17 // Ray start position
18 ray_origin: Point3::from(ray.origin),
19 // Ray direction
20 ray_direction: ray.dir,
21 // Ray maximum length
22 max_len: f32::MAX,
23 // Default collision
24 groups: InteractionGroups::default(),
25 // Put elements in order they were hit
26 sort_results: true,
27 },
28 &mut buffer,
29 );
30
31 if let Some(hit) = buffer.into_iter().next() {
32 // The Ray can hit only the Collider element
33 let collider = &graph[hit.collider];
34 // We want Rigid Body to add, remove and reference clicked element
35 let parent = collider.parent();
36 if parent.is_some() {
37 return;
38 }
39 let parent = &graph[parent];
40 if !parent.is_rigid_body() {
41 return;
42 }
43 // Rigid Body
44 let parent = parent.as_rigid_body();
45
46 println!("clicked {:?}", parent.name());
47
48 // DO SOMETHING
49 }
There are a couple of things which need to be highlighted here. Variable hit
is Intersection
which has nothing to do with Result
. This structure references collider
and distance from the Camera to the element (toi
). We will use it to find the component of the graph
. In line 33, we found the Collider
for the element we want to modify. Then from lines 35 to 44, we retrieve Rigid Body from the graph
.