Ported to bevy 0.15
parent
b72d550b4d
commit
ea93d50e44
File diff suppressed because it is too large
Load Diff
|
@ -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]
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,
|
|
||||||
}
|
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
17
src/enemy.rs
17
src/enemy.rs
|
@ -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(
|
||||||
|
|
|
@ -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>();
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
187
src/hud.rs
187
src/hud.rs
|
@ -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);
|
||||||
|
|
||||||
|
|
99
src/level.rs
99
src/level.rs
|
@ -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 {
|
||||||
|
|
36
src/main.rs
36
src/main.rs
|
@ -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>) {
|
||||||
|
|
119
src/player.rs
119
src/player.rs
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue