Skip to main content

rotate_to_cursor/
rotate_to_cursor.rs

1//! Demonstrates rotating sprites to face the cursor.
2
3use bevy::prelude::*;
4use std::f32::consts::FRAC_PI_2;
5
6fn main() {
7    App::new()
8        .add_plugins(DefaultPlugins)
9        .add_systems(Startup, setup)
10        .add_systems(FixedUpdate, player_movement_system)
11        .run();
12}
13
14/// Player component
15#[derive(Component)]
16struct Player;
17
18/// Add the game's entities to our world and create an orthographic camera for 2D rendering.
19///
20/// The Bevy coordinate system is the same for 2D and 3D, in terms of 2D this means that:
21///
22/// * `X` axis goes from left to right (`+X` points right)
23/// * `Y` axis goes from bottom to top (`+Y` point up)
24/// * `Z` axis goes from far to near (`+Z` points towards you, out of the screen)
25///
26/// The world origin in this case is at the center of the screen, but the camera could
27/// move in which case the world origin would not be the center of the screen
28fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
29    let ship_handle = asset_server.load("textures/simplespace/ship_C.png");
30
31    commands.spawn(Camera2d);
32
33    // Player controlled ship
34    commands.spawn((Sprite::from_image(ship_handle), Player));
35}
36
37/// Demonstrates applying rotation and movement based on keyboard input.
38fn player_movement_system(
39    mut player: Single<&mut Transform, With<Player>>,
40    camera_query: Single<(&Camera, &GlobalTransform)>,
41    window: Single<&Window>,
42) {
43    let (camera, camera_transform) = *camera_query;
44
45    if let Some(cursor_position) = window.cursor_position()
46        // Calculate a world position based on the cursor's position.
47        && let Ok(cursor_world_pos) = camera.viewport_to_world_2d(camera_transform, cursor_position)
48    {
49        // The angle an entity needs to rotate to face a point is defined
50        // by the vector between the two points (Vec2 - Vec2), which we can then
51        // turn into radians using to_angle.
52        //
53        // FRAC_PI_2 is because our sprite's ship is facing "up" so we rotate it an additional 90 degrees
54        // so that it faces the cursor.
55        player.rotation = Quat::from_rotation_z(
56            (cursor_world_pos - player.translation.xy()).to_angle() - FRAC_PI_2,
57        );
58    }
59}