
# Objective Extracts the state mechanisms into a new crate called "bevy_state". This comes with a few goals: - state wasn't really an inherent machinery of the ecs system, and so keeping it within bevy_ecs felt forced - by mixing it in with bevy_ecs, the maintainability of our more robust state system was significantly compromised moving state into a new crate makes it easier to encapsulate as it's own feature, and easier to read and understand since it's no longer a single, massive file. ## Solution move the state-related elements from bevy_ecs to a new crate ## Testing - Did you test these changes? If so, how? all the automated tests migrated and passed, ran the pre-existing examples without changes to validate. --- ## Migration Guide Since bevy_state is now gated behind the `bevy_state` feature, projects that use state but don't use the `default-features` will need to add that feature flag. Since it is no longer part of bevy_ecs, projects that use bevy_ecs directly will need to manually pull in `bevy_state`, trigger the StateTransition schedule, and handle any of the elements that bevy_app currently sets up. --------- Co-authored-by: Kristoffer Søholm <k.soeholm@gmail.com>
309 lines
12 KiB
Rust
309 lines
12 KiB
Rust
use crate::{App, Plugin};
|
|
use bevy_ecs::{
|
|
schedule::{ExecutorKind, InternedScheduleLabel, Schedule, ScheduleLabel},
|
|
system::{Local, Resource},
|
|
world::{Mut, World},
|
|
};
|
|
#[cfg(feature = "bevy_state")]
|
|
use bevy_state::state::StateTransition;
|
|
|
|
/// The schedule that contains the app logic that is evaluated each tick of [`App::update()`].
|
|
///
|
|
/// By default, it will run the following schedules in the given order:
|
|
///
|
|
/// On the first run of the schedule (and only on the first run), it will run:
|
|
/// * [`PreStartup`]
|
|
/// * [`Startup`]
|
|
/// * [`PostStartup`]
|
|
///
|
|
/// Then it will run:
|
|
/// * [`First`]
|
|
/// * [`PreUpdate`]
|
|
/// * [`StateTransition`]
|
|
/// * [`RunFixedMainLoop`]
|
|
/// * This will run [`FixedMain`] zero to many times, based on how much time has elapsed.
|
|
/// * [`Update`]
|
|
/// * [`PostUpdate`]
|
|
/// * [`Last`]
|
|
///
|
|
/// # Rendering
|
|
///
|
|
/// Note rendering is not executed in the main schedule by default.
|
|
/// Instead, rendering is performed in a separate [`SubApp`]
|
|
/// which exchanges data with the main app in between the main schedule runs.
|
|
///
|
|
/// See [`RenderPlugin`] and [`PipelinedRenderingPlugin`] for more details.
|
|
///
|
|
/// [`RenderPlugin`]: https://docs.rs/bevy/latest/bevy/render/struct.RenderPlugin.html
|
|
/// [`PipelinedRenderingPlugin`]: https://docs.rs/bevy/latest/bevy/render/pipelined_rendering/struct.PipelinedRenderingPlugin.html
|
|
/// [`SubApp`]: crate::SubApp
|
|
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
|
|
pub struct Main;
|
|
|
|
/// The schedule that runs before [`Startup`].
|
|
///
|
|
/// See the [`Main`] schedule for some details about how schedules are run.
|
|
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
|
|
pub struct PreStartup;
|
|
|
|
/// The schedule that runs once when the app starts.
|
|
///
|
|
/// See the [`Main`] schedule for some details about how schedules are run.
|
|
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
|
|
pub struct Startup;
|
|
|
|
/// The schedule that runs once after [`Startup`].
|
|
///
|
|
/// See the [`Main`] schedule for some details about how schedules are run.
|
|
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
|
|
pub struct PostStartup;
|
|
|
|
/// Runs first in the schedule.
|
|
///
|
|
/// See the [`Main`] schedule for some details about how schedules are run.
|
|
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
|
|
pub struct First;
|
|
|
|
/// The schedule that contains logic that must run before [`Update`]. For example, a system that reads raw keyboard
|
|
/// input OS events into an `Events` resource. This enables systems in [`Update`] to consume the events from the `Events`
|
|
/// resource without actually knowing about (or taking a direct scheduler dependency on) the "os-level keyboard event system".
|
|
///
|
|
/// [`PreUpdate`] exists to do "engine/plugin preparation work" that ensures the APIs consumed in [`Update`] are "ready".
|
|
/// [`PreUpdate`] abstracts out "pre work implementation details".
|
|
///
|
|
/// See the [`Main`] schedule for some details about how schedules are run.
|
|
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
|
|
pub struct PreUpdate;
|
|
|
|
/// Runs the [`FixedMain`] schedule in a loop according until all relevant elapsed time has been "consumed".
|
|
///
|
|
/// See the [`Main`] schedule for some details about how schedules are run.
|
|
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
|
|
pub struct RunFixedMainLoop;
|
|
|
|
/// Runs first in the [`FixedMain`] schedule.
|
|
///
|
|
/// See the [`FixedMain`] schedule for details on how fixed updates work.
|
|
/// See the [`Main`] schedule for some details about how schedules are run.
|
|
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
|
|
pub struct FixedFirst;
|
|
|
|
/// The schedule that contains logic that must run before [`FixedUpdate`].
|
|
///
|
|
/// See the [`FixedMain`] schedule for details on how fixed updates work.
|
|
/// See the [`Main`] schedule for some details about how schedules are run.
|
|
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
|
|
pub struct FixedPreUpdate;
|
|
|
|
/// The schedule that contains most gameplay logic.
|
|
///
|
|
/// See the [`FixedMain`] schedule for details on how fixed updates work.
|
|
/// See the [`Main`] schedule for some details about how schedules are run.
|
|
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
|
|
pub struct FixedUpdate;
|
|
|
|
/// The schedule that runs after the [`FixedUpdate`] schedule, for reacting
|
|
/// to changes made in the main update logic.
|
|
///
|
|
/// See the [`FixedMain`] schedule for details on how fixed updates work.
|
|
/// See the [`Main`] schedule for some details about how schedules are run.
|
|
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
|
|
pub struct FixedPostUpdate;
|
|
|
|
/// The schedule that runs last in [`FixedMain`]
|
|
///
|
|
/// See the [`FixedMain`] schedule for details on how fixed updates work.
|
|
/// See the [`Main`] schedule for some details about how schedules are run.
|
|
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
|
|
pub struct FixedLast;
|
|
|
|
/// The schedule that contains systems which only run after a fixed period of time has elapsed.
|
|
///
|
|
/// The exclusive `run_fixed_main_schedule` system runs this schedule.
|
|
/// This is run by the [`RunFixedMainLoop`] schedule.
|
|
///
|
|
/// Frequency of execution is configured by inserting `Time<Fixed>` resource, 64 Hz by default.
|
|
/// See [this example](https://github.com/bevyengine/bevy/blob/latest/examples/time/time.rs).
|
|
///
|
|
/// See the [`Main`] schedule for some details about how schedules are run.
|
|
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
|
|
pub struct FixedMain;
|
|
|
|
/// The schedule that contains app logic. Ideally containing anything that must run once per
|
|
/// render frame, such as UI.
|
|
///
|
|
/// See the [`Main`] schedule for some details about how schedules are run.
|
|
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
|
|
pub struct Update;
|
|
|
|
/// The schedule that contains scene spawning.
|
|
///
|
|
/// See the [`Main`] schedule for some details about how schedules are run.
|
|
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
|
|
pub struct SpawnScene;
|
|
|
|
/// The schedule that contains logic that must run after [`Update`]. For example, synchronizing "local transforms" in a hierarchy
|
|
/// to "global" absolute transforms. This enables the [`PostUpdate`] transform-sync system to react to "local transform" changes in
|
|
/// [`Update`] without the [`Update`] systems needing to know about (or add scheduler dependencies for) the "global transform sync system".
|
|
///
|
|
/// [`PostUpdate`] exists to do "engine/plugin response work" to things that happened in [`Update`].
|
|
/// [`PostUpdate`] abstracts out "implementation details" from users defining systems in [`Update`].
|
|
///
|
|
/// See the [`Main`] schedule for some details about how schedules are run.
|
|
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
|
|
pub struct PostUpdate;
|
|
|
|
/// Runs last in the schedule.
|
|
///
|
|
/// See the [`Main`] schedule for some details about how schedules are run.
|
|
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
|
|
pub struct Last;
|
|
|
|
/// Defines the schedules to be run for the [`Main`] schedule, including
|
|
/// their order.
|
|
#[derive(Resource, Debug)]
|
|
pub struct MainScheduleOrder {
|
|
/// The labels to run for the main phase of the [`Main`] schedule (in the order they will be run).
|
|
pub labels: Vec<InternedScheduleLabel>,
|
|
/// The labels to run for the startup phase of the [`Main`] schedule (in the order they will be run).
|
|
pub startup_labels: Vec<InternedScheduleLabel>,
|
|
}
|
|
|
|
impl Default for MainScheduleOrder {
|
|
fn default() -> Self {
|
|
Self {
|
|
labels: vec![
|
|
First.intern(),
|
|
PreUpdate.intern(),
|
|
StateTransition.intern(),
|
|
RunFixedMainLoop.intern(),
|
|
Update.intern(),
|
|
SpawnScene.intern(),
|
|
PostUpdate.intern(),
|
|
Last.intern(),
|
|
],
|
|
startup_labels: vec![PreStartup.intern(), Startup.intern(), PostStartup.intern()],
|
|
}
|
|
}
|
|
}
|
|
|
|
impl MainScheduleOrder {
|
|
/// Adds the given `schedule` after the `after` schedule in the main list of schedules.
|
|
pub fn insert_after(&mut self, after: impl ScheduleLabel, schedule: impl ScheduleLabel) {
|
|
let index = self
|
|
.labels
|
|
.iter()
|
|
.position(|current| (**current).eq(&after))
|
|
.unwrap_or_else(|| panic!("Expected {after:?} to exist"));
|
|
self.labels.insert(index + 1, schedule.intern());
|
|
}
|
|
|
|
/// Adds the given `schedule` after the `after` schedule in the list of startup schedules.
|
|
pub fn insert_startup_after(
|
|
&mut self,
|
|
after: impl ScheduleLabel,
|
|
schedule: impl ScheduleLabel,
|
|
) {
|
|
let index = self
|
|
.startup_labels
|
|
.iter()
|
|
.position(|current| (**current).eq(&after))
|
|
.unwrap_or_else(|| panic!("Expected {after:?} to exist"));
|
|
self.startup_labels.insert(index + 1, schedule.intern());
|
|
}
|
|
}
|
|
|
|
impl Main {
|
|
/// A system that runs the "main schedule"
|
|
pub fn run_main(world: &mut World, mut run_at_least_once: Local<bool>) {
|
|
if !*run_at_least_once {
|
|
world.resource_scope(|world, order: Mut<MainScheduleOrder>| {
|
|
for &label in &order.startup_labels {
|
|
let _ = world.try_run_schedule(label);
|
|
}
|
|
});
|
|
*run_at_least_once = true;
|
|
}
|
|
|
|
world.resource_scope(|world, order: Mut<MainScheduleOrder>| {
|
|
for &label in &order.labels {
|
|
let _ = world.try_run_schedule(label);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
/// Initializes the [`Main`] schedule, sub schedules, and resources for a given [`App`].
|
|
pub struct MainSchedulePlugin;
|
|
|
|
impl Plugin for MainSchedulePlugin {
|
|
fn build(&self, app: &mut App) {
|
|
// simple "facilitator" schedules benefit from simpler single threaded scheduling
|
|
let mut main_schedule = Schedule::new(Main);
|
|
main_schedule.set_executor_kind(ExecutorKind::SingleThreaded);
|
|
let mut fixed_main_schedule = Schedule::new(FixedMain);
|
|
fixed_main_schedule.set_executor_kind(ExecutorKind::SingleThreaded);
|
|
let mut fixed_main_loop_schedule = Schedule::new(RunFixedMainLoop);
|
|
fixed_main_loop_schedule.set_executor_kind(ExecutorKind::SingleThreaded);
|
|
|
|
app.add_schedule(main_schedule)
|
|
.add_schedule(fixed_main_schedule)
|
|
.add_schedule(fixed_main_loop_schedule)
|
|
.init_resource::<MainScheduleOrder>()
|
|
.init_resource::<FixedMainScheduleOrder>()
|
|
.add_systems(Main, Main::run_main)
|
|
.add_systems(FixedMain, FixedMain::run_fixed_main);
|
|
|
|
#[cfg(feature = "bevy_debug_stepping")]
|
|
{
|
|
use bevy_ecs::schedule::{IntoSystemConfigs, Stepping};
|
|
app.add_systems(Main, Stepping::begin_frame.before(Main::run_main));
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Defines the schedules to be run for the [`FixedMain`] schedule, including
|
|
/// their order.
|
|
#[derive(Resource, Debug)]
|
|
pub struct FixedMainScheduleOrder {
|
|
/// The labels to run for the [`FixedMain`] schedule (in the order they will be run).
|
|
pub labels: Vec<InternedScheduleLabel>,
|
|
}
|
|
|
|
impl Default for FixedMainScheduleOrder {
|
|
fn default() -> Self {
|
|
Self {
|
|
labels: vec![
|
|
FixedFirst.intern(),
|
|
FixedPreUpdate.intern(),
|
|
FixedUpdate.intern(),
|
|
FixedPostUpdate.intern(),
|
|
FixedLast.intern(),
|
|
],
|
|
}
|
|
}
|
|
}
|
|
|
|
impl FixedMainScheduleOrder {
|
|
/// Adds the given `schedule` after the `after` schedule
|
|
pub fn insert_after(&mut self, after: impl ScheduleLabel, schedule: impl ScheduleLabel) {
|
|
let index = self
|
|
.labels
|
|
.iter()
|
|
.position(|current| (**current).eq(&after))
|
|
.unwrap_or_else(|| panic!("Expected {after:?} to exist"));
|
|
self.labels.insert(index + 1, schedule.intern());
|
|
}
|
|
}
|
|
|
|
impl FixedMain {
|
|
/// A system that runs the fixed timestep's "main schedule"
|
|
pub fn run_fixed_main(world: &mut World) {
|
|
world.resource_scope(|world, order: Mut<FixedMainScheduleOrder>| {
|
|
for &label in &order.labels {
|
|
let _ = world.try_run_schedule(label);
|
|
}
|
|
});
|
|
}
|
|
}
|