Skip to content

Commit 6a8dfc8

Browse files
committed
exercises
1 parent f010191 commit 6a8dfc8

File tree

4 files changed

+148
-27
lines changed

4 files changed

+148
-27
lines changed

src/game.rs

Lines changed: 97 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::f32::consts::TAU;
1+
use std::{f32::consts::TAU, time::Duration};
22

33
use avian2d::prelude::*;
44
use bevy::prelude::*;
@@ -12,7 +12,7 @@ pub fn game_plugin(app: &mut App) {
1212
.add_systems(OnEnter(GameState::Game), display_level)
1313
.add_systems(
1414
Update,
15-
(collision, tick_explosion).run_if(in_state(GameState::Game)),
15+
(tick_explosion, laser_range, has_won).run_if(in_state(GameState::Game)),
1616
);
1717
}
1818

@@ -69,29 +69,6 @@ fn display_level(
6969
}
7070
}
7171

72-
fn collision(
73-
collisions: Collisions,
74-
player: Query<(&Transform, Entity), With<Player>>,
75-
mut commands: Commands,
76-
game_assets: Res<GameAssets>,
77-
) -> Result {
78-
let Ok((transform, entity)) = player.single() else {
79-
return Ok(());
80-
};
81-
82-
if collisions.collisions_with(entity).next().is_some() {
83-
commands.spawn((
84-
Sprite::from_image(game_assets.explosion.clone()),
85-
(*transform).with_scale(Vec3::splat(0.2)),
86-
Explosion(Timer::from_seconds(1.0, TimerMode::Once)),
87-
StateScoped(GameState::Game),
88-
));
89-
commands.entity(entity).despawn();
90-
}
91-
92-
Ok(())
93-
}
94-
9572
fn tick_explosion(
9673
mut commands: Commands,
9774
mut explosions: Query<(Entity, &mut Explosion, &Transform)>,
@@ -125,6 +102,7 @@ fn spawn_player(commands: &mut Commands, game_assets: &GameAssets, position: Vec
125102
negative: KeyCode::KeyD,
126103
});
127104
actions.bind::<Thrust>().to(KeyCode::KeyW);
105+
actions.bind::<FireLaser>().to(KeyCode::Space);
128106

129107
commands
130108
.spawn((
@@ -145,7 +123,9 @@ fn spawn_player(commands: &mut Commands, game_assets: &GameAssets, position: Vec
145123
))
146124
.observe(rotate)
147125
.observe(thrust)
148-
.observe(thrust_stop);
126+
.observe(thrust_stop)
127+
.observe(fire_laser)
128+
.observe(asteroid_collision);
149129
}
150130

151131
#[derive(InputContext)]
@@ -159,6 +139,10 @@ struct Rotate;
159139
#[input_action(output = bool)]
160140
struct Thrust;
161141

142+
#[derive(Debug, InputAction)]
143+
#[input_action(output = bool)]
144+
struct FireLaser;
145+
162146
fn rotate(
163147
trigger: Trigger<Fired<Rotate>>,
164148
mut player: Query<&mut AngularVelocity>,
@@ -192,11 +176,97 @@ fn thrust_stop(
192176
player: Query<&Children>,
193177
mut visibility: Query<&mut Visibility>,
194178
) -> Result {
195-
let children = player.get(trigger.target())?;
179+
let Ok(children) = player.get(trigger.target()) else {
180+
return Ok(());
181+
};
196182

197183
visibility
198184
.get_mut(children[0])?
199185
.set_if_neq(Visibility::Hidden);
200186

201187
Ok(())
202188
}
189+
190+
fn asteroid_collision(
191+
collision: Trigger<OnCollisionStart>,
192+
is_asteroid: Query<(), With<Asteroid>>,
193+
player: Query<&Transform>,
194+
mut commands: Commands,
195+
game_assets: Res<GameAssets>,
196+
) -> Result {
197+
if is_asteroid.get(collision.collider).is_ok() {
198+
let transform = player.get(collision.target())?;
199+
commands.spawn((
200+
Sprite::from_image(game_assets.explosion.clone()),
201+
(*transform).with_scale(Vec3::splat(0.2)),
202+
Explosion(Timer::from_seconds(1.0, TimerMode::Once)),
203+
StateScoped(GameState::Game),
204+
));
205+
commands.entity(collision.target()).despawn();
206+
commands.entity(collision.collider).despawn();
207+
}
208+
Ok(())
209+
}
210+
211+
#[derive(Component)]
212+
struct Laser(Timer);
213+
214+
fn fire_laser(
215+
trigger: Trigger<Fired<FireLaser>>,
216+
player: Query<&Transform>,
217+
mut commands: Commands,
218+
time: Res<Time>,
219+
mut last_fired: Local<Duration>,
220+
game_assets: Res<GameAssets>,
221+
) -> Result {
222+
let mut transform = *player.get(trigger.target())?;
223+
transform.translation += transform.local_y() * 40.0;
224+
transform.scale = Vec3::ONE / 2.0;
225+
226+
if time.elapsed() > *last_fired + Duration::from_secs_f32(0.5) {
227+
commands
228+
.spawn((
229+
// Sprite::from_image(game_assets.laser.clone()),
230+
Sprite {
231+
image: game_assets.laser.clone(),
232+
color: Color::srgb(5.0, 1.0, 1.0),
233+
..default()
234+
},
235+
transform,
236+
RigidBody::Dynamic,
237+
Collider::rectangle(4.0, 15.0),
238+
LinearVelocity(transform.local_y().xy() * 1000.0),
239+
Laser(Timer::from_seconds(1.0, TimerMode::Once)),
240+
CollisionEventsEnabled,
241+
StateScoped(GameState::Game),
242+
))
243+
.observe(laser_attack);
244+
*last_fired = time.elapsed();
245+
}
246+
Ok(())
247+
}
248+
249+
fn laser_range(mut commands: Commands, mut lasers: Query<(Entity, &mut Laser)>, time: Res<Time>) {
250+
for (entity, mut laser) in &mut lasers {
251+
if laser.0.tick(time.delta()).just_finished() {
252+
commands.entity(entity).despawn();
253+
}
254+
}
255+
}
256+
257+
fn laser_attack(
258+
collision: Trigger<OnCollisionStart>,
259+
is_asteroid: Query<(), With<Asteroid>>,
260+
mut commands: Commands,
261+
) {
262+
if is_asteroid.get(collision.collider).is_ok() {
263+
commands.entity(collision.collider).despawn();
264+
commands.entity(collision.target()).despawn();
265+
}
266+
}
267+
268+
fn has_won(asteroids: Query<(), With<Asteroid>>, mut next_state: ResMut<NextState<GameState>>) {
269+
if asteroids.is_empty() {
270+
next_state.set(GameState::Won);
271+
}
272+
}

src/main.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ mod hud;
88
mod level;
99
mod splash;
1010
mod start_menu;
11+
mod won;
1112

1213
fn main() {
1314
App::new()
@@ -28,6 +29,7 @@ fn main() {
2829
game::game_plugin,
2930
level::level_loader_plugin,
3031
hud::hud_plugin,
32+
won::won_plugin,
3133
))
3234
.run();
3335
}
@@ -38,6 +40,7 @@ enum GameState {
3840
Splash,
3941
StartMenu,
4042
Game,
43+
Won,
4144
}
4245

4346
#[derive(Resource)]
@@ -46,6 +49,7 @@ struct GameAssets {
4649
asteroid: Handle<Image>,
4750
jets: Handle<Image>,
4851
explosion: Handle<Image>,
52+
laser: Handle<Image>,
4953
}
5054

5155
#[derive(Resource)]

src/splash.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ fn load_assets(mut commands: Commands, asset_server: Res<AssetServer>) {
6060
asteroid: asset_server.load("meteorBrown_big1.png"),
6161
jets: asset_server.load("fire07.png"),
6262
explosion: asset_server.load("explosion00.png"),
63+
laser: asset_server.load("laserRed07.png"),
6364
});
6465
commands.insert_resource(LoadedLevel {
6566
level: asset_server.load("level.bw"),

src/won.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
use bevy::{color::palettes, prelude::*};
2+
3+
use crate::GameState;
4+
5+
pub fn won_plugin(app: &mut App) {
6+
app.add_systems(OnEnter(GameState::Won), display_text)
7+
.add_systems(Update, back_to_menu.run_if(in_state(GameState::Won)));
8+
}
9+
10+
fn display_text(mut commands: Commands) {
11+
commands.spawn((
12+
Node {
13+
width: Val::Percent(100.0),
14+
height: Val::Percent(100.0),
15+
align_items: AlignItems::Center,
16+
justify_content: JustifyContent::Center,
17+
flex_direction: FlexDirection::Column,
18+
..default()
19+
},
20+
children![
21+
(
22+
Text::new("You Won!"),
23+
TextFont {
24+
font_size: 200.0,
25+
..default()
26+
},
27+
TextColor::from(palettes::tailwind::RED_600),
28+
),
29+
(
30+
Text::new("Press any key to go back to the menu"),
31+
TextFont {
32+
font_size: 50.0,
33+
..default()
34+
},
35+
TextColor::from(palettes::tailwind::RED_800),
36+
)
37+
],
38+
StateScoped(GameState::Won),
39+
));
40+
}
41+
42+
fn back_to_menu(keyboard: Res<ButtonInput<KeyCode>>, mut next: ResMut<NextState<GameState>>) {
43+
if keyboard.get_just_pressed().next().is_some() {
44+
next.set(GameState::StartMenu);
45+
}
46+
}

0 commit comments

Comments
 (0)