Mouse Input
Mouse input is usually used to control a camera rotation, to pick objects in game world, etc. There are two major ways to get the mouse state - the simple and the event-based ones.
Mouse Motion
The following example shows how to use raw mouse events to rotate an object. It could also be used to rotate a camera in your game (with slight modifications).
#![allow(unused)] fn main() { #[derive(Clone, Debug, Reflect, Visit, TypeUuidProvider, ComponentProvider)] #[type_uuid(id = "abbad54c-e267-4d7e-a3cd-e125a7e87ff0")] #[visit(optional)] pub struct Player { yaw: f32, pitch: f32, } impl ScriptTrait for Player { fn on_update(&mut self, ctx: &mut ScriptContext) { let mouse_speed = ctx.input_state.mouse_speed(); let limit = std::f32::consts::FRAC_PI_2; self.pitch = (self.pitch + mouse_speed.y).clamp(-limit, limit); self.yaw += mouse_speed.x; let node = &mut ctx.scene.graph[ctx.handle]; let transform = node.local_transform_mut(); transform.set_rotation( UnitQuaternion::from_axis_angle(&Vector3::x_axis(), self.pitch) * UnitQuaternion::from_axis_angle(&Vector3::y_axis(), self.yaw), ); } } }
The same can be done with an event-based approach:
#![allow(unused)] fn main() { #[derive(Clone, Debug, Reflect, Visit, TypeUuidProvider, ComponentProvider)] #[type_uuid(id = "abbad54c-e267-4d7e-a3cd-e125a7e87ff0")] #[visit(optional)] pub struct Player { yaw: f32, pitch: f32, } impl ScriptTrait for Player { fn on_os_event(&mut self, event: &Event<()>, _ctx: &mut ScriptContext) { // We'll listen to MouseMotion raw device event to rotate an object. It provides // offsets only. if let Event::DeviceEvent { event: DeviceEvent::MouseMotion { delta: (dx, dy), .. }, .. } = event { let limit = std::f32::consts::FRAC_PI_2; self.pitch = (self.pitch + *dy as f32).clamp(-limit, limit); self.yaw += *dx as f32; } } fn on_update(&mut self, ctx: &mut ScriptContext) { let node = &mut ctx.scene.graph[ctx.handle]; let transform = node.local_transform_mut(); transform.set_rotation( UnitQuaternion::from_axis_angle(&Vector3::x_axis(), self.pitch) * UnitQuaternion::from_axis_angle(&Vector3::y_axis(), self.yaw), ); } } }
This example consists of two main parts - on_os_event
and on_update
methods. The first one is called when some
event comes to the main window, and we need to check if this event is DeviceEvent::MouseMotion
. After that, we're taking
relative offsets (dx
, dy
) and modifying the pitch
, yaw
variables accordingly. on_update
method is called
every frame and it is used to apply pitch
and yaw
values to the scene node the script is assigned to.
Mouse Buttons
The following example shows how to handle events from mouse buttons.
#![allow(unused)] fn main() { #[derive(Clone, Debug, Reflect, Visit, TypeUuidProvider, ComponentProvider)] #[type_uuid(id = "abbad54c-e267-4d7e-a3cd-e125a7e87ff1")] #[visit(optional)] pub struct Clicker { counter: i32, } impl ScriptTrait for Clicker { fn on_update(&mut self, ctx: &mut ScriptContext) { if ctx.input_state.is_left_mouse_button_pressed() { self.counter += 1; } else if ctx.input_state.is_left_mouse_button_released() { self.counter -= 1; } } } }
The same can be done with an event-based approach:
#![allow(unused)] fn main() { #[derive(Clone, Debug, Reflect, Visit, TypeUuidProvider, ComponentProvider)] #[type_uuid(id = "abbad54c-e267-4d7e-a3cd-e125a7e87ff1")] #[visit(optional)] pub struct Clicker { counter: i32, } impl ScriptTrait for Clicker { fn on_os_event(&mut self, event: &Event<()>, _ctx: &mut ScriptContext) { if let Event::WindowEvent { event: WindowEvent::MouseInput { button, state, .. }, .. } = event { if *state == ElementState::Pressed { match *button { MouseButton::Left => { self.counter -= 1; } MouseButton::Right => { self.counter += 1; } _ => (), } } } } } }
At first, we're checking for WindowEvent::MouseInput
and creating respective bindings to its internals (button
, state
)
and then all we need to do, is to check if the button was pressed and if so, which one.