7 Commits

Author SHA1 Message Date
eecfbf5d7c Dev version bump to 0.6.0-dev3
Some checks failed
Basic checks / Basic build-and-test supertask (push) Failing after 1m2s
I'm moving on from asset stuff, so I'm considering this a "new version."

Next up: Fixing the Makefile to understand the sound files.
2025-12-19 10:41:08 -06:00
1e09e3ce3a Fix: Include the sound asset for the thruster!
I forgot to check-in the file the other day. Big brain here in this
head.
2025-12-19 10:40:28 -06:00
19ac032792 Add proper meshes & materials for game objects
The asteroid circles have been replaced with polylines. I was going to
do a regular triangle mesh, but I don't want to figure out GLTF
loading or manually chunking the points into convex hulls.

Asteroid materials are now all populated and their GameAsset getters are
indexed correctly. I'm not sold on the size based color selection.
Later, I think I'll either remove the extra colors or let each of them
randomly apply to any asteroid.

The bullet is now a short line segment. Apparently I already wired in
the rotation logic, so pointing it correctly out of the ship already
works.
2025-12-19 10:29:48 -06:00
de75e25ca6 Add ship thruster sound
The player's Ship now has an AudioPlayer component constantly looping a
thruster sound effect. It starts paused and is only resumed when the
player fires the thruster.

As noted in the TODO comment at the top of the input_ship_thruster(...)
system, I need to figure out if I want to start using the `Single<>`
query parameter instead of a `Query<>` and then doing my own
null-ability checks (like what it does now).
2025-12-17 17:18:18 -06:00
d7802bdbed Add asteroid destruction sound 2025-12-17 16:18:31 -06:00
ae093d2c9c Add bullet/laser sound
Firing the weapon now makes a sound. I've implemented this by spawning
the playback component on the bullet rather than the gun. This seemed
easier than figuring out how to reset a playback component that lives on
the ship entity -- although thats probably better for memory access
patterns.
2025-12-17 14:29:57 -06:00
3963b548b9 Fix: Make shipwreck sound entity despawn itself
I started to work on weapon fire sounds and realized I don't know if the
entity and/or component get removed after playback ends. It turns out
that they DO NOT!... unless told to do so, like this.
2025-12-17 14:23:57 -06:00
9 changed files with 150 additions and 42 deletions

26
Cargo.lock generated
View File

@@ -242,7 +242,7 @@ dependencies = [
[[package]] [[package]]
name = "asteroids" name = "asteroids"
version = "0.6.0-dev2" version = "0.6.0-dev3"
dependencies = [ dependencies = [
"bevy", "bevy",
"bevy-inspector-egui", "bevy-inspector-egui",
@@ -1848,9 +1848,9 @@ dependencies = [
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.2.49" version = "1.2.50"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90583009037521a116abf44494efecd645ba48b6622457080f080b85544e2215" checksum = "9f50d563227a1c37cc0a263f64eca3334388c01c5e4c4861a9def205c614383c"
dependencies = [ dependencies = [
"find-msvc-tools", "find-msvc-tools",
"jobserver", "jobserver",
@@ -4470,9 +4470,9 @@ checksum = "c3d6831663a5098ea164f89cff59c6284e95f4e3c76ce9848d4529f5ccca9bde"
[[package]] [[package]]
name = "rangemap" name = "rangemap"
version = "1.7.0" version = "1.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acbbbbea733ec66275512d0b9694f34102e7d5406fdbe2ad8d21b28dce92887c" checksum = "973443cf09a9c8656b574a866ab68dfa19f0867d0340648c7d2f6a71b8a8ea68"
[[package]] [[package]]
name = "rapier2d" name = "rapier2d"
@@ -5167,9 +5167,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]] [[package]]
name = "toml_datetime" name = "toml_datetime"
version = "0.7.4+spec-1.0.0" version = "0.7.5+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe3cea6b2aa3b910092f6abd4053ea464fab5f9c170ba5e9a6aead16ec4af2b6" checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347"
dependencies = [ dependencies = [
"serde_core", "serde_core",
] ]
@@ -5188,18 +5188,18 @@ dependencies = [
[[package]] [[package]]
name = "toml_parser" name = "toml_parser"
version = "1.0.5+spec-1.0.0" version = "1.0.6+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c03bee5ce3696f31250db0bbaff18bc43301ce0e8db2ed1f07cbb2acf89984c" checksum = "a3198b4b0a8e11f09dd03e133c0280504d0801269e9afa46362ffde1cbeebf44"
dependencies = [ dependencies = [
"winnow", "winnow",
] ]
[[package]] [[package]]
name = "tracing" name = "tracing"
version = "0.1.43" version = "0.1.44"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d15d90a0b5c19378952d479dc858407149d7bb45a14de0142f6c534b16fc647" checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100"
dependencies = [ dependencies = [
"pin-project-lite", "pin-project-lite",
"tracing-attributes", "tracing-attributes",
@@ -5219,9 +5219,9 @@ dependencies = [
[[package]] [[package]]
name = "tracing-core" name = "tracing-core"
version = "0.1.35" version = "0.1.36"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a04e24fab5c89c6a36eb8558c9656f30d81de51dfa4d3b45f26b21d61fa0a6c" checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a"
dependencies = [ dependencies = [
"once_cell", "once_cell",
"valuable", "valuable",

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "asteroids" name = "asteroids"
version = "0.6.0-dev2" version = "0.6.0-dev3"
edition = "2024" edition = "2024"
license = "AGPL-3.0-only" license = "AGPL-3.0-only"

Binary file not shown.

BIN
assets/laserSmall_001.ogg Normal file

Binary file not shown.

BIN
assets/thrusterFire_004.ogg Normal file

Binary file not shown.

View File

@@ -9,19 +9,21 @@ pub const UI_BUTTON_NORMAL: Color = Color::srgb(0.15, 0.15, 0.15); // Button col
pub const UI_BUTTON_HOVERED: Color = Color::srgb(0.25, 0.25, 0.25); // ... when it's hovered pub const UI_BUTTON_HOVERED: Color = Color::srgb(0.25, 0.25, 0.25); // ... when it's hovered
pub const UI_BUTTON_PRESSED: Color = Color::srgb(0.55, 0.55, 0.55); // ... when it's pressed pub const UI_BUTTON_PRESSED: Color = Color::srgb(0.55, 0.55, 0.55); // ... when it's pressed
pub(crate) const BACKGROUND_COLOR: Color = Color::srgb(0.3, 0.3, 0.3); pub(crate) const BACKGROUND_COLOR: Color = Color::srgb(0.1, 0.1, 0.1);
pub(crate) const PLAYER_SHIP_COLOR: Color = Color::srgb(1.0, 1.0, 1.0); pub(crate) const PLAYER_SHIP_COLOR: Color = Color::srgb(1.0, 1.0, 1.0);
pub(crate) const SHIP_THRUSTER_COLOR_ACTIVE: Color = Color::srgb(1.0, 0.2, 0.2); pub(crate) const SHIP_THRUSTER_COLOR_ACTIVE: Color = Color::srgb(1.0, 0.2, 0.2);
pub(crate) const SHIP_THRUSTER_COLOR_INACTIVE: Color = Color::srgb(0.5, 0.5, 0.5); pub(crate) const SHIP_THRUSTER_COLOR_INACTIVE: Color = Color::srgb(0.5, 0.5, 0.5);
pub(crate) const SHIP_FIRE_RATE: f32 = 3.0; // in bullets-per-second pub(crate) const SHIP_FIRE_RATE: f32 = 3.0; // in bullets-per-second
pub(crate) const ASTEROID_SMALL_COLOR: Color = Color::srgb(1.0, 0., 0.); pub(crate) const ASTEROID_SMALL_COLOR: Color = Color::srgb(0.9, 0.9, 0.9);
pub(crate) const BULLET_COLOR: Color = Color::srgb(0.0, 0.1, 0.9); pub(crate) const ASTEROID_MEDIUM_COLOR: Color = Color::srgb(0.8, 0.8, 0.8);
pub(crate) const ASTEROID_LARGE_COLOR: Color = Color::srgb(0.6, 0.6, 0.6);
pub(crate) const BULLET_COLOR: Color = Color::srgb(0.9, 0.9, 0.9);
// TODO: asteroid medium & large // TODO: asteroid medium & large
pub(crate) const SHIP_THRUST: f32 = 1.0; pub(crate) const SHIP_THRUST: f32 = 4.0;
pub(crate) const SHIP_ROTATION: f32 = 4.0; // +/- rotation speed in... radians per frame pub(crate) const SHIP_ROTATION: f32 = 4.0; // +/- rotation speed in... radians per frame
pub(crate) const BULLET_SPEED: f32 = 150.0; pub(crate) const BULLET_SPEED: f32 = 500.0;
pub(crate) const BULLET_LIFETIME: f32 = 2.0; pub(crate) const BULLET_LIFETIME: f32 = 2.0;
pub(crate) const ASTEROID_LIFETIME: f32 = 40.0; pub(crate) const ASTEROID_LIFETIME: f32 = 40.0;

View File

@@ -11,9 +11,9 @@ mod resources;
mod widgets; mod widgets;
use crate::config::{ use crate::config::{
ASTEROID_SMALL_COLOR, BACKGROUND_COLOR, BULLET_COLOR, BULLET_LIFETIME, BULLET_SPEED, ASTEROID_LARGE_COLOR, ASTEROID_MEDIUM_COLOR, ASTEROID_SMALL_COLOR, BACKGROUND_COLOR,
PLAYER_SHIP_COLOR, SHIP_ROTATION, SHIP_THRUST, SHIP_THRUSTER_COLOR_ACTIVE, BULLET_COLOR, BULLET_LIFETIME, BULLET_SPEED, PLAYER_SHIP_COLOR, SHIP_ROTATION, SHIP_THRUST,
SHIP_THRUSTER_COLOR_INACTIVE, SHIP_THRUSTER_COLOR_ACTIVE, SHIP_THRUSTER_COLOR_INACTIVE,
}; };
use crate::machinery::AsteroidSpawner; use crate::machinery::AsteroidSpawner;
use crate::objects::{Bullet, Ship, Weapon}; use crate::objects::{Bullet, Ship, Weapon};
@@ -111,14 +111,28 @@ fn spawn_camera(mut commands: Commands) {
/// Checks if "W" is pressed and increases velocity accordingly. /// Checks if "W" is pressed and increases velocity accordingly.
fn input_ship_thruster( fn input_ship_thruster(
keyboard_input: Res<ButtonInput<KeyCode>>, keyboard_input: Res<ButtonInput<KeyCode>>,
mut query: Query<(&mut physics::Velocity, &Transform, &mut Children), With<Ship>>, mut query: Query<
(
&mut physics::Velocity,
&Transform,
Option<&mut AudioSink>,
&mut Children,
),
With<Ship>,
>,
mut commands: Commands, mut commands: Commands,
game_assets: Res<GameAssets>, game_assets: Res<GameAssets>,
) { ) {
// TODO: Maybe change for a Single<Ship>> so this only runs for the one ship // TODO: Maybe change for a Single<Ship>> so this only runs for the one ship
// buuut... that would silently do nothing if there are 0 or >1 ships, and // buuut... that would silently do nothing if there are 0 or >1 ships, and
// I might want to crash on purpose in that case. // I might want to crash on purpose in that case.
let Ok((mut velocity, transform, children)) = query.single_mut() else { //
// The AudioSink component doesn't exist for just one frame, forcing it to
// be an optional system parameter. I'm not sure if I want to guard it with
// a check like it does now, or finally switch to using a Single<...> query
// parameter. I would lose ship control if the sound sink didn't spawn, but
// that should be fine -- any time that fails, more has likely also failed.
let Ok((mut velocity, transform, audio, children)) = query.single_mut() else {
let count = query.iter().count(); let count = query.iter().count();
panic!("There should be exactly one player ship! Instead, there seems to be {count}."); panic!("There should be exactly one player ship! Instead, there seems to be {count}.");
}; };
@@ -132,10 +146,16 @@ fn input_ship_thruster(
commands commands
.entity(*thrusters) .entity(*thrusters)
.insert(MeshMaterial2d(game_assets.thruster_mat_active())); .insert(MeshMaterial2d(game_assets.thruster_mat_active()));
if let Some(audio) = audio {
audio.play();
}
} else { } else {
commands commands
.entity(*thrusters) .entity(*thrusters)
.insert(MeshMaterial2d(game_assets.thruster_mat_inactive())); .insert(MeshMaterial2d(game_assets.thruster_mat_inactive()));
if let Some(audio) = audio {
audio.pause();
}
} }
} }
@@ -199,6 +219,8 @@ fn input_ship_shoot(
MeshMaterial2d(game_assets.bullet().1), MeshMaterial2d(game_assets.bullet().1),
ship_pos.clone(), // clone ship transform ship_pos.clone(), // clone ship transform
Lifetime(Timer::from_seconds(BULLET_LIFETIME, TimerMode::Once)), Lifetime(Timer::from_seconds(BULLET_LIFETIME, TimerMode::Once)),
AudioPlayer::new(game_assets.laser_sound()),
PlaybackSettings::ONCE, // `Lifetime` already despawns the entity, so this doesn't need to
)); ));
} }
} }

View File

@@ -83,9 +83,9 @@ pub fn spawn_asteroid(
}; };
let collider_radius = match spawn.size { let collider_radius = match spawn.size {
AsteroidSize::Small => 10.0, AsteroidSize::Small => 5.0,
AsteroidSize::Medium => 20.0, AsteroidSize::Medium => 10.0,
AsteroidSize::Large => 40.0, AsteroidSize::Large => 20.0,
}; };
commands.spawn(( commands.spawn((
@@ -115,6 +115,7 @@ pub fn split_asteroids(
mut respawn_events: MessageWriter<SpawnAsteroid>, mut respawn_events: MessageWriter<SpawnAsteroid>,
mut commands: Commands, mut commands: Commands,
query: Query<(&Transform, &Asteroid, &Velocity)>, query: Query<(&Transform, &Asteroid, &Velocity)>,
game_assets: Res<GameAssets>,
) { ) {
for event in destroy_events.read() { for event in destroy_events.read() {
if let Ok((transform, rock, velocity)) = query.get(event.0) { if let Ok((transform, rock, velocity)) = query.get(event.0) {
@@ -137,6 +138,12 @@ pub fn split_asteroids(
// Always despawn the asteroid. New ones (may) be spawned in it's // Always despawn the asteroid. New ones (may) be spawned in it's
// place, but this one is gone. // place, but this one is gone.
commands.entity(event.0).despawn(); commands.entity(event.0).despawn();
// Play a sound for the asteroid exploding
commands.spawn((
AudioPlayer::new(game_assets.asteroid_crack_sound()),
PlaybackSettings::DESPAWN,
));
} }
} }
} }
@@ -161,6 +168,12 @@ pub fn spawn_player(mut commands: Commands, game_assets: Res<GameAssets>) {
Mesh2d(game_assets.ship().0), Mesh2d(game_assets.ship().0),
MeshMaterial2d(game_assets.ship().1), MeshMaterial2d(game_assets.ship().1),
Transform::default().with_scale(Vec3::new(20.0, 20.0, 20.0)), Transform::default().with_scale(Vec3::new(20.0, 20.0, 20.0)),
AudioPlayer::new(game_assets.ship_thruster_sound()),
PlaybackSettings {
mode: bevy::audio::PlaybackMode::Loop,
paused: true,
..Default::default()
},
)) ))
.with_child(( .with_child((
Mesh2d(game_assets.thruster_mesh()), Mesh2d(game_assets.thruster_mesh()),
@@ -237,7 +250,7 @@ pub fn ship_impact_listener(
// STEP 5: Play crash sound // STEP 5: Play crash sound
commands.spawn(( commands.spawn((
AudioPlayer::new(game_assets.wreck_sound()), AudioPlayer::new(game_assets.wreck_sound()),
PlaybackSettings::ONCE, PlaybackSettings::DESPAWN, // despawn this entity when playback ends.
)); ));
} }
} }

View File

@@ -9,7 +9,7 @@ use bevy::{
}, },
math::{ math::{
Vec2, Vec2,
primitives::{Circle, Triangle2d}, primitives::{Polyline2d, Segment2d, Triangle2d},
}, },
mesh::Mesh, mesh::Mesh,
prelude::{Deref, DerefMut, Reflect, ReflectResource}, prelude::{Deref, DerefMut, Reflect, ReflectResource},
@@ -19,8 +19,9 @@ use bevy_inspector_egui::InspectorOptions;
use bevy_inspector_egui::inspector_options::ReflectInspectorOptions; use bevy_inspector_egui::inspector_options::ReflectInspectorOptions;
use crate::{ use crate::{
ASTEROID_SMALL_COLOR, BULLET_COLOR, PLAYER_SHIP_COLOR, SHIP_THRUSTER_COLOR_ACTIVE, ASTEROID_LARGE_COLOR, ASTEROID_MEDIUM_COLOR, ASTEROID_SMALL_COLOR, BULLET_COLOR,
SHIP_THRUSTER_COLOR_INACTIVE, config::WINDOW_SIZE, PLAYER_SHIP_COLOR, SHIP_THRUSTER_COLOR_ACTIVE, SHIP_THRUSTER_COLOR_INACTIVE,
config::WINDOW_SIZE,
}; };
#[derive(InspectorOptions, Reflect, Resource, Debug, Deref, Clone, Copy)] #[derive(InspectorOptions, Reflect, Resource, Debug, Deref, Clone, Copy)]
@@ -58,7 +59,7 @@ impl Default for WorldSize {
pub struct GameAssets { pub struct GameAssets {
meshes: [Handle<Mesh>; 5], meshes: [Handle<Mesh>; 5],
materials: [Handle<ColorMaterial>; 7], materials: [Handle<ColorMaterial>; 7],
sounds: [Handle<AudioSource>; 1], sounds: [Handle<AudioSource>; 4],
} }
impl GameAssets { impl GameAssets {
@@ -83,15 +84,15 @@ impl GameAssets {
} }
pub fn asteroid_small(&self) -> (Handle<Mesh>, Handle<ColorMaterial>) { pub fn asteroid_small(&self) -> (Handle<Mesh>, Handle<ColorMaterial>) {
(self.meshes[1].clone(), self.materials[1].clone()) (self.meshes[1].clone(), self.materials[3].clone())
} }
pub fn asteroid_medium(&self) -> (Handle<Mesh>, Handle<ColorMaterial>) { pub fn asteroid_medium(&self) -> (Handle<Mesh>, Handle<ColorMaterial>) {
(self.meshes[2].clone(), self.materials[2].clone()) (self.meshes[2].clone(), self.materials[4].clone())
} }
pub fn asteroid_large(&self) -> (Handle<Mesh>, Handle<ColorMaterial>) { pub fn asteroid_large(&self) -> (Handle<Mesh>, Handle<ColorMaterial>) {
(self.meshes[3].clone(), self.materials[3].clone()) (self.meshes[3].clone(), self.materials[5].clone())
} }
pub fn bullet(&self) -> (Handle<Mesh>, Handle<ColorMaterial>) { pub fn bullet(&self) -> (Handle<Mesh>, Handle<ColorMaterial>) {
@@ -101,6 +102,18 @@ impl GameAssets {
pub fn wreck_sound(&self) -> Handle<AudioSource> { pub fn wreck_sound(&self) -> Handle<AudioSource> {
self.sounds[0].clone() self.sounds[0].clone()
} }
pub fn laser_sound(&self) -> Handle<AudioSource> {
self.sounds[1].clone()
}
pub fn asteroid_crack_sound(&self) -> Handle<AudioSource> {
self.sounds[2].clone()
}
pub fn ship_thruster_sound(&self) -> Handle<AudioSource> {
self.sounds[3].clone()
}
} }
impl FromWorld for GameAssets { impl FromWorld for GameAssets {
@@ -112,10 +125,63 @@ impl FromWorld for GameAssets {
Vec2::new(-0.5, 0.45), Vec2::new(-0.5, 0.45),
Vec2::new(-0.5, -0.45), Vec2::new(-0.5, -0.45),
)), )),
world_meshes.add(Circle::new(10.0)), world_meshes.add(Polyline2d::new(
world_meshes.add(Circle::new(20.0)), [
world_meshes.add(Circle::new(40.0)), Vec2::new(0.1, 0.0),
world_meshes.add(Circle::new(0.2)), Vec2::new(0.8, 0.2),
Vec2::new(0.8, 0.3),
Vec2::new(0.1, 1.0),
Vec2::new(-0.5, 1.0),
Vec2::new(-0.3, 0.3),
Vec2::new(-1.0, 0.3),
Vec2::new(-1.0, -0.2),
Vec2::new(-0.5, -1.0),
Vec2::new(0.1, -0.8),
Vec2::new(0.5, -0.9),
Vec2::new(1.0, -0.4),
Vec2::new(0.1, 0.0),
]
.into_iter()
.map(|vert| vert * 5.0),
)),
world_meshes.add(Polyline2d::new(
[
Vec2::new(0.6, 0.3),
Vec2::new(1.0, 0.6),
Vec2::new(0.6, 1.0),
Vec2::new(0.1, 0.8),
Vec2::new(-0.4, 1.0),
Vec2::new(-1.0, 0.6),
Vec2::new(-0.8, -0.1),
Vec2::new(-1.0, -0.5),
Vec2::new(-0.4, -1.0),
Vec2::new(-0.3, -0.7),
Vec2::new(0.6, -1.0),
Vec2::new(1.0, -0.3),
Vec2::new(0.6, 0.3),
]
.into_iter()
.map(|vert| vert * 10.0),
)),
world_meshes.add(Polyline2d::new(
[
Vec2::new(1.0, -0.1),
Vec2::new(1.0, 0.3),
Vec2::new(0.4, 1.0),
Vec2::new(-0.2, 1.0),
Vec2::new(-0.9, 0.3),
Vec2::new(-0.5, 0.1),
Vec2::new(-0.9, -0.1),
Vec2::new(-0.5, -1.0),
Vec2::new(0.0, 0.0),
Vec2::new(0.0, -1.0),
Vec2::new(0.5, -1.0),
Vec2::new(1.0, -0.1),
]
.into_iter()
.map(|vert| vert * 20.0),
)),
world_meshes.add(Segment2d::new(Vec2::new(-0.1, 0.0), Vec2::new(0.1, 0.0))),
]; ];
let mut world_materials = world.resource_mut::<Assets<ColorMaterial>>(); let mut world_materials = world.resource_mut::<Assets<ColorMaterial>>();
let materials = [ let materials = [
@@ -124,12 +190,17 @@ impl FromWorld for GameAssets {
world_materials.add(SHIP_THRUSTER_COLOR_ACTIVE), world_materials.add(SHIP_THRUSTER_COLOR_ACTIVE),
world_materials.add(ASTEROID_SMALL_COLOR), world_materials.add(ASTEROID_SMALL_COLOR),
// TODO: asteroid medium and large colors // TODO: asteroid medium and large colors
world_materials.add(ASTEROID_SMALL_COLOR), world_materials.add(ASTEROID_MEDIUM_COLOR),
world_materials.add(ASTEROID_SMALL_COLOR), world_materials.add(ASTEROID_LARGE_COLOR),
world_materials.add(BULLET_COLOR), world_materials.add(BULLET_COLOR),
]; ];
let loader = world.resource_mut::<AssetServer>(); let loader = world.resource_mut::<AssetServer>();
let sounds = [loader.load("explosionCrunch_004.ogg")]; let sounds = [
loader.load("explosionCrunch_004.ogg"),
loader.load("laserSmall_001.ogg"),
loader.load("explosionCrunch_000.ogg"),
loader.load("thrusterFire_004.ogg"),
];
GameAssets { GameAssets {
meshes, meshes,
materials, materials,