use std::time::Duration;
use bevy::{
prelude::*,
winit::cursor::{CursorIcon, CustomCursor, CustomCursorImage},
};
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_systems(
Startup,
(setup_cursor_icon, setup_camera, setup_instructions),
)
.add_systems(
Update,
(
execute_animation,
toggle_texture_atlas,
toggle_flip_x,
toggle_flip_y,
cycle_rect,
),
)
.run();
}
fn setup_cursor_icon(
mut commands: Commands,
asset_server: Res<AssetServer>,
mut texture_atlas_layouts: ResMut<Assets<TextureAtlasLayout>>,
window: Single<Entity, With<Window>>,
) {
let layout =
TextureAtlasLayout::from_grid(UVec2::splat(64), 20, 10, Some(UVec2::splat(5)), None);
let texture_atlas_layout = texture_atlas_layouts.add(layout);
let animation_config = AnimationConfig::new(0, 199, 1, 4);
commands.entity(*window).insert((
CursorIcon::Custom(CustomCursor::Image(CustomCursorImage {
handle: asset_server
.load("cursors/kenney_crosshairPack/Tilesheet/crosshairs_tilesheet_white.png"),
texture_atlas: Some(TextureAtlas {
layout: texture_atlas_layout.clone(),
index: animation_config.first_sprite_index,
}),
flip_x: false,
flip_y: false,
rect: None,
hotspot: (0, 0),
})),
animation_config,
));
}
fn setup_camera(mut commands: Commands) {
commands.spawn(Camera3d::default());
}
fn setup_instructions(mut commands: Commands) {
commands.spawn((
Text::new(
"Press T to toggle the cursor's `texture_atlas`.\n
Press X to toggle the cursor's `flip_x` setting.\n
Press Y to toggle the cursor's `flip_y` setting.\n
Press C to cycle through the sections of the cursor's image using `rect`.",
),
Node {
position_type: PositionType::Absolute,
bottom: Val::Px(12.0),
left: Val::Px(12.0),
..default()
},
));
}
#[derive(Component)]
struct AnimationConfig {
first_sprite_index: usize,
last_sprite_index: usize,
increment: usize,
fps: u8,
frame_timer: Timer,
}
impl AnimationConfig {
fn new(first: usize, last: usize, increment: usize, fps: u8) -> Self {
Self {
first_sprite_index: first,
last_sprite_index: last,
increment,
fps,
frame_timer: Self::timer_from_fps(fps),
}
}
fn timer_from_fps(fps: u8) -> Timer {
Timer::new(Duration::from_secs_f32(1.0 / (fps as f32)), TimerMode::Once)
}
}
fn execute_animation(time: Res<Time>, mut query: Query<(&mut AnimationConfig, &mut CursorIcon)>) {
for (mut config, mut cursor_icon) in &mut query {
if let CursorIcon::Custom(CustomCursor::Image(ref mut image)) = *cursor_icon {
config.frame_timer.tick(time.delta());
if config.frame_timer.finished() {
if let Some(atlas) = image.texture_atlas.as_mut() {
atlas.index += config.increment;
if atlas.index > config.last_sprite_index {
atlas.index = config.first_sprite_index;
}
config.frame_timer = AnimationConfig::timer_from_fps(config.fps);
}
}
}
}
}
fn toggle_texture_atlas(
input: Res<ButtonInput<KeyCode>>,
mut query: Query<&mut CursorIcon, With<Window>>,
mut cached_atlas: Local<Option<TextureAtlas>>, ) {
if input.just_pressed(KeyCode::KeyT) {
for mut cursor_icon in &mut query {
if let CursorIcon::Custom(CustomCursor::Image(ref mut image)) = *cursor_icon {
match image.texture_atlas.take() {
Some(a) => {
*cached_atlas = Some(a.clone());
}
None => {
if let Some(cached_a) = cached_atlas.take() {
image.texture_atlas = Some(cached_a);
}
}
}
}
}
}
}
fn toggle_flip_x(
input: Res<ButtonInput<KeyCode>>,
mut query: Query<&mut CursorIcon, With<Window>>,
) {
if input.just_pressed(KeyCode::KeyX) {
for mut cursor_icon in &mut query {
if let CursorIcon::Custom(CustomCursor::Image(ref mut image)) = *cursor_icon {
image.flip_x = !image.flip_x;
}
}
}
}
fn toggle_flip_y(
input: Res<ButtonInput<KeyCode>>,
mut query: Query<&mut CursorIcon, With<Window>>,
) {
if input.just_pressed(KeyCode::KeyY) {
for mut cursor_icon in &mut query {
if let CursorIcon::Custom(CustomCursor::Image(ref mut image)) = *cursor_icon {
image.flip_y = !image.flip_y;
}
}
}
}
fn cycle_rect(input: Res<ButtonInput<KeyCode>>, mut query: Query<&mut CursorIcon, With<Window>>) {
if !input.just_pressed(KeyCode::KeyC) {
return;
}
const RECT_SIZE: u32 = 32;
const SECTIONS: [Option<URect>; 5] = [
Some(URect {
min: UVec2::ZERO,
max: UVec2::splat(RECT_SIZE),
}),
Some(URect {
min: UVec2::new(RECT_SIZE, 0),
max: UVec2::new(2 * RECT_SIZE, RECT_SIZE),
}),
Some(URect {
min: UVec2::new(0, RECT_SIZE),
max: UVec2::new(RECT_SIZE, 2 * RECT_SIZE),
}),
Some(URect {
min: UVec2::new(RECT_SIZE, RECT_SIZE),
max: UVec2::splat(2 * RECT_SIZE),
}),
None, ];
for mut cursor_icon in &mut query {
if let CursorIcon::Custom(CustomCursor::Image(ref mut image)) = *cursor_icon {
let next_rect = SECTIONS
.iter()
.cycle()
.skip_while(|&&corner| corner != image.rect)
.nth(1) .unwrap_or(&None);
image.rect = *next_rect;
}
}
}