use gizmo_core::world::World;
use gizmo_physics_core::Transform;
#[derive(Debug, Clone, Copy)]
pub struct DespawnAfter {
pub remaining: f32,
}
impl DespawnAfter {
pub fn secs(secs: f32) -> Self {
Self { remaining: secs }
}
}
gizmo_core::impl_component!(DespawnAfter);
#[derive(Debug, Clone, Copy)]
pub struct DespawnBelowY {
pub y: f32,
}
impl DespawnBelowY {
pub fn new(y: f32) -> Self {
Self { y }
}
}
gizmo_core::impl_component!(DespawnBelowY);
pub struct LifetimeSystem;
impl gizmo_core::system::System for LifetimeSystem {
fn access_info(&self) -> gizmo_core::system::AccessInfo {
let mut info = gizmo_core::system::AccessInfo::new();
info.is_exclusive = true;
info
}
#[tracing::instrument(skip_all, level = "trace", name = "lifetime")]
fn run(&mut self, world: &World, dt: f32) {
use gizmo_core::commands::Commands;
use gizmo_core::system::SystemParam;
let mut commands = match Commands::fetch(world, dt) {
Ok(c) => c,
Err(_) => {
tracing::trace!(
"LifetimeSystem: Commands (CommandQueue) yok — despawn atlanıyor, ömür komponentleri atıl"
);
return;
}
};
let mut despawned_after = 0usize;
let mut despawned_below_y = 0usize;
if let Some(mut q) =
unsafe { world.query_unchecked::<gizmo_core::query::Mut<DespawnAfter>>() }
{
for (id, mut d) in q.iter_mut() {
d.remaining -= dt;
if d.remaining <= 0.0 {
if let Some(e) = world.entity(id) {
commands.entity(e).despawn();
despawned_after += 1;
}
}
}
}
if let Some(q) = unsafe { world.query_unchecked::<(&DespawnBelowY, &Transform)>() } {
for (id, (below, t)) in q.iter() {
if t.position.y < below.y {
if let Some(e) = world.entity(id) {
commands.entity(e).despawn();
despawned_below_y += 1;
}
}
}
}
if despawned_after > 0 || despawned_below_y > 0 {
tracing::debug!(
despawned_after,
despawned_below_y,
"LifetimeSystem: geçici varlıklar despawn için kuyruğa alındı"
);
}
}
}
pub struct LifetimePlugin;
impl<State: 'static> crate::app::Plugin<State> for LifetimePlugin {
fn build(&self, app: &mut crate::app::App<State>) {
app.schedule.add_di_system(
gizmo_core::system::SystemConfig::new(Box::new(LifetimeSystem)).label("lifetime"),
);
}
}
#[cfg(test)]
mod tests {
use super::*;
use gizmo_core::commands::CommandQueue;
use gizmo_core::system::System;
use gizmo_math::Vec3;
fn world_with_commands() -> World {
let mut world = World::new();
world.insert_resource(CommandQueue::default());
world
}
#[test]
fn despawn_after_removes_entity_when_timer_elapses() {
let mut world = world_with_commands();
let e = world.spawn();
world.add_component(e, Transform::new(Vec3::ZERO));
world.add_component(e, DespawnAfter::secs(0.1));
let mut sys = LifetimeSystem;
sys.run(&world, 0.05);
world.apply_commands();
assert!(world.is_alive(e), "0.05s'de hâlâ canlı olmalı");
sys.run(&world, 0.1);
world.apply_commands();
assert!(!world.is_alive(e), "süre dolunca despawn edilmeli");
}
#[test]
fn despawn_below_y_removes_fallen_entity() {
let mut world = world_with_commands();
let above = world.spawn();
world.add_component(above, Transform::new(Vec3::new(0.0, 5.0, 0.0)));
world.add_component(above, DespawnBelowY::new(-60.0));
let fallen = world.spawn();
world.add_component(fallen, Transform::new(Vec3::new(0.0, -100.0, 0.0)));
world.add_component(fallen, DespawnBelowY::new(-60.0));
let mut sys = LifetimeSystem;
sys.run(&world, 0.016);
world.apply_commands();
assert!(world.is_alive(above), "eşiğin üstündeki korunmalı");
assert!(!world.is_alive(fallen), "eşiğin altındaki silinmeli");
}
}