Aim Towards the Mouse
We're making a shooty game. Not sure if you knew that, but we are. It's nice to be able to aim when shooty. If you went on that side quest for me, now it's time for something much easier
Collecting our mouse position
For now we're going to bloat our update_player_input system a little bit in lib.rs. That's why we did the refactor last chapter, to give us room to be lazy here.
We add aim_position to our PlayerInput component. Naming is important because, eventually, we'll support updating this position with... other methods. Like a controller.
#[derive(Default)]
#[resource]
struct PlayerInput {
move_axes: Vec2,
aim_position: Vec2,
}
#[system]
fn update_player_input(
mut player_input: Mut<PlayerInput>,
input: Ref<InputState>,
camera: Query<(Ref<Transform>, Ref<Camera>)>,
window: Ref<Window>,
) {
let key_to_f32 = |key_code| {
if input.keys[key_code].pressed() {
1.0
} else {
0.0
}
};
player_input.move_axes = Vec2::new(
key_to_f32(KeyCode::KeyD) - key_to_f32(KeyCode::KeyA),
key_to_f32(KeyCode::KeyW) - key_to_f32(KeyCode::KeyS),
)
.clamp_length_max(1.);
//New camera stuff:
let Some((camera_transform, camera)) = camera.get(0) else {
return;
};
let normalized_cursor_position = input.mouse.cursor_position / window.size - 0.5;
let CameraAspect::FixedVirtualHeight { height } = camera.aspect else {
return;
};
let scale = Vec2::new(window.size.x * (height / window.size.y), -height);
let view_cursor_position = normalized_cursor_position * scale;
player_input.aim_position = view_cursor_position + camera_transform.position;
}
The engine makes this pretty convenient. The InputState resource keeps track of our mouse's cursor position. It's up to us to adjust it for where in the current window it is.
Hurray, now you have an aim position!
We probably want to make sure it works, though, so let's add a reticle.
Adding a Reticle
We're spawning something, so what time is it? That's right, it's time for a new function in our spawn_systems.rs. You could use a reticle image and assign it as a texture to your reticle, but I want to create it using in-engine shape tools. Actually right now we can't disable the OS cursor, so you might not want to use this for any reason besides debugging. I also want to make the reticle be a gravity and light source because I think it'll be neat. If you didn't go on our recent sidequest you won't be able to participate on that part.
First we'll add a Reticle component to our growing list of components. It doesn't need any information so it can also just be another tag component. We'll also need to call our spawn-reticle function that we're just about to write.
#[component]
struct Reticle;
#[system_once]
fn spawn_everything(engine: Ref<Engine>) {
spawn_grid_dots(&engine);
spawn_camera(&engine);
spawn_player(&engine);
spawn_enemy(&engine);
spawn_reticle(&engine);
}
pub fn spawn_reticle(engine: &Engine) {
let transform = &Transform {
layer: 100., // We want this to show up above everything else.
..Default::default()
};
let color = &Color::new(0.8, 0.6, 0.2, 1.0);
let circle_render = &CircleRender {
num_sides: 7,
size: Vec2::splat(15.0),
visible: true,
};
let light = &LightSource {
falloff_distance: 100.,
strength: 30.,
falloff_power: 1.5,
};
let grid_gravity = &GravitySource {
strength: -80.,
falloff_distance: 50.,
falloff_power: 0.5,
};
engine.spawn(bundle!(
transform,
color,
circle_render,
light,
grid_gravity,
&Reticle
));
}
Of course, just spawning it isn't enough, so let's add in a system to finish the job!
#[system]
fn update_reticle_position_system(
mut reticle_query: Query<(Mut<Transform>, Ref<Reticle>)>,
player_input: Ref<PlayerInput>,
) {
let Some((mut transform, _)) = reticle_query.get_mut(0) else {
return;
};
transform.position = player_input.aim_position;
}
And that's it! Enjoy playing around with the gravity and light system with your cursor, or at least dragging your cursor around. Don't get too caught up, though, it's time to make our shooty game go shoot!