bevyengine / bevyengine/bevy

Type-safe `Instant` equivalent for `bevy_time` that encodes source of clock

Open
#25,495 0 comments 0 reactions 0 assignees View on GitHub
A-Time C-Feature D-Modest S-Needs-Design X-Contentious
Dominant language
Rust
Stars
48.2k
Forks
4.8k
Avg merge
3d 16h
Merged PRs (30d)
171

Description

## What problem does this solve or what need does it fill?

`bevy_time` doesn't have any ergonomic equivalent to ``std::time::Instant`` that would work with built-in timekeeping(``Res>``)
The closest equivalent is to use ``Stopwatch``, but it requires to be manually ticked every schedule update, which is fine in one type of situations, but can be problematic/non-trivial/expensive in other kinds of situations.

Also as minor downside, ``Stopwatch`` is not type-safe and will gladly accept tick delta from wrong Time.

## What solution would you like?

Add `Instant`-like type(or newtype) based on Duration(with ability to get underlying Duration) with equivalent to `bevy_time::Time` generic argument, e.g. ``TimeInstant``(maybe chose better name)
Add ``bevy_time::Time::now(&self) -> TimeInstant``
Also add ``TimeInstant::as_generic(&self) -> TimeInstant<()>`` to ``TimeInstant`` for cases when it needs to be type-erased.

So, final API looks like that
```rs
#[Derive(Component)]
struct Spell {
last_used: TimeInstant,
cooldown: Duration,
damage: f32,
kind: SpellKind,
}

fn on_cast_spell(spell: On, spells: Query<&mut Ability>, transform: Query<&Transform>, mut commands: Commands, time_res: Res>) {
let mut spell_info = spells.get_mut(spell.spell).unwrap();
if time_res.now() - spell_info.last_used < spell_info.cooldown { return; }
// Or
if spell_info.last_used.elapsed(time_res) < spell_info.cooldown { return; }
spell_info.last_used = time_res::now();
let player_pos = transform.get(spell.caster).unwrap();
commands.trigger(SpawnProjectile { damage: spell_info.damage, position: player_pos.translation. kind: spell_info.kind, owner: spell.caster });
}
```

Additionally that would be immediately useful for refactoring [``crates/bevy_camera_controller/src/pan_orbit_camera/controller/smoothing.rs``](https://github.com/bevyengine/bevy/blob/main/crates/bevy_camera_controller/src/pan_orbit_camera/controller/smoothing.rs) to replace all usages of ``std::time::Instant`` in favor of ``Time``.

Also probably it would be good to add ``Timer`` equivalent based around ``TimeInstant``
```rs
let mut timer = TimerInstant::new(start, duration);
timer.update(time_now);
trace!("{}", timer.fraction());
trace!("{}", timer.is_finished());
```

Contributor guide

Open the contributing guide

Research direction

Start by reading the existing bevy_time APIs, especially Time and Stopwatch, then inspect crates/bevy_camera_controller/src/pan_orbit_camera/controller/smoothing.rs to understand the std::time::Instant usage. Done means an agreed type-safe Instant-like API for clock sources, with the requested timekeeping use cases addressed and the smoothing code refactored if included in scope.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
game-dev
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.