simple hitpoint mechanic

This commit is contained in:
2025-03-12 23:25:13 +01:00
parent 709c1762bd
commit 8946289ac2
8 changed files with 140 additions and 29 deletions

Binary file not shown.

View File

@@ -5,11 +5,12 @@ use crate::{
};
use bevy::prelude::*;
use bevy_sprite3d::{Sprite3dBuilder, Sprite3dParams};
use ops::sin;
use std::f32::consts::PI;
#[derive(Resource, Reflect)]
#[reflect(Resource)]
struct AimState {
pub struct AimState {
pub target: Option<Entity>,
pub range: f32,
pub angle: f32,
@@ -37,7 +38,7 @@ enum MarkerEvent {
pub fn plugin(app: &mut App) {
app.init_resource::<AimState>();
app.add_systems(Update, update);
app.add_systems(Update, (update, move_marker));
app.add_observer(marker_event);
}
@@ -63,7 +64,7 @@ fn marker_event(
Name::new("aim-marker"),
Billboard,
Marker,
Transform::from_translation(Vec3::new(0., 3., 0.)),
Transform::default(),
Sprite3dBuilder {
image: selector,
pixels_per_metre: 30.,
@@ -129,3 +130,9 @@ fn update(
state.target = new_target;
}
}
fn move_marker(mut query: Query<&mut Transform, With<Marker>>, time: Res<Time>) {
for mut transform in query.iter_mut() {
transform.translation = Vec3::new(0., 3. + (sin(time.elapsed_secs() * 6.) * 0.2), 0.);
}
}

View File

@@ -1,4 +1,4 @@
use crate::movables::TriggerMovableEvent;
use crate::{movables::TriggerMovableEvent, npc::KeyCollected};
use bevy::{prelude::*, utils::hashbrown::HashSet};
#[derive(Resource)]
@@ -9,16 +9,15 @@ enum GatesState {
pub fn plugin(app: &mut App) {
app.insert_resource(GatesState::Init);
app.add_systems(Update, update);
app.add_observer(on_key);
}
fn update(
fn on_key(
_trigger: Trigger<KeyCollected>,
mut commands: Commands,
keyboard: Res<ButtonInput<KeyCode>>,
mut state: ResMut<GatesState>,
asset_server: Res<AssetServer>,
) {
if keyboard.just_pressed(KeyCode::Digit1) {
if matches!(*state, GatesState::Init) {
*state = GatesState::GateOpen1;
@@ -40,5 +39,4 @@ fn update(
commands.trigger(TriggerMovableEvent(entities));
}
}
}

View File

@@ -6,8 +6,10 @@ mod cash;
mod gates;
mod heads_ui;
mod movables;
mod npc;
mod platforms;
mod player;
mod shooting;
mod tb_entities;
use avian3d::PhysicsPlugins;
@@ -76,6 +78,8 @@ fn main() {
app.add_plugins(movables::plugin);
app.add_plugins(billboards::plugin);
app.add_plugins(aim::plugin);
app.add_plugins(shooting::plugin);
app.add_plugins(npc::plugin);
app.insert_resource(AmbientLight {
color: Color::WHITE,

View File

@@ -0,0 +1,41 @@
use crate::tb_entities::EnemySpawn;
use bevy::prelude::*;
#[derive(Event, Reflect)]
pub struct Hit {
pub damage: u32,
}
// TODO: add actual keys
#[derive(Event, Reflect)]
pub struct KeyCollected;
#[derive(Component, Reflect)]
struct Hp(i32);
pub fn plugin(app: &mut App) {
app.add_systems(Update, init);
}
fn init(mut commands: Commands, query: Query<Entity, (With<EnemySpawn>, Without<Hp>)>) {
for e in query.iter() {
commands.entity(e).insert(Hp(100)).observe(on_hit);
}
}
fn on_hit(trigger: Trigger<Hit>, mut commands: Commands, mut query: Query<&mut Hp>) {
let Hit { damage } = trigger.event();
let Ok(mut hp) = query.get_mut(trigger.entity()) else {
return;
};
hp.0 = hp.0.saturating_sub(*damage as i32);
info!("npc hp changed: {} [{}]", hp.0, trigger.entity());
if hp.0 <= 0 {
commands.entity(trigger.entity()).despawn_recursive();
commands.trigger(KeyCollected);
}
}

View File

@@ -6,11 +6,15 @@ use crate::{
camera::GameCameraRig,
cash::{Cash, CashCollectEvent},
heads_ui::HeadChanged,
shooting::TriggerState,
tb_entities::SpawnPoint,
};
use avian3d::prelude::*;
use bevy::{
input::mouse::MouseMotion,
input::{
ButtonState,
mouse::{MouseButtonInput, MouseMotion},
},
prelude::*,
window::{CursorGrabMode, PrimaryWindow},
};
@@ -49,7 +53,6 @@ pub fn plugin(app: &mut App) {
(
spawn,
update_camera,
mouse_rotate,
collect_cash,
toggle_animation,
setup_animations_marker_for_player,
@@ -60,6 +63,9 @@ pub fn plugin(app: &mut App) {
apply_controls.in_set(TnuaUserControlsSystemSet),
);
app.add_systems(Update, mouse_rotate.run_if(on_event::<MouseMotion>));
app.add_systems(Update, mouse_click.run_if(on_event::<MouseButtonInput>));
app.add_observer(updaate_head);
}
@@ -131,6 +137,28 @@ fn mouse_rotate(
}
}
fn mouse_click(mut events: EventReader<MouseButtonInput>, mut commands: Commands) {
for ev in events.read() {
match ev {
MouseButtonInput {
button: MouseButton::Left,
state: ButtonState::Pressed,
..
} => {
commands.trigger(TriggerState::Active);
}
MouseButtonInput {
button: MouseButton::Left,
state: ButtonState::Released,
..
} => {
commands.trigger(TriggerState::Inactive);
}
_ => {}
}
}
}
fn toggle_grab_cursor(window: &mut Window) {
match window.cursor_options.grab_mode {
CursorGrabMode::None => {

31
src/shooting.rs Normal file
View File

@@ -0,0 +1,31 @@
use bevy::prelude::*;
use crate::{aim::AimState, npc::Hit};
#[derive(Event, Reflect)]
pub enum TriggerState {
Active,
Inactive,
}
pub fn plugin(app: &mut App) {
app.add_observer(on_trigger_state);
}
fn on_trigger_state(
trigger: Trigger<TriggerState>,
mut commands: Commands,
asset_server: Res<AssetServer>,
aim: Res<AimState>,
) {
if matches!(trigger.event(), TriggerState::Active) {
commands.spawn((
AudioPlayer::new(asset_server.load("sfx/abilities/gun.ogg")),
PlaybackSettings::DESPAWN,
));
if let Some(target) = aim.target {
commands.entity(target).trigger(Hit { damage: 20 });
}
}
}

View File

@@ -108,12 +108,14 @@ impl EnemySpawn {
let head_mesh = asset_server
.load(GltfAssetLabel::Scene(0).from_asset(format!("models/heads/{}.glb", this.head)));
let head = this.head.clone();
world
.commands()
.entity(entity)
.insert((
this_transform,
Name::from("Enemy"),
Name::from(format!("enemy [{}]", head)),
Visibility::default(),
RigidBody::Dynamic,
Collider::capsule(0.4, 2.),