1use bevy::prelude::*;
12
13fn 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(OnEnter(GameState::C(1)), on_c_1_enter)
23 .add_systems(OnExit(GameState::C(1)), on_c_1_exit)
24 .add_systems(Update, toggle)
25 .insert_resource(TickTock(Timer::from_seconds(1.0, TimerMode::Repeating)))
26 .run();
27}
28
29#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default, States)]
30enum GameState {
31 #[default]
32 A,
33 B,
34 C(u8),
35}
36
37#[derive(Resource)]
38struct TickTock(Timer);
39
40fn on_a_enter(mut commands: Commands) {
41 info!("on_a_enter");
42 commands.spawn((
43 DespawnOnExit(GameState::A),
44 Text::new("Game is in state 'A'"),
45 TextFont {
46 font_size: FontSize::Px(33.0),
47 ..default()
48 },
49 TextColor(Color::srgb(0.5, 0.5, 1.0)),
50 Node {
51 position_type: PositionType::Absolute,
52 top: px(0),
53 left: px(0),
54 ..default()
55 },
56 (children![DespawnOnExit(GameState::A)]),
57 ));
58}
59
60fn on_a_exit(mut commands: Commands) {
61 info!("on_a_exit");
62 commands.spawn((
63 DespawnOnEnter(GameState::A),
64 Text::new("Game state 'A' will be back in 1 second"),
65 TextFont {
66 font_size: FontSize::Px(33.0),
67 ..default()
68 },
69 TextColor(Color::srgb(0.5, 0.5, 1.0)),
70 Node {
71 position_type: PositionType::Absolute,
72 top: px(0),
73 left: px(500),
74 ..default()
75 },
76 (children![DespawnOnEnter(GameState::A)]),
80 ));
81}
82
83fn on_b_enter(mut commands: Commands) {
84 info!("on_b_enter");
85 commands.spawn((
86 DespawnOnExit(GameState::B),
87 Text::new("Game is in state 'B'"),
88 TextFont {
89 font_size: FontSize::Px(33.0),
90 ..default()
91 },
92 TextColor(Color::srgb(0.5, 0.5, 1.0)),
93 Node {
94 position_type: PositionType::Absolute,
95 top: px(50),
96 left: px(0),
97 ..default()
98 },
99 (children![DespawnOnExit(GameState::B)]),
100 ));
101}
102
103fn on_b_exit(mut commands: Commands) {
104 info!("on_b_exit");
105 commands.spawn((
106 DespawnOnEnter(GameState::B),
107 Text::new("Game state 'B' will be back in 1 second"),
108 TextFont {
109 font_size: FontSize::Px(33.0),
110 ..default()
111 },
112 TextColor(Color::srgb(0.5, 0.5, 1.0)),
113 Node {
114 position_type: PositionType::Absolute,
115 top: px(50),
116 left: px(500),
117 ..default()
118 },
119 (children![DespawnOnEnter(GameState::B)]),
120 ));
121}
122
123fn on_c_1_enter(mut commands: Commands) {
124 info!("on_c_1_enter");
125 commands.spawn((
126 DespawnWhen::new(|transition| matches!(transition.exited, Some(GameState::C(_)))),
127 Text::new("Game is in state 'C(1)'"),
128 TextFont {
129 font_size: FontSize::Px(33.0),
130 ..default()
131 },
132 TextColor(Color::srgb(0.5, 0.5, 1.0)),
133 Node {
134 position_type: PositionType::Absolute,
135 top: px(100),
136 left: px(0),
137 ..default()
138 },
139 (children![DespawnWhen::new(|transition| matches!(
140 transition.exited,
141 Some(GameState::C(_))
142 ))]),
143 ));
144}
145
146fn on_c_1_exit(mut commands: Commands) {
147 info!("on_c_1_exit");
148 commands.spawn((
149 DespawnWhen::new(|transition| matches!(transition.entered, Some(GameState::C(1)))),
150 Text::new("Game state 'C(1)' will be back in 1 second"),
151 TextFont {
152 font_size: FontSize::Px(33.0),
153 ..default()
154 },
155 TextColor(Color::srgb(0.5, 0.5, 1.0)),
156 Node {
157 position_type: PositionType::Absolute,
158 top: px(100),
159 left: px(500),
160 ..default()
161 },
162 (children![DespawnWhen::new(|transition| matches!(
163 transition.entered,
164 Some(GameState::C(_))
165 ))]),
166 ));
167}
168
169fn setup_camera(mut commands: Commands) {
170 commands.spawn(Camera3d::default());
171}
172
173fn toggle(
174 time: Res<Time>,
175 mut timer: ResMut<TickTock>,
176 state: Res<State<GameState>>,
177 mut next_state: ResMut<NextState<GameState>>,
178) {
179 if !timer.0.tick(time.delta()).is_finished() {
180 return;
181 }
182 *next_state = match state.get() {
183 GameState::A => NextState::Pending(GameState::B),
184 GameState::B => NextState::Pending(GameState::C(1)),
185 GameState::C(_) => NextState::Pending(GameState::A),
186 }
187}