Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/examples/ecs/state_scoped.rs
9298 views
1
//! Shows how to spawn entities that are automatically despawned either when
2
//! entering or exiting specific game states.
3
//!
4
//! This pattern is useful for managing menus, levels, or other state-specific
5
//! content that should only exist during certain states.
6
//!
7
//! If the entity was already despawned then no error will be logged. This means
8
//! that you don't have to worry about duplicate [`DespawnOnExit`] and
9
//! [`DespawnOnEnter`] components deep in your hierarchy.
10
11
use bevy::prelude::*;
12
13
fn main() {
14
App::new()
15
.add_plugins(DefaultPlugins)
16
.init_state::<GameState>()
17
.add_systems(Startup, setup_camera)
18
.add_systems(OnEnter(GameState::A), on_a_enter)
19
.add_systems(OnEnter(GameState::B), on_b_enter)
20
.add_systems(OnExit(GameState::A), on_a_exit)
21
.add_systems(OnExit(GameState::B), on_b_exit)
22
.add_systems(Update, toggle)
23
.insert_resource(TickTock(Timer::from_seconds(1.0, TimerMode::Repeating)))
24
.run();
25
}
26
27
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default, States)]
28
enum GameState {
29
#[default]
30
A,
31
B,
32
}
33
34
#[derive(Resource)]
35
struct TickTock(Timer);
36
37
fn on_a_enter(mut commands: Commands) {
38
info!("on_a_enter");
39
commands.spawn((
40
DespawnOnExit(GameState::A),
41
Text::new("Game is in state 'A'"),
42
TextFont {
43
font_size: FontSize::Px(33.0),
44
..default()
45
},
46
TextColor(Color::srgb(0.5, 0.5, 1.0)),
47
Node {
48
position_type: PositionType::Absolute,
49
top: px(0),
50
left: px(0),
51
..default()
52
},
53
(children![DespawnOnExit(GameState::A)]),
54
));
55
}
56
57
fn on_a_exit(mut commands: Commands) {
58
info!("on_a_exit");
59
commands.spawn((
60
DespawnOnEnter(GameState::A),
61
Text::new("Game state 'A' will be back in 1 second"),
62
TextFont {
63
font_size: FontSize::Px(33.0),
64
..default()
65
},
66
TextColor(Color::srgb(0.5, 0.5, 1.0)),
67
Node {
68
position_type: PositionType::Absolute,
69
top: px(0),
70
left: px(500),
71
..default()
72
},
73
// You can apply this even when the parent has a state scoped component.
74
// It is unnecessary but in complex hierarchies it saves you from having to
75
// mentally track which components are found at the top level.
76
(children![DespawnOnEnter(GameState::A)]),
77
));
78
}
79
80
fn on_b_enter(mut commands: Commands) {
81
info!("on_b_enter");
82
commands.spawn((
83
DespawnOnExit(GameState::B),
84
Text::new("Game is in state 'B'"),
85
TextFont {
86
font_size: FontSize::Px(33.0),
87
..default()
88
},
89
TextColor(Color::srgb(0.5, 0.5, 1.0)),
90
Node {
91
position_type: PositionType::Absolute,
92
top: px(50),
93
left: px(0),
94
..default()
95
},
96
(children![DespawnOnExit(GameState::B)]),
97
));
98
}
99
100
fn on_b_exit(mut commands: Commands) {
101
info!("on_b_exit");
102
commands.spawn((
103
DespawnOnEnter(GameState::B),
104
Text::new("Game state 'B' will be back in 1 second"),
105
TextFont {
106
font_size: FontSize::Px(33.0),
107
..default()
108
},
109
TextColor(Color::srgb(0.5, 0.5, 1.0)),
110
Node {
111
position_type: PositionType::Absolute,
112
top: px(50),
113
left: px(500),
114
..default()
115
},
116
(children![DespawnOnEnter(GameState::B)]),
117
));
118
}
119
120
fn setup_camera(mut commands: Commands) {
121
commands.spawn(Camera3d::default());
122
}
123
124
fn toggle(
125
time: Res<Time>,
126
mut timer: ResMut<TickTock>,
127
state: Res<State<GameState>>,
128
mut next_state: ResMut<NextState<GameState>>,
129
) {
130
if !timer.0.tick(time.delta()).is_finished() {
131
return;
132
}
133
*next_state = match state.get() {
134
GameState::A => NextState::Pending(GameState::B),
135
GameState::B => NextState::Pending(GameState::A),
136
}
137
}
138
139