Last active
September 29, 2023 19:42
-
-
Save IronGremlin/7916e8f11e34c2629a35a9eb8d6ed6cd to your computer and use it in GitHub Desktop.
Example
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use std::time::Duration; | |
use bevy::prelude::*; | |
use crate::SimState; | |
#[derive(Resource)] | |
pub struct GameClock { | |
delta: Duration, | |
// We store this here so that the user can pause/unpause separately from incremently advancing our scalar all the way back down/up from 0. | |
resume_speed: TickRate | |
} | |
impl Default for GameClock { | |
fn default() -> Self { | |
GameClock { delta: Duration::new(0, 0), resume_speed: TickRate::Standard } | |
} | |
} | |
#[derive(Component)] | |
pub struct SimTimer { | |
pub time: Timer | |
} | |
#[derive(Debug, Clone, Copy, Default, Eq, PartialEq, Hash, States)] | |
pub enum TickRate { | |
#[default] | |
Paused, | |
Standard, | |
X2, | |
X4 | |
} | |
pub struct GameTimerPlugin; | |
const BASE_TICK_RATE: f32 = 1.0; | |
impl Plugin for GameTimerPlugin { | |
fn build(&self, app: &mut App) { | |
app | |
.insert_resource(GameClock::default()) | |
.add_state::<TickRate>() | |
.add_systems(OnEnter(SimState::Playing),resume_when_sim_resumes) | |
.add_systems(OnEnter(SimState::Paused),pause_when_sim_pauses) | |
.add_systems(Update, (advance_scaled_time, tick_sim_timers)) | |
.add_systems(Update, adjust_sim_speed); | |
} | |
} | |
fn scaled_time(rate: &TickRate, duration: Duration ) -> Duration { | |
let scalar :f32 = match rate { | |
TickRate::Paused => 0.0, | |
TickRate::Standard => 1.0, | |
TickRate::X2 => 2.0, | |
TickRate::X4 => 4.0 | |
}; | |
Duration::from_secs_f32(scalar * duration.as_secs_f32() * BASE_TICK_RATE) | |
} | |
fn pause_when_sim_pauses(mut rate : ResMut<NextState<TickRate>>, current_rate: Res<State<TickRate>>, mut timer: ResMut<GameClock>) { | |
// we treat "paused" as a valid rate to record - this is intentional, because if the player were to open a menu or do anything else that causes out of band | |
// sim halt while the sim clock is manually paused, we should not surprise the player by returning them to an unpaused sim once that state is exited. | |
timer.resume_speed = current_rate.get().clone(); | |
rate.set(TickRate::Paused); | |
} | |
fn resume_when_sim_resumes(mut rate : ResMut<NextState<TickRate>>, timer: Res<GameClock>) { | |
rate.set(timer.resume_speed.clone()); | |
} | |
fn advance_scaled_time(time: Res<Time>, mut game_time: ResMut<GameClock>, rate: Res<State<TickRate>>) { | |
let elapsed = scaled_time(rate.get(),time.delta()); | |
game_time.delta = elapsed; | |
} | |
fn tick_sim_timers(time: Res<GameClock>, mut simtimers : Query<&mut SimTimer>) { | |
for mut simtimer in simtimers.iter_mut() { | |
simtimer.time.tick(time.delta); | |
} | |
} | |
fn adjust_sim_speed(input: Res<Input<KeyCode>>, mut rate : ResMut<NextState<TickRate>>, current_rate: Res<State<TickRate>>) { | |
if input.pressed(KeyCode::Comma) { | |
rate.set(slower_rate(¤t_rate.get())) | |
} | |
if (input.pressed(KeyCode::Period)) { | |
rate.set(faster_rate(¤t_rate.get())) | |
} | |
} | |
fn faster_rate(rate: &TickRate) -> TickRate { | |
match rate { | |
TickRate::Paused => TickRate::Standard, | |
TickRate::Standard => TickRate::X2, | |
TickRate::X2 => TickRate::X4, | |
TickRate::X4 => TickRate::X4, | |
_ => TickRate::Paused | |
} | |
} | |
fn slower_rate(rate: &TickRate) -> TickRate { | |
match rate { | |
TickRate::Paused => TickRate::Paused, | |
TickRate::Standard => TickRate::Paused, | |
TickRate::X2 => TickRate::Standard, | |
TickRate::X4 => TickRate::X2, | |
_ => TickRate::Paused | |
} | |
} | |
#[derive(Component)] | |
pub struct Lifespan; | |
pub struct EatenFood(i32); | |
#[derive(Component)] | |
pub struct PassiveHunger { | |
stomach: EatenFood | |
} | |
fn cull_mortals(mut commands: Commands, mortals: Query<(&Parent, &SimTimer), With<Lifespan>>) { | |
for (parent, age) in mortals.iter() { | |
if age.time.finished() { | |
commands.entity(parent.get()).despawn_recursive(); | |
} | |
} | |
} | |
fn cull_starving(mut commands: Commands, hungry: Query<(&Parent, &PassiveHunger)>) { | |
for (parent, hunger) in hungry.iter() { | |
if hunger.stomach.0 <= 0 { | |
commands.entity(parent.get()).despawn_recursive(); | |
} | |
} | |
} | |
fn apply_hunger(mut hungry: Query<(&mut PassiveHunger, &SimTimer)>) { | |
for (mut hunger, timer) in hungry.iter_mut() { | |
if timer.time.finished() { | |
hunger.stomach.0 -= 1; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment