How to create a custom cursor? #8613
-
I'm trying to create a custom cursor but am hitting a roadblock. I am able to hide the default cursor and create an ImageBundle to represent my new cursor - as there doesn't seem to be a way to specify the default cursor as an image this seems to be the only way. My function to move the cursor looks like this: fn move_cursor(window: Query<&Window>, mut cursor_state: Query<(&mut Transform, &mut Movable)>) {
let window: &Window = window.single();
if let Some(position) = window.cursor_position() {
for (mut transform, mut cursor) in &mut cursor_state {
transform.translation = Vec3::new(position.x, position.y, 0.0);
}
}
} I have a query to get the window and retrieve the cursor position, then I grab the transform and cursor to set the translation to a new value. These functions are being called by #[derive(Component)]
struct Movable {
spawn: Vec3,
}
impl Movable {
fn new(spawn: Vec3) -> Self {
Movable { spawn }
}
} I am also drawing the cursor inside a fn setup_cursor(
mut windows: Query<&mut Window>,
mut commands: Commands,
asset_server: Res<AssetServer>,
) {
let mut window: Mut<Window> = windows.single_mut();
window.cursor.visible = false;
let cursor_spawn: Vec3 = Vec3::ZERO;
commands.spawn((
ImageBundle {
image: asset_server.load("cursors/point.png").into(),
style: Style {
//display: Display::None,
position_type: PositionType::Absolute,
..default()
},
z_index: ZIndex::Local(1),
transform: Transform::from_translation(cursor_spawn),
..default()
},
Movable::new(cursor_spawn)
));
} My intention is to hide the cursor until the cursor moves into the window and then update the style on the Have been trying to follow the transform example but I'm not sure how to proceed from here. Does anyone have any ideas? |
Beta Was this translation helpful? Give feedback.
Replies: 5 comments 5 replies
-
I got it working. Changed my code to look like this: #[derive(Component)]
struct GameCursor {}
fn setup_cursor(
mut windows: Query<&mut Window>,
mut commands: Commands,
asset_server: Res<AssetServer>,
) {
let mut window: Mut<Window> = windows.single_mut();
window.cursor.visible = false;
let cursor_spawn: Vec3 = Vec3::ZERO;
commands.spawn((
ImageBundle {
image: asset_server.load("cursors/point.png").into(),
style: Style {
//display: Display::None,
position_type: PositionType::Absolute,
position: UiRect::all(Val::Auto),
..default()
},
z_index: ZIndex::Global(15),
transform: Transform::from_translation(cursor_spawn),
..default()
},
GameCursor {}
));
}
fn move_cursor(window: Query<&Window>, mut cursor: Query<&mut Style, With<GameCursor>>) {
let window: &Window = window.single();
if let Some(position) = window.cursor_position() {
let mut img_style = cursor.single_mut();
img_style.position.left = Val::Px(position.x);
img_style.position.bottom = Val::Px(position.y);
}
} Seems like ImageBundle get there x y coordinates from I then attached a reference to the |
Beta Was this translation helpful? Give feedback.
-
for those who are interested I had to apply an offset to get it to align with the default Windows cursor. img_style.position.left = Val::Px(position.x - 2.0);
img_style.position.bottom = Val::Px(position.y - 24.0); Not sure how this will impact other systems and resolutions |
Beta Was this translation helpful? Give feedback.
-
nah bru this aint working |
Beta Was this translation helpful? Give feedback.
-
Tried this and I've found there's a significant lag between the actual cursor and the display image ... has to be multiple frames |
Beta Was this translation helpful? Give feedback.
-
I got it working on 0.15.0, here is the code: use bevy::prelude::*;
use bevy::render::view::RenderLayers;
use bevy::window::PrimaryWindow;
use crate::game::MainCamera;
use crate::layers::{CURSOR_LAYER};
pub(crate) struct CursorPlugin;
#[derive(Component)]
struct GameCursor {}
impl Plugin for CursorPlugin {
fn build(&self, app: &mut App) {
app
.add_systems(Startup, setup)
.add_systems(Update, (update_sprite_position_on_resize));
}
}
fn setup(
mut commands: Commands,
asset_server: Res<AssetServer>,
mut texture_atlas_layouts: ResMut<Assets<TextureAtlasLayout>>,
mut q_window: Query<&Window, With<PrimaryWindow>>) {
let texture = asset_server.load("cursors/normal.png");
let layout = TextureAtlasLayout::from_grid(UVec2::new(32,32), 1, 1, None, None);
let texture_atlas_layout = texture_atlas_layouts.add(layout);
let mut spawnpos = Vec3::new(0.0, 0.0, 101.0);
if let Some(position) = q_window.single().cursor_position() {
spawnpos = Vec3::new(position.x, position.y, 0.0);
}
commands.spawn((
Sprite::from_atlas_image(
texture,
TextureAtlas {
layout: texture_atlas_layout,
index: 0,
},
),
Transform::from_translation(spawnpos),
RenderLayers::layer(CURSOR_LAYER),
GameCursor {},
));
}
fn update_sprite_position_on_resize(
q_window: Query<&Window, With<PrimaryWindow>>,
q_camera: Query<(&Camera, &GlobalTransform), With<MainCamera>>,
mut query: Query<(&mut Transform, &Sprite), With<GameCursor>>,
) {
if let Some(window) = q_window.iter().next() {
let left_margin = 16.0;
let top_margin = 16.0;
let mut mouse_position_x = 0.0;
let mut mouse_position_y = 0.0;
if let Some(position) = q_window.single().cursor_position() {
mouse_position_x = position.x + left_margin;
mouse_position_y = position.y + top_margin;
}
if let Some((camera, camera_transform)) = q_camera.iter().next() {
let camera_position = camera_transform.translation();
for (mut transform, _sprite) in query.iter_mut() {
transform.translation = Vec3::new(
camera_position.x - window.width() / 2.0 + mouse_position_x,
camera_position.y + window.height() / 2.0 - mouse_position_y,
transform.translation.z,
);
}
}
}
} |
Beta Was this translation helpful? Give feedback.
I got it working on 0.15.0, here is the code: