Ported to bevy 0.15

master
Wynd 2025-01-02 22:15:03 +02:00
parent b72d550b4d
commit ea93d50e44
12 changed files with 1068 additions and 786 deletions

1291
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -2,11 +2,11 @@ cargo-features = ["codegen-backend"]
[package] [package]
name = "avoid-rs" name = "avoid-rs"
version = "0.1.0" version = "0.2.0"
edition = "2021" edition = "2021"
[dependencies] [dependencies]
bevy = { version = "0.14", features = ["wav"] } bevy = { version = "0.15", features = ["wav"] }
fastrand = "*" fastrand = "*"
[profile.dev] [profile.dev]

View File

@ -10,39 +10,41 @@ impl Plugin for AnimationPlugin {
} }
} }
#[derive(Bundle, Default)]
pub struct AnimationBundle {
pub timer: AnimationTimer,
pub atlas: TextureAtlas,
pub indices: AnimationIndices,
}
#[derive(Component, Default)] #[derive(Component, Default)]
pub struct AnimationIndices { pub struct SpriteAnimation {
pub first_index: usize, pub first: usize,
pub last_index: usize, pub last: usize,
pub timer: Timer,
} }
#[derive(Component, Default, Deref, DerefMut)] impl SpriteAnimation {
pub struct AnimationTimer(pub Timer); pub fn new(last: usize, timer: Timer) -> Self {
Self {
first: 0,
last,
timer,
}
}
}
// #[derive(Component, Default, Deref, DerefMut)]
// pub struct AnimationTimer(pub Timer);
fn animation_tick( fn animation_tick(
time: Res<Time>, time: Res<Time>,
mut query: Query<( mut query: Query<(&mut Sprite, &mut SpriteAnimation, &Velocity)>,
&mut AnimationTimer,
&mut TextureAtlas,
&AnimationIndices,
&Velocity,
)>,
) { ) {
for (mut timer, mut atlas, anim_indicies, velocity) in &mut query { for (mut sprite, mut anim, velocity) in &mut query {
let Some(atlas) = &mut sprite.texture_atlas
else {
continue;
};
if velocity.length() > 0.0 { if velocity.length() > 0.0 {
timer.tick(time.delta()); anim.timer.tick(time.delta());
if timer.just_finished() { if anim.timer.just_finished() {
atlas.index = if atlas.index == anim_indicies.last_index { atlas.index = if atlas.index == anim.last {
anim_indicies.first_index anim.first
} }
else { else {
atlas.index + 1 atlas.index + 1

View File

@ -7,6 +7,7 @@ impl Plugin for PathFollow2DPlugin {
} }
#[derive(Component, Default)] #[derive(Component, Default)]
#[require(Transform)]
pub struct PathFollow2D { pub struct PathFollow2D {
pub progress: f32, pub progress: f32,
@ -82,10 +83,3 @@ impl PathFollow2D {
(Vec2::default(), Vec2::default()) (Vec2::default(), Vec2::default())
} }
} }
#[derive(Bundle, Default)]
pub struct PathFollow2DBundle {
pub path: PathFollow2D,
pub transform: Transform,
}

View File

@ -13,7 +13,7 @@ pub struct Velocity(pub Vec2);
fn apply_velocity(time: Res<Time>, mut query: Query<(&mut Transform, &Velocity)>) { fn apply_velocity(time: Res<Time>, mut query: Query<(&mut Transform, &Velocity)>) {
for (mut transform, velocity) in &mut query { for (mut transform, velocity) in &mut query {
transform.translation.x += velocity.x * time.delta_seconds(); transform.translation.x += velocity.x * time.delta_secs();
transform.translation.y += velocity.y * time.delta_seconds(); transform.translation.y += velocity.y * time.delta_secs();
} }
} }

View File

@ -1,6 +1,6 @@
use bevy::{color::palettes::css::RED, prelude::*}; use bevy::prelude::*;
use crate::common::velocity::VelocityPlugin; use crate::{common::animation::SpriteAnimation, level::Unit};
pub struct EnemyPlugin; pub struct EnemyPlugin;
@ -13,14 +13,15 @@ impl Plugin for EnemyPlugin {
} }
#[derive(Component)] #[derive(Component)]
#[require(Unit, SpriteAnimation)]
pub struct Enemy; pub struct Enemy;
fn debug_direction(query: Query<&Transform, With<Enemy>>, mut gizmos: Gizmos) { fn debug_direction(query: Query<&Transform, With<Enemy>>, gizmos: Gizmos) {
for enemy in &query { // for enemy in &query {
// let start = enemy.translation.truncate(); // let start = enemy.translation.truncate();
// let end = enemy.rotation.xyz().truncate() - start; // let end = start + Vec2::new(10.0, 0.0);
// gizmos.arrow_2d(start, end, RED); // gizmos.arrow_2d(start, end, RED);
} // }
} }
fn outside_bounds( fn outside_bounds(

View File

@ -1,7 +1,4 @@
use bevy::{ use bevy::{input::gamepad::GamepadEvent, prelude::*};
input::gamepad::{GamepadConnection, GamepadEvent},
prelude::*,
};
pub struct GamepadPlugin; pub struct GamepadPlugin;
@ -15,7 +12,7 @@ impl Plugin for GamepadPlugin {
pub struct GamepadOne(pub Gamepad); pub struct GamepadOne(pub Gamepad);
fn setup( fn setup(
mut commands: Commands, commands: Commands,
gamepad1: Option<Res<GamepadOne>>, gamepad1: Option<Res<GamepadOne>>,
mut gamepad_events: EventReader<GamepadEvent>, mut gamepad_events: EventReader<GamepadEvent>,
) { ) {
@ -25,19 +22,19 @@ fn setup(
continue; continue;
}; };
match &conn.connection { // match &conn.connection {
GamepadConnection::Connected(info) => { // GamepadConnection::Connected(_) => {
if gamepad1.is_none() { // if gamepad1.is_none() {
commands.insert_resource(GamepadOne(conn.gamepad)); // commands.insert_resource(GamepadOne(conn.gamepad));
} // }
} // }
GamepadConnection::Disconnected => { // GamepadConnection::Disconnected => {
if let Some(GamepadOne(id)) = gamepad1.as_deref() { // if let Some(GamepadOne(id)) = gamepad1.as_deref() {
if *id == conn.gamepad { // if *id == conn.gamepad {
commands.remove_resource::<GamepadOne>(); // commands.remove_resource::<GamepadOne>();
} // }
} // }
} // }
} // }
} }
} }

View File

@ -1,14 +1,6 @@
use std::path::Path; use bevy::{asset::AssetPath, prelude::*};
use bevy::{ use crate::{player::Player, score::Score, GameState, START_POSITION};
asset::{io::AssetSourceId, AssetPath},
ecs::query::QueryIter,
prelude::*,
};
use crate::{
gamepad::GamepadOne, player::Player, score::Score, GameStartEvent, GameState, START_POSITION,
};
pub struct HUDPlugin; pub struct HUDPlugin;
@ -22,7 +14,7 @@ impl Plugin for HUDPlugin {
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) { fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
// let asset_path: AssetPath = "embedded://avoid_rs/assets/Xolonium-Regular.ttf".into(); // let asset_path: AssetPath = "embedded://avoid_rs/assets/Xolonium-Regular.ttf".into();
let asset_path: AssetPath = "Xolonium-Regular.ttf".into(); let asset_path: AssetPath = "Xolonium-Regular.ttf".into();
let font: Handle<_> = asset_server.load(asset_path); let font: Handle<Font> = asset_server.load(asset_path);
// let path = Path::new("avoid-rs").join("assets/Xolonium-Regular.ttf"); // let path = Path::new("avoid-rs").join("assets/Xolonium-Regular.ttf");
// let source = AssetSourceId::from("embedded"); // let source = AssetSourceId::from("embedded");
@ -31,132 +23,104 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
// score UI, top middle // score UI, top middle
commands commands
.spawn(NodeBundle { .spawn((Node {
style: Style { width: Val::Percent(100.0),
width: Val::Percent(100.0), align_items: AlignItems::Center,
align_items: AlignItems::Center, justify_content: JustifyContent::Center,
justify_content: JustifyContent::Center, ..Default::default()
},))
.with_child((
ScoreText,
Text::new("0"),
TextFont {
font: font.clone(),
font_size: 64.0,
..Default::default() ..Default::default()
}, },
..Default::default() TextLayout::new_with_justify(JustifyText::Center),
}) ));
.with_children(|parent| {
parent.spawn((
TextBundle::from_section(
"0",
TextStyle {
font: font.clone(),
font_size: 64.0,
..Default::default()
},
)
.with_text_justify(JustifyText::Center)
.with_style(Style {
justify_content: JustifyContent::Center,
align_items: AlignItems::Center,
..Default::default()
}),
ScoreText,
));
});
// game info, middle of the screen
commands commands
.spawn(NodeBundle { .spawn((
style: Style { IntroMessageText,
Node {
width: Val::Percent(100.0), width: Val::Percent(100.0),
height: Val::Percent(100.0), height: Val::Percent(100.0),
align_items: AlignItems::Center, align_items: AlignItems::Center,
justify_content: JustifyContent::Center, justify_content: JustifyContent::Center,
..Default::default() ..Default::default()
}, },
..Default::default() ))
}) .with_child((
.with_children(|parent| { Text::new("Dodge the creeps!"),
// game info, middle of the screen TextFont {
parent.spawn(( font: font.clone(),
TextBundle::from_section( font_size: 64.0,
"Dodge the creeps!", ..Default::default()
TextStyle { },
font: font.clone(), TextLayout::new_with_justify(JustifyText::Center),
font_size: 64.0, ));
..Default::default()
},
)
.with_text_justify(JustifyText::Center)
.with_style(Style {
justify_content: JustifyContent::Center,
align_items: AlignItems::Center,
..Default::default()
}),
IntroMessageText,
StartMenuUI,
));
});
commands commands
.spawn(NodeBundle { .spawn((
style: Style { StartMenuUI,
Node {
width: Val::Percent(100.0), width: Val::Percent(100.0),
height: Val::Percent(90.0), height: Val::Percent(90.0),
align_items: AlignItems::End, align_items: AlignItems::End,
justify_content: JustifyContent::Center, justify_content: JustifyContent::Center,
..Default::default() ..Default::default()
}, },
..Default::default() ))
})
.with_children(|parent| { .with_children(|parent| {
parent parent
.spawn(( .spawn((
ButtonBundle {
style: Style {
width: Val::Px(200.0),
height: Val::Px(100.0),
border: UiRect::all(Val::Px(5.0)),
// horizontally center child text
justify_content: JustifyContent::Center,
// vertically center child text
align_items: AlignItems::Center,
..default()
},
border_color: BorderColor(Color::BLACK),
border_radius: BorderRadius::all(Val::Px(10.0)),
background_color: Color::srgb(0.15, 0.15, 0.15).into(),
..default()
},
StartButton, StartButton,
StartMenuUI, Node {
width: Val::Px(200.0),
height: Val::Px(100.0),
border: UiRect::all(Val::Px(5.0)),
justify_content: JustifyContent::Center,
align_items: AlignItems::Center,
..Default::default()
},
BorderColor(Color::BLACK),
BorderRadius::all(Val::Px(10.0)),
BackgroundColor::from(Color::srgb(0.15, 0.15, 0.15)),
)) ))
.with_children(|parent| { .with_child((
parent.spawn(TextBundle::from_section( Text::new("Start"),
"Start", TextFont {
TextStyle { font: font.clone(),
font: font.clone(), font_size: 64.0,
font_size: 64.0, ..Default::default()
color: Color::srgb(0.9, 0.9, 0.9), },
}, TextColor::from(Color::srgb(0.9, 0.9, 0.9)),
)); ));
});
}); });
} }
#[derive(Component)] #[derive(Component, Default)]
#[require(Text)]
pub struct ScoreText; pub struct ScoreText;
#[derive(Component)] #[derive(Component)]
#[require(StartMenuUI, Text)]
pub struct IntroMessageText; pub struct IntroMessageText;
#[derive(Component)] #[derive(Component)]
#[require(StartMenuUI, Button)]
pub struct StartButton; pub struct StartButton;
#[derive(Component)] #[derive(Component, Default)]
pub struct StartMenuUI; pub struct StartMenuUI;
fn start_button_logic( fn start_button_logic(
current_state: Res<State<GameState>>, current_state: Res<State<GameState>>,
mut state: ResMut<NextState<GameState>>, mut state: ResMut<NextState<GameState>>,
mut score: ResMut<Score>, mut score: ResMut<Score>,
gamepads: Res<ButtonInput<GamepadButton>>, gamepads: Query<&Gamepad>,
gamepad1: Option<Res<GamepadOne>>,
mut button: Query<(&Interaction, &mut BorderColor), With<StartButton>>, mut button: Query<(&Interaction, &mut BorderColor), With<StartButton>>,
mut player_query: Query<(&mut Transform, &mut Visibility), With<Player>>, mut player_query: Query<(&mut Transform, &mut Visibility), With<Player>>,
mut ui_elems_query: Query<&mut Visibility, (With<StartMenuUI>, Without<Player>)>, mut ui_elems_query: Query<&mut Visibility, (With<StartMenuUI>, Without<Player>)>,
@ -169,25 +133,22 @@ fn start_button_logic(
let mut score_text = score_text_query.single_mut(); let mut score_text = score_text_query.single_mut();
let (mut player_transform, mut player_visibility) = player_query.single_mut(); let (mut player_transform, mut player_visibility) = player_query.single_mut();
if let Some(gamepad) = gamepad1.as_deref() { let gamepad = gamepads.single();
let start_btn = GamepadButton {
gamepad: gamepad.0,
button_type: GamepadButtonType::Start,
};
if gamepads.pressed(start_btn) { if gamepad.just_pressed(GamepadButton::Start) {
start_game( start_game(
&mut score_text, &mut score_text,
&mut score, &mut score,
&mut state, &mut state,
&mut ui_elems_query, &mut ui_elems_query,
&mut player_visibility, &mut player_visibility,
&mut player_transform, &mut player_transform,
); );
} return;
} }
let (interaction, mut border_color) = button.single_mut(); let (interaction, mut border_color) = button.single_mut();
match *interaction { match *interaction {
Interaction::Pressed => { Interaction::Pressed => {
start_game( start_game(
@ -213,7 +174,7 @@ fn start_game(
player_transform: &mut Transform, player_transform: &mut Transform,
) { ) {
score.0 = 0; score.0 = 0;
score_text.sections[0].value = format!("{}", score.0); score_text.0 = format!("{}", score.0);
state.set(GameState::Playing); state.set(GameState::Playing);

View File

@ -1,20 +1,19 @@
use std::f32::consts::PI; use std::f32::consts::PI;
use bevy::{ use bevy::{
color::palettes::css::{LIME, RED}, math::bounding::{Aabb2d, BoundingCircle, IntersectsVolume},
math::bounding::{Aabb2d, BoundingCircle, BoundingVolume, IntersectsVolume},
prelude::*, prelude::*,
}; };
// use rand::Rng; // use rand::Rng;
use crate::{ use crate::{
common::{ common::{
animation::{AnimationBundle, AnimationIndices, AnimationPlugin, AnimationTimer}, animation::{AnimationPlugin, SpriteAnimation},
path_follow::{PathFollow2D, PathFollow2DBundle, PathFollow2DPlugin}, path_follow::{PathFollow2D, PathFollow2DPlugin},
velocity::{Velocity, VelocityPlugin}, velocity::{Velocity, VelocityPlugin},
}, },
enemy::{Enemy, EnemyPlugin}, enemy::{Enemy, EnemyPlugin},
hud::{HUDPlugin, ScoreText, StartMenuUI}, hud::{HUDPlugin, StartMenuUI},
player::{Player, PlayerPlugin}, player::{Player, PlayerPlugin},
score::{Score, ScorePlugin}, score::{Score, ScorePlugin},
GameOverEvent, GameState, GameOverEvent, GameState,
@ -42,35 +41,26 @@ impl Plugin for LevelPlugin {
} }
fn setup(mut commands: Commands) { fn setup(mut commands: Commands) {
commands.spawn(PathFollow2DBundle { commands.spawn(PathFollow2D {
path: PathFollow2D { progress: 0.0,
progress: 0.0, points: vec![
points: vec![ Vec2::new(0.0, 720.0),
Vec2::new(0.0, 720.0), Vec2::new(480.0, 720.0),
Vec2::new(480.0, 720.0), Vec2::new(480.0, 0.0),
Vec2::new(480.0, 0.0), Vec2::new(0.0, 0.0),
Vec2::new(0.0, 0.0), ],
], looping: true,
looping: true,
..Default::default()
},
..Default::default()
}); });
} }
#[derive(Resource, Deref, DerefMut)] #[derive(Resource, Deref, DerefMut)]
struct SpawnTimer(Timer); struct SpawnTimer(Timer);
#[derive(Bundle, Default)] #[derive(Component, Default)]
pub struct UnitBundle { #[require(Sprite, Velocity, Collider)]
pub sprite: SpriteBundle, pub struct Unit;
pub velocity: Velocity, #[derive(Component, Default)]
pub animator: AnimationBundle,
}
#[derive(Component)]
pub struct Collider; pub struct Collider;
#[derive(Event, Default)] #[derive(Event, Default)]
@ -115,43 +105,33 @@ fn spawn_enemy(
commands.spawn(( commands.spawn((
Enemy, Enemy,
Collider, Transform {
UnitBundle { translation: spawn_pos.extend(1.0),
sprite: SpriteBundle { rotation: direction,
transform: Transform { scale: Vec2::new(0.75, 0.75).extend(1.0),
translation: spawn_pos.extend(1.0),
rotation: direction,
scale: Vec2::new(0.75, 0.75).extend(1.0),
},
texture: asset_server.load(idx),
..Default::default()
},
animator: AnimationBundle {
atlas: TextureAtlas {
layout: texture_atlas_layouts,
index: 0,
},
timer: AnimationTimer(Timer::from_seconds(0.3, TimerMode::Repeating)),
indices: AnimationIndices {
first_index: 0,
last_index: 1,
},
..Default::default()
},
velocity: Velocity(velocity),
}, },
Sprite {
image: asset_server.load(idx),
texture_atlas: Some(TextureAtlas {
layout: texture_atlas_layouts,
index: 0,
}),
..Default::default()
},
SpriteAnimation::new(1, Timer::from_seconds(0.3, TimerMode::Repeating)),
Velocity(velocity),
)); ));
} }
} }
fn check_for_collisions( fn check_for_collisions(
mut commands: Commands, commands: Commands,
mut score: ResMut<Score>, score: ResMut<Score>,
mut player_query: Query<(&mut Velocity, &Transform), With<Player>>, mut player_query: Query<(&mut Velocity, &Transform), With<Player>>,
collider_query: Query<(Entity, &Transform), (With<Collider>, With<Enemy>)>, collider_query: Query<(Entity, &Transform), (With<Collider>, With<Enemy>)>,
mut game_over_events: EventWriter<GameOverEvent>, mut game_over_events: EventWriter<GameOverEvent>,
) { ) {
let (mut player_velocity, player_transform) = player_query.single_mut(); let (player_velocity, player_transform) = player_query.single_mut();
for (collider_entity, collider_transform) in &collider_query { for (collider_entity, collider_transform) in &collider_query {
let collision = player_collision( let collision = player_collision(
@ -193,11 +173,10 @@ fn game_over(
commands.entity(enemy).despawn(); commands.entity(enemy).despawn();
} }
commands.spawn(AudioBundle { commands.spawn((
source: asset_server.load("gameover.wav"), AudioPlayer::new(asset_server.load("gameover.wav")),
// auto-despawn the entity when playback finishes PlaybackSettings::DESPAWN,
settings: PlaybackSettings::DESPAWN, ));
});
} }
fn player_collision(player: BoundingCircle, bounding_box: Aabb2d) -> bool { fn player_collision(player: BoundingCircle, bounding_box: Aabb2d) -> bool {
@ -208,7 +187,7 @@ fn player_collision(player: BoundingCircle, bounding_box: Aabb2d) -> bool {
true true
} }
fn debug_gizmos(mut gizmos: Gizmos, query: Query<&PathFollow2D>) { fn debug_gizmos(gizmos: Gizmos, query: Query<&PathFollow2D>) {
for path in &query { for path in &query {
// let x = path.points.len(); // let x = path.points.len();
// for i in 0..x { // for i in 0..x {

View File

@ -1,18 +1,7 @@
use std::default; use bevy::{diagnostic::FrameTimeDiagnosticsPlugin, prelude::*, window::WindowResolution};
use bevy::{
asset::{embedded_asset, embedded_path},
diagnostic::FrameTimeDiagnosticsPlugin,
prelude::*,
render::camera::Viewport,
window::{WindowMode, WindowResolution},
};
use common::mouse::{Mouse, MousePlugin}; use common::mouse::{Mouse, MousePlugin};
use gamepad::GamepadPlugin; use gamepad::GamepadPlugin;
use hud::HUDPlugin;
use level::LevelPlugin; use level::LevelPlugin;
use player::PlayerPlugin;
use score::ScorePlugin;
mod common; mod common;
mod enemy; mod enemy;
@ -42,25 +31,10 @@ struct GameOverEvent;
struct GameStartEvent; struct GameStartEvent;
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) { fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
commands.spawn(Camera2dBundle { commands.spawn((
camera: Camera { Camera2d,
viewport: Some(Viewport { Transform::from_xyz((SCREEN_WIDTH / 2) as f32, (SCREEN_HEIGHT / 2) as f32, 1.0),
physical_position: UVec2::new(0, 0), ));
physical_size: UVec2::new(480, 720),
..Default::default()
}),
..Default::default()
},
transform: Transform {
translation: Vec3 {
x: (SCREEN_WIDTH / 2) as f32,
y: (SCREEN_HEIGHT / 2) as f32,
z: 1.0,
},
..Default::default()
},
..Default::default()
});
} }
fn debug_mouse(mouse: Res<Mouse>) { fn debug_mouse(mouse: Res<Mouse>) {

View File

@ -1,19 +1,11 @@
use std::f32::consts::PI; use std::f32::consts::PI;
use bevy::{ use bevy::prelude::*;
input::gamepad::{GamepadConnection, GamepadEvent},
prelude::*,
render::view::VisibilityPlugin,
};
use crate::{ use crate::{
common::{ common::{animation::SpriteAnimation, velocity::Velocity},
animation::{AnimationBundle, AnimationIndices, AnimationPlugin, AnimationTimer}, level::Unit,
velocity::Velocity, GameState, START_POSITION,
},
gamepad::GamepadOne,
level::{Collider, UnitBundle},
GameState, SCREEN_HEIGHT, SCREEN_WIDTH, START_POSITION,
}; };
pub struct PlayerPlugin; pub struct PlayerPlugin;
@ -29,6 +21,7 @@ impl Plugin for PlayerPlugin {
} }
#[derive(Component)] #[derive(Component)]
#[require(Unit, SpriteAnimation)]
pub struct Player; pub struct Player;
fn setup( fn setup(
@ -37,52 +30,39 @@ fn setup(
mut texture_atlas_layouts: ResMut<Assets<TextureAtlasLayout>>, mut texture_atlas_layouts: ResMut<Assets<TextureAtlasLayout>>,
) { ) {
let layout = TextureAtlasLayout::from_grid(UVec2::splat(135), 2, 1, None, None); let layout = TextureAtlasLayout::from_grid(UVec2::splat(135), 2, 1, None, None);
let texture_atlas_layouts = texture_atlas_layouts.add(layout); let layout_handle = texture_atlas_layouts.add(layout);
commands.spawn(( commands.spawn((
Player, Player,
Collider, Transform {
UnitBundle { translation: START_POSITION,
sprite: SpriteBundle { scale: Vec2::new(0.5, 0.5).extend(1.0),
transform: Transform {
translation: START_POSITION,
scale: Vec2::new(0.5, 0.5).extend(1.0),
..Default::default()
},
visibility: Visibility::Hidden,
texture: asset_server.load("player.png"),
..Default::default()
},
animator: AnimationBundle {
atlas: TextureAtlas {
layout: texture_atlas_layouts,
index: 0,
},
timer: AnimationTimer(Timer::from_seconds(0.3, TimerMode::Repeating)),
indices: AnimationIndices {
first_index: 0,
last_index: 1,
},
},
..Default::default() ..Default::default()
}, },
Visibility::Hidden,
Sprite {
image: asset_server.load("player.png"),
texture_atlas: Some(TextureAtlas {
layout: layout_handle,
index: 0,
}),
..Default::default()
},
SpriteAnimation::new(1, Timer::from_seconds(0.3, TimerMode::Repeating)),
)); ));
} }
fn move_player( fn move_player(
keys: Res<ButtonInput<KeyCode>>, keys: Res<ButtonInput<KeyCode>>,
axes: Res<Axis<GamepadAxis>>, gamepads: Query<&Gamepad>,
gamepads: Res<ButtonInput<GamepadButton>>,
gamepad1: Option<Res<GamepadOne>>,
mut query: Query<(&mut Transform, &mut Velocity), With<Player>>, mut query: Query<(&mut Transform, &mut Velocity), With<Player>>,
window: Query<&Window>, window: Query<&Window>,
) { ) {
let (mut transform, mut velocity) = query.single_mut(); let (mut transform, mut velocity) = query.single_mut();
let gamepad = gamepads.single();
velocity.0 = Vec2::ZERO; velocity.0 = Vec2::ZERO;
if let Some(gamepad) = gamepad1.as_deref() { handle_gamepad_movement(gamepad, &mut velocity);
handle_gamepad_movement(&axes, gamepad, &gamepads, &mut velocity);
};
if keys.pressed(KeyCode::ArrowRight) { if keys.pressed(KeyCode::ArrowRight) {
velocity.x += 1.0; velocity.x += 1.0;
@ -109,59 +89,26 @@ fn move_player(
transform.translation = transform.translation.clamp(Vec3::ZERO, screen_size); transform.translation = transform.translation.clamp(Vec3::ZERO, screen_size);
} }
fn handle_gamepad_movement( fn handle_gamepad_movement(gamepad: &Gamepad, velocity: &mut Velocity) {
axes: &Axis<GamepadAxis>, let axis_lx = gamepad.get(GamepadAxis::LeftStickX).unwrap_or_default();
gamepad: &GamepadOne, let axis_ly = gamepad.get(GamepadAxis::LeftStickY).unwrap_or_default();
gamepads: &Res<ButtonInput<GamepadButton>>,
velocity: &mut Velocity,
) {
let axis_lx = GamepadAxis {
gamepad: gamepad.0,
axis_type: GamepadAxisType::LeftStickX,
};
let axis_ly = GamepadAxis {
gamepad: gamepad.0,
axis_type: GamepadAxisType::LeftStickY,
};
if let (Some(x), Some(y)) = (axes.get(axis_lx), axes.get(axis_ly)) { let left_stick = Vec2::new(axis_lx, axis_ly);
let left_stick = Vec2::new(x, y); if left_stick.length() > 0.25 {
if left_stick.length() > 0.25 { *velocity = Velocity(left_stick);
*velocity = Velocity(left_stick); return;
return;
}
} }
let up_btn = GamepadButton { if gamepad.pressed(GamepadButton::DPadRight) {
gamepad: gamepad.0,
button_type: GamepadButtonType::DPadUp,
};
let down_btn = GamepadButton {
gamepad: gamepad.0,
button_type: GamepadButtonType::DPadDown,
};
let left_btn = GamepadButton {
gamepad: gamepad.0,
button_type: GamepadButtonType::DPadLeft,
};
let right_btn = GamepadButton {
gamepad: gamepad.0,
button_type: GamepadButtonType::DPadRight,
};
if gamepads.pressed(right_btn) {
velocity.x += 1.0; velocity.x += 1.0;
} }
if gamepads.pressed(left_btn) { if gamepad.pressed(GamepadButton::DPadLeft) {
velocity.x -= 1.0; velocity.x -= 1.0;
} }
if gamepads.pressed(up_btn) { if gamepad.pressed(GamepadButton::DPadUp) {
velocity.y += 1.0; velocity.y += 1.0;
} }
if gamepads.pressed(down_btn) { if gamepad.pressed(GamepadButton::DPadDown) {
velocity.y -= 1.0; velocity.y -= 1.0;
} }
} }

View File

@ -28,6 +28,6 @@ fn score_tick(
timer.tick(time.delta()); timer.tick(time.delta());
if timer.just_finished() { if timer.just_finished() {
score.0 += 1; score.0 += 1;
score_text.sections[0].value = format!("{}", score.0); score_text.0 = format!("{}", score.0);
} }
} }