Skip to content

Commit 0495ae7

Browse files
fix gizmos during fixed update
1 parent cd0a642 commit 0495ae7

File tree

5 files changed

+124
-9
lines changed

5 files changed

+124
-9
lines changed

crates/bevy_gizmos/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ keywords = ["bevy"]
1010

1111
[features]
1212
webgl = []
13+
fixed_update = ["dep:bevy_time"]
1314

1415
[dependencies]
1516
# Bevy
@@ -25,3 +26,4 @@ bevy_core = { path = "../bevy_core", version = "0.12.0-dev" }
2526
bevy_reflect = { path = "../bevy_reflect", version = "0.12.0-dev" }
2627
bevy_core_pipeline = { path = "../bevy_core_pipeline", version = "0.12.0-dev" }
2728
bevy_transform = { path = "../bevy_transform", version = "0.12.0-dev" }
29+
bevy_time = { path = "../bevy_time", version = "0.12.0-dev", optional = true }

crates/bevy_gizmos/src/gizmos.rs

Lines changed: 77 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,28 @@
33
use std::{f32::consts::TAU, iter};
44

55
use bevy_ecs::{
6-
system::{Deferred, Resource, SystemBuffer, SystemMeta, SystemParam},
7-
world::World,
6+
component::Tick,
7+
system::{Resource, SystemBuffer, SystemMeta, SystemParam},
8+
world::{unsafe_world_cell::UnsafeWorldCell, World},
89
};
910
use bevy_math::{Mat2, Quat, Vec2, Vec3};
1011
use bevy_render::color::Color;
1112
use bevy_transform::TransformPoint;
13+
use bevy_utils::default;
1214

1315
type PositionItem = [f32; 3];
1416
type ColorItem = [f32; 4];
1517

1618
const DEFAULT_CIRCLE_SEGMENTS: usize = 32;
1719

1820
#[derive(Resource, Default)]
21+
pub(crate) struct GizmoStorages {
22+
pub frame: GizmoStorage,
23+
pub fixed_update_tick: u64,
24+
pub fixed_update: GizmoStorage,
25+
}
26+
27+
#[derive(Default)]
1928
pub(crate) struct GizmoStorage {
2029
pub list_positions: Vec<PositionItem>,
2130
pub list_colors: Vec<ColorItem>,
@@ -24,13 +33,14 @@ pub(crate) struct GizmoStorage {
2433
}
2534

2635
/// A [`SystemParam`](bevy_ecs::system::SystemParam) for drawing gizmos.
27-
#[derive(SystemParam)]
2836
pub struct Gizmos<'s> {
29-
buffer: Deferred<'s, GizmoBuffer>,
37+
buffer: &'s mut GizmoBuffer,
3038
}
3139

3240
#[derive(Default)]
3341
struct GizmoBuffer {
42+
/// Which fixed update tick this belongs to, `None` if this isn't from a fixed update.
43+
fixed_time_update: Option<u64>,
3444
list_positions: Vec<PositionItem>,
3545
list_colors: Vec<ColorItem>,
3646
strip_positions: Vec<PositionItem>,
@@ -39,14 +49,76 @@ struct GizmoBuffer {
3949

4050
impl SystemBuffer for GizmoBuffer {
4151
fn apply(&mut self, _system_meta: &SystemMeta, world: &mut World) {
42-
let mut storage = world.resource_mut::<GizmoStorage>();
52+
let mut storages = world.resource_mut::<GizmoStorages>();
53+
54+
let storage = if let Some(tick) = self.fixed_time_update {
55+
// If a new fixed update has begun, clear gizmos from previous fixed update
56+
if storages.fixed_update_tick < tick {
57+
storages.fixed_update_tick = tick;
58+
storages.fixed_update.list_positions.clear();
59+
storages.fixed_update.list_colors.clear();
60+
storages.fixed_update.strip_positions.clear();
61+
storages.fixed_update.strip_colors.clear();
62+
}
63+
&mut storages.fixed_update
64+
} else {
65+
&mut storages.frame
66+
};
67+
4368
storage.list_positions.append(&mut self.list_positions);
4469
storage.list_colors.append(&mut self.list_colors);
4570
storage.strip_positions.append(&mut self.strip_positions);
4671
storage.strip_colors.append(&mut self.strip_colors);
4772
}
4873
}
4974

75+
// Wrap to keep GizmoBuffer hidden
76+
const _: () = {
77+
pub struct Wrap(GizmoBuffer);
78+
79+
// SAFETY: Only local state is accessed.
80+
unsafe impl SystemParam for Gizmos<'_> {
81+
type State = Wrap;
82+
type Item<'w, 's> = Gizmos<'s>;
83+
84+
fn init_state(world: &mut World, _system_meta: &mut SystemMeta) -> Self::State {
85+
#[cfg(not(feature = "fixed_update"))]
86+
let fixed_time_tick = None;
87+
#[cfg(feature = "fixed_update")]
88+
let fixed_time_update =
89+
if world.contains_resource::<bevy_time::fixed_timestep::FixedUpdateScheduleIsCurrentlyRunning>() {
90+
world
91+
.get_resource::<bevy_time::prelude::FixedTime>()
92+
.map(|time| time.times_expended())
93+
} else {
94+
None
95+
};
96+
Wrap(GizmoBuffer {
97+
fixed_time_update,
98+
list_positions: default(),
99+
list_colors: default(),
100+
strip_positions: default(),
101+
strip_colors: default(),
102+
})
103+
}
104+
105+
fn apply(state: &mut Self::State, system_meta: &SystemMeta, world: &mut World) {
106+
state.0.apply(system_meta, world);
107+
}
108+
109+
unsafe fn get_param<'w, 's>(
110+
state: &'s mut Self::State,
111+
_system_meta: &SystemMeta,
112+
_world: UnsafeWorldCell<'w>,
113+
_change_tick: Tick,
114+
) -> Self::Item<'w, 's> {
115+
Gizmos {
116+
buffer: &mut state.0,
117+
}
118+
}
119+
}
120+
};
121+
50122
impl<'s> Gizmos<'s> {
51123
/// Draw a line from `start` to `end`.
52124
///

crates/bevy_gizmos/src/lib.rs

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@
1414
//! # bevy_ecs::system::assert_is_system(system);
1515
//! ```
1616
//!
17+
//! With the `fixed_update` feature (enabled by default by the bevy main crate),
18+
//! gizmos drawn during `FixedUpdate` last until the next fixed update.
19+
//!
1720
//! See the documentation on [`Gizmos`](crate::gizmos::Gizmos) for more examples.
1821
1922
use std::mem;
@@ -59,7 +62,7 @@ mod pipeline_2d;
5962
#[cfg(feature = "bevy_pbr")]
6063
mod pipeline_3d;
6164

62-
use gizmos::{GizmoStorage, Gizmos};
65+
use gizmos::{GizmoStorages, Gizmos};
6366

6467
/// The `bevy_gizmos` prelude.
6568
pub mod prelude {
@@ -82,7 +85,7 @@ impl Plugin for GizmoPlugin {
8285
.add_plugins(RenderAssetPlugin::<LineGizmo>::default())
8386
.init_resource::<LineGizmoHandles>()
8487
.init_resource::<GizmoConfig>()
85-
.init_resource::<GizmoStorage>()
88+
.init_resource::<GizmoStorages>()
8689
.add_systems(Last, update_gizmo_meshes)
8790
.add_systems(
8891
Update,
@@ -92,6 +95,11 @@ impl Plugin for GizmoPlugin {
9295
),
9396
);
9497

98+
// Ensure gizmos from prefious fixed update are cleaned up if no other system
99+
// accesses gizmos during fixed update any more
100+
#[cfg(feature = "fixed_update")]
101+
app.add_systems(bevy_app::FixedUpdate, |_: Gizmos| ());
102+
95103
let Ok(render_app) = app.get_sub_app_mut(RenderApp) else { return; };
96104

97105
render_app
@@ -260,8 +268,23 @@ struct LineGizmoHandles {
260268
fn update_gizmo_meshes(
261269
mut line_gizmos: ResMut<Assets<LineGizmo>>,
262270
mut handles: ResMut<LineGizmoHandles>,
263-
mut storage: ResMut<GizmoStorage>,
271+
mut storages: ResMut<GizmoStorages>,
264272
) {
273+
// Combine gizmos for this frame (which get cleared here) with the ones from the last fixed update (which get cleared during system buffer application)
274+
let mut storage = mem::take(&mut storages.frame);
275+
storage
276+
.list_positions
277+
.extend_from_slice(&storages.fixed_update.list_positions);
278+
storage
279+
.list_colors
280+
.extend_from_slice(&storages.fixed_update.list_colors);
281+
storage
282+
.strip_positions
283+
.extend_from_slice(&storages.fixed_update.strip_positions);
284+
storage
285+
.strip_colors
286+
.extend_from_slice(&storages.fixed_update.strip_colors);
287+
265288
if storage.list_positions.is_empty() {
266289
handles.list = None;
267290
} else if let Some(handle) = handles.list.as_ref() {

crates/bevy_internal/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,4 +139,4 @@ bevy_text = { path = "../bevy_text", optional = true, version = "0.12.0-dev" }
139139
bevy_ui = { path = "../bevy_ui", optional = true, version = "0.12.0-dev" }
140140
bevy_winit = { path = "../bevy_winit", optional = true, version = "0.12.0-dev" }
141141
bevy_gilrs = { path = "../bevy_gilrs", optional = true, version = "0.12.0-dev" }
142-
bevy_gizmos = { path = "../bevy_gizmos", optional = true, version = "0.12.0-dev", default-features = false }
142+
bevy_gizmos = { path = "../bevy_gizmos", optional = true, version = "0.12.0-dev", default-features = false, features = ["fixed_update"] }

crates/bevy_time/src/fixed_timestep.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ use thiserror::Error;
3131
#[derive(Resource, Debug)]
3232
pub struct FixedTime {
3333
accumulated: Duration,
34+
total_ticks: u64,
3435
/// Defaults to 1/60th of a second.
3536
/// To configure this value, simply mutate or overwrite this resource.
3637
pub period: Duration,
@@ -42,6 +43,7 @@ impl FixedTime {
4243
FixedTime {
4344
accumulated: Duration::ZERO,
4445
period,
46+
total_ticks: 0,
4547
}
4648
}
4749

@@ -50,6 +52,7 @@ impl FixedTime {
5052
FixedTime {
5153
accumulated: Duration::ZERO,
5254
period: Duration::from_secs_f32(period),
55+
total_ticks: 0,
5356
}
5457
}
5558

@@ -63,13 +66,19 @@ impl FixedTime {
6366
self.accumulated
6467
}
6568

69+
/// Returns how often this has expended a period of time.
70+
pub fn times_expended(&self) -> u64 {
71+
self.total_ticks
72+
}
73+
6674
/// Expends one `period` of accumulated time.
6775
///
6876
/// [`Err(FixedUpdateError`)] will be returned if there is
6977
/// not enough accumulated time to span an entire period.
7078
pub fn expend(&mut self) -> Result<(), FixedUpdateError> {
7179
if let Some(new_value) = self.accumulated.checked_sub(self.period) {
7280
self.accumulated = new_value;
81+
self.total_ticks += 1;
7382
Ok(())
7483
} else {
7584
Err(FixedUpdateError::NotEnoughTime {
@@ -85,10 +94,15 @@ impl Default for FixedTime {
8594
FixedTime {
8695
accumulated: Duration::ZERO,
8796
period: Duration::from_secs_f32(1. / 60.),
97+
total_ticks: 0,
8898
}
8999
}
90100
}
91101

102+
/// Indicates that [`run_fixed_update_schedule`] is currently active.
103+
#[derive(Resource)]
104+
pub struct FixedUpdateScheduleIsCurrentlyRunning;
105+
92106
/// An error returned when working with [`FixedTime`].
93107
#[derive(Debug, Error)]
94108
pub enum FixedUpdateError {
@@ -101,6 +115,8 @@ pub enum FixedUpdateError {
101115

102116
/// Ticks the [`FixedTime`] resource then runs the [`FixedUpdate`].
103117
pub fn run_fixed_update_schedule(world: &mut World) {
118+
world.insert_resource(FixedUpdateScheduleIsCurrentlyRunning);
119+
104120
// Tick the time
105121
let delta_time = world.resource::<Time>().delta();
106122
let mut fixed_time = world.resource_mut::<FixedTime>();
@@ -112,6 +128,8 @@ pub fn run_fixed_update_schedule(world: &mut World) {
112128
schedule.run(world);
113129
}
114130
});
131+
132+
world.remove_resource::<FixedUpdateScheduleIsCurrentlyRunning>();
115133
}
116134

117135
#[cfg(test)]

0 commit comments

Comments
 (0)