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.