Compare commits

..

2 commits

Author SHA1 Message Date
0d945de135
add lives 2024-05-10 21:45:47 +02:00
6a63d13395
clean up player grounding detection 2024-05-10 20:00:31 +02:00
3 changed files with 147 additions and 19 deletions

View file

@ -7,7 +7,11 @@ use bevy_rand::prelude::*;
use bevy_rapier2d::prelude::*; use bevy_rapier2d::prelude::*;
use rand::Rng; use rand::Rng;
use crate::{player::Player, scene::SceneObj, METER}; use crate::{
player::{LifeChangeEvent, Player},
scene::SceneObj,
METER,
};
#[derive(Component)] #[derive(Component)]
struct SpawnTimer(pub Timer); struct SpawnTimer(pub Timer);
@ -27,7 +31,13 @@ pub fn spawner_plugin(app: &mut App) {
.add_systems(Startup, add_timer) .add_systems(Startup, add_timer)
.add_systems( .add_systems(
Update, Update,
(drop_crates, do_drop, crate_collisions, delete_on_env_coll), (
drop_crates,
do_drop,
crate_collisions,
delete_on_env_coll,
player_coll,
),
); );
} }
@ -130,10 +140,27 @@ fn delete_on_env_coll(mut ev_colls: EventReader<CrateCollision>, mut commands: C
if let CollisionType::Player(_) = with { if let CollisionType::Player(_) = with {
continue; continue;
} }
// let CollisionType::Scene(obj) = with else {
// unreachable!()
// };
commands.entity(*coll_crate).despawn(); if let Some(mut e) = commands.get_entity(*coll_crate) {
e.despawn();
}
}
}
fn player_coll(
mut ev_colls: EventReader<CrateCollision>,
mut ev_lives: EventWriter<LifeChangeEvent>,
mut commands: Commands,
) {
for CrateCollision { coll_crate, with } in ev_colls.read() {
if let CollisionType::Scene(_) = with {
continue;
}
ev_lives.send(LifeChangeEvent::Lost);
if let Some(mut e) = commands.get_entity(*coll_crate) {
e.despawn();
}
} }
} }

View file

@ -4,6 +4,7 @@ use bevy::{
}; };
use bevy_rapier2d::prelude::*; use bevy_rapier2d::prelude::*;
use drops::spawner_plugin; use drops::spawner_plugin;
use player::LifeChangeEvent;
const METER: f32 = 48.; const METER: f32 = 48.;
fn main() { fn main() {
@ -19,12 +20,12 @@ fn main() {
.insert_resource(rapier_config) .insert_resource(rapier_config)
.add_plugins(RapierPhysicsPlugin::<NoUserData>::pixels_per_meter(METER)) .add_plugins(RapierPhysicsPlugin::<NoUserData>::pixels_per_meter(METER))
.add_plugins(RapierDebugRenderPlugin::default()) .add_plugins(RapierDebugRenderPlugin::default())
.add_plugins((spawner_plugin)) .add_plugins((
.add_systems(Startup, (scene::setup_scene, setup_cam, player::add_player)) spawner_plugin,
.add_systems( game_state::state_and_ui_plugin,
Update, player::player_plugin,
(player::move_player, player::player_ground_collision), ))
) .add_systems(Startup, (scene::setup_scene, setup_cam))
.run() .run()
} }
@ -38,3 +39,62 @@ fn setup_cam(mut commands: Commands) {
mod drops; mod drops;
mod player; mod player;
mod scene; mod scene;
mod game_state {
use bevy::{prelude::*, time::Stopwatch};
#[derive(Component)]
pub struct GameState {
score: u32,
game_time: Stopwatch,
}
#[derive(Component)]
struct StateText;
pub fn state_and_ui_plugin(app: &mut App) {
app.add_systems(Startup, (setup_gamestate, setup_ui))
.add_systems(Update, (update_time));
}
fn setup_gamestate(mut commands: Commands) {
commands.spawn(GameState {
score: 0,
game_time: Stopwatch::new(),
});
}
fn setup_ui(mut commands: Commands) {
commands.spawn((
TextBundle::from_sections([
TextSection::new("Score: ", TextStyle::default()),
TextSection::new("0", TextStyle::default()),
TextSection::new("\n", TextStyle::default()),
TextSection::new("Time: ", TextStyle::default()),
TextSection::new("0", TextStyle::default()),
TextSection::new("s", TextStyle::default()),
])
.with_style(Style {
position_type: PositionType::Absolute,
top: Val::Px(5.0),
right: Val::Px(5.0),
..default()
}),
StateText,
));
}
fn update_time(
mut state_txt: Query<&mut Text, With<StateText>>,
mut game_state: Query<&mut GameState>,
time: Res<Time>,
) {
let mut txt = state_txt.single_mut();
let time = game_state
.single_mut()
.game_time
.tick(time.delta())
.elapsed_secs();
txt.sections[4].value = format!("{time:.2}");
}
}

View file

@ -1,6 +1,7 @@
use bevy::{ use bevy::{
prelude::*, prelude::*,
sprite::{MaterialMesh2dBundle, Mesh2dHandle}, sprite::{MaterialMesh2dBundle, Mesh2dHandle},
utils::{HashMap, HashSet},
}; };
use bevy_rapier2d::prelude::*; use bevy_rapier2d::prelude::*;
@ -10,6 +11,21 @@ use crate::METER;
pub struct Player { pub struct Player {
move_cooldown: Timer, move_cooldown: Timer,
grounded: bool, grounded: bool,
lives: u8,
}
#[derive(Event)]
pub enum LifeChangeEvent {
Gained,
Lost,
}
const DEFAULT_LIVES: u8 = 5;
pub fn player_plugin(app: &mut App) {
app.add_event::<LifeChangeEvent>()
.add_systems(Startup, (add_player, add_lives_ui))
.add_systems(Update, (move_player, player_ground_collision, update_lives));
} }
pub fn add_player( pub fn add_player(
@ -23,6 +39,7 @@ pub fn add_player(
move_cooldown: Timer::from_seconds(0.01, TimerMode::Repeating), move_cooldown: Timer::from_seconds(0.01, TimerMode::Repeating),
// move_cooldown: Timer::from_seconds(0.01, TimerMode::Repeating), // move_cooldown: Timer::from_seconds(0.01, TimerMode::Repeating),
grounded: false, grounded: false,
lives: DEFAULT_LIVES,
}) })
.insert(MaterialMesh2dBundle { .insert(MaterialMesh2dBundle {
mesh: shape, mesh: shape,
@ -40,25 +57,49 @@ pub fn add_player(
.insert(KinematicCharacterController { ..default() }); .insert(KinematicCharacterController { ..default() });
} }
#[derive(Component)]
struct LivesUi;
fn add_lives_ui(mut commands: Commands) {
commands.spawn((
TextBundle::from("<3 ".repeat(DEFAULT_LIVES.into())),
LivesUi,
));
}
fn update_lives(
mut ev_lifechange: EventReader<LifeChangeEvent>,
mut player: Query<&mut Player>,
mut ui: Query<&mut Text, With<LivesUi>>,
) {
let mut txt = ui.single_mut();
let mut p = player.single_mut();
for ev in ev_lifechange.read() {
match ev {
LifeChangeEvent::Lost => p.lives -= 1,
LifeChangeEvent::Gained => p.lives += 1,
}
txt.sections[0].value = "<3 ".repeat(p.lives.into())
}
}
pub fn player_ground_collision( pub fn player_ground_collision(
mut player: Query<(Entity, &mut Player)>, mut player: Query<(Entity, &mut Player)>,
scene_objs: Query<Entity, With<super::scene::SceneObj>>, scene_objs: Query<Entity, With<super::scene::SceneObj>>,
mut collision_events: EventReader<CollisionEvent>, mut collision_events: EventReader<CollisionEvent>,
) { ) {
let (p, mut player) = player.single_mut(); let (p, mut player) = player.single_mut();
let scene_objs = scene_objs.iter().collect::<HashSet<_>>();
for collision_event in collision_events.read() { for collision_event in collision_events.read() {
match collision_event { match collision_event {
CollisionEvent::Started(e1, e2, _) => { CollisionEvent::Started(e1, e2, _) => {
if (e1 == &p && scene_objs.iter().any(|obj| *e2 == obj)) if (e1 == &p && scene_objs.contains(e2)) || (e2 == &p && scene_objs.contains(e1)) {
|| (e2 == &p && scene_objs.iter().any(|obj| *e1 == obj))
{
player.grounded = true; player.grounded = true;
} }
} }
CollisionEvent::Stopped(e1, e2, _) => { CollisionEvent::Stopped(e1, e2, _) => {
if (e1 == &p && scene_objs.iter().any(|obj| *e2 == obj)) if (e1 == &p && scene_objs.contains(e2)) || (e2 == &p && scene_objs.contains(e1)) {
|| (e2 == &p && scene_objs.iter().any(|obj| *e1 == obj))
{
player.grounded = false; player.grounded = false;
} }
} }
@ -91,7 +132,7 @@ pub fn move_player(
if kb_input.pressed(KeyCode::KeyW) && player.grounded { if kb_input.pressed(KeyCode::KeyW) && player.grounded {
moved = true; moved = true;
*vel = Velocity::linear(Vec2::new(0., 9. * METER)); *vel = Velocity::linear(Vec2::new(0., 9. * METER));
// *vel = Velocity::linear(Vec2::new(0., 9.81 * METER)); // *vel = Velocity::linear(Vec2::new(0., 9.81 * METER));
} }