14 Commits

Author SHA1 Message Date
76426094d3 Mark v0.6.0 release
Some checks failed
Basic checks / Basic build-and-test supertask (push) Failing after 59s
2025-12-19 13:47:49 -06:00
4b101633e7 Better colors for the "get ready" screen
The card itself is transparent, but it still has a size of 30% of the
viewport because the bar is based on the size of it's container.

The start bar and text can still be green and red, respectively, but the
light blue was completely out of place.
2025-12-19 13:45:22 -06:00
a5a6f32037 Add a sound warning and mute guide
Page visitors may be surprised by the sounds and not know how to turn
them off. The answer is "you don't", but practically the browser tab can
be muted instead.
2025-12-19 13:36:48 -06:00
4d899d3c97 Improved "load game" button, now with elem swap
Some checks failed
Basic checks / Basic build-and-test supertask (push) Failing after 1m3s
The canvas element no longer exists in the page source. Instead, there
is a div styled to look similar and contain a button. The button is
styled to match the ones in the Bevy app. When pressed, the button
creates the canvas element, replaces the fake canvas div, and loads the
WASM to do it's thing.
2025-12-19 13:27:42 -06:00
6d5afc2445 Add a basic "load game" button
This will ensure the user has interacted with the page before the game
starts, thus allowing the sound to play correctly.

It doesn't re-load the game if the user quits. I'd like to figure out
how to do that.

The need to push the button isn't immediately obvious. It should be over
top (or in place of) the canvas to best convey that the user must start
the game.
2025-12-19 11:46:18 -06:00
d34d0a31f2 Fix: Folder deps need to be order-only deps
I did the thing againnnnn. The Makefile thinks the sound assets are
constantly out-of-date because the folder that contains them technically
changes any time a file is added or removed from the folder.

1. "out/assets" is created, it is up-to-date
2. "out/assets/example.ogg" is created, it is up-to-date
3. Parent folder "out/assets" has it's mtime updated because it's
   contents have changed. It is newer than it was at step 1, and newer
   than the .ogg file in step 2.
4. run `make` again
5. "out/assets" is up-to-date, nothing changes
6. "out/assets/example.ogg" is OLDER than one of it's dependencies
   ("out/assets/"), and is rebuilt.
7. "out/assets" got new contents, so it's mtime was updated again.

The cycle isn't infinite, but it will always try to rebuild the sound
files. The fix is to consider the containing folder to only be an
ordering dependency rather than a substantive dependency. The former
only needs the dependency to be made first, where the latter considers
the dependency to be part of the target file. The containing folder is
not part of the sound files, so "rebuilding" the sound files when the
folder changes is complete nonsense.
2025-12-19 11:26:18 -06:00
72f062ea10 Teach the Makefile how to "build" the audio assets
Some checks failed
Basic checks / Basic build-and-test supertask (push) Failing after 55s
Makefiles are pretty strange things.

There is a new wildcard target which will copy any .ogg file from
"assets/" into "out/assets". On it's own, it does nothing -- it only
knows how to create the output file by copying the input one.

To drive this, a list of output assets must be created and depended upon
by the main build target(s). I don't want to manually maintain an asset
manifest in a Makefile, so I've achived this by wildcard matching
anything in the "assets/" folder, then rewriting the prefix to be
"out/assets/". This list is in a variable, which is now part of the
dependency list for the main build target(s). All files will be
installed, but only when they are out-of-date. Excellent.
2025-12-19 10:55:57 -06:00
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
12 changed files with 216 additions and 59 deletions

26
Cargo.lock generated
View File

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

View File

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

View File

@@ -12,23 +12,26 @@ CARGO_PROFILE := tiny
SRC_DIR = ./src
SRCS := $(wildcard $(SRC_DIR)/**)
ASSET_SOURCE := $(wildcard assets/**)
ASSETS := $(patsubst assets/%.ogg, out/assets/%.ogg, $(ASSET_SOURCE))
.PHONY: clean full-clean tarball tarball-standalone web web-standalone
# "Standalone" version. It includes an index.html to serve as-is
web-standalone: out/asteroids.js out/asteroids_bg.wasm.gz out/index.html
web-standalone: out/asteroids.js out/asteroids_bg.wasm.gz out/index.html $(ASSETS)
# "Bundle-able" version. It has a page, but no index.html. Consumers are
# expected to provide their own index.html and link to this page.
web: out/asteroids.js out/asteroids_bg.wasm.gz out/asteroids.html
web: out/asteroids.js out/asteroids_bg.wasm.gz out/asteroids.html $(ASSETS)
tarball: asteroids_web_root.tar
tarball_standalone: asteroids_web_root_standalone.tar
asteroids_web_root.tar: out/asteroids.js out/asteroids_bg.wasm.gz out/asteroids.html
asteroids_web_root.tar: out/asteroids.js out/asteroids_bg.wasm.gz out/asteroids.html $(ASSETS)
tar -caf $@ $^
asteroids_web_root_standalone.tar: out/asteroids.js out/asteroids_bg.wasm.gz out/index.html
asteroids_web_root_standalone.tar: out/asteroids.js out/asteroids_bg.wasm.gz out/index.html $(ASSETS)
tar -caf $@ $^
target/$(CARGO_TARGET)/$(CARGO_PROFILE)/asteroids.wasm: $(SRCS) Cargo.lock Cargo.toml
@@ -37,6 +40,12 @@ target/$(CARGO_TARGET)/$(CARGO_PROFILE)/asteroids.wasm: $(SRCS) Cargo.lock Cargo
out:
mkdir $@
out/assets: | out
mkdir $@
out/assets/%.ogg: assets/%.ogg | out/assets
cp -ar assets/$*.ogg $@
# Both the JS and WASM files are generated by the wasm-bindgen call, so both
# get to be on the target half of this recipe.
out/asteroids.js out/asteroids_bg.wasm.gz &: target/$(CARGO_TARGET)/$(CARGO_PROFILE)/asteroids.wasm | out

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_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 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_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 BULLET_COLOR: Color = Color::srgb(0.0, 0.1, 0.9);
pub(crate) const ASTEROID_SMALL_COLOR: Color = Color::srgb(0.9, 0.9, 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
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 BULLET_SPEED: f32 = 150.0;
pub(crate) const BULLET_SPEED: f32 = 500.0;
pub(crate) const BULLET_LIFETIME: f32 = 2.0;
pub(crate) const ASTEROID_LIFETIME: f32 = 40.0;

View File

@@ -11,9 +11,9 @@ mod resources;
mod widgets;
use crate::config::{
ASTEROID_SMALL_COLOR, BACKGROUND_COLOR, BULLET_COLOR, BULLET_LIFETIME, BULLET_SPEED,
PLAYER_SHIP_COLOR, SHIP_ROTATION, SHIP_THRUST, SHIP_THRUSTER_COLOR_ACTIVE,
SHIP_THRUSTER_COLOR_INACTIVE,
ASTEROID_LARGE_COLOR, ASTEROID_MEDIUM_COLOR, ASTEROID_SMALL_COLOR, BACKGROUND_COLOR,
BULLET_COLOR, BULLET_LIFETIME, BULLET_SPEED, PLAYER_SHIP_COLOR, SHIP_ROTATION, SHIP_THRUST,
SHIP_THRUSTER_COLOR_ACTIVE, SHIP_THRUSTER_COLOR_INACTIVE,
};
use crate::machinery::AsteroidSpawner;
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.
fn input_ship_thruster(
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,
game_assets: Res<GameAssets>,
) {
// 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
// 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();
panic!("There should be exactly one player ship! Instead, there seems to be {count}.");
};
@@ -132,10 +146,16 @@ fn input_ship_thruster(
commands
.entity(*thrusters)
.insert(MeshMaterial2d(game_assets.thruster_mat_active()));
if let Some(audio) = audio {
audio.play();
}
} else {
commands
.entity(*thrusters)
.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),
ship_pos.clone(), // clone ship transform
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 {
AsteroidSize::Small => 10.0,
AsteroidSize::Medium => 20.0,
AsteroidSize::Large => 40.0,
AsteroidSize::Small => 5.0,
AsteroidSize::Medium => 10.0,
AsteroidSize::Large => 20.0,
};
commands.spawn((
@@ -115,6 +115,7 @@ pub fn split_asteroids(
mut respawn_events: MessageWriter<SpawnAsteroid>,
mut commands: Commands,
query: Query<(&Transform, &Asteroid, &Velocity)>,
game_assets: Res<GameAssets>,
) {
for event in destroy_events.read() {
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
// place, but this one is gone.
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),
MeshMaterial2d(game_assets.ship().1),
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((
Mesh2d(game_assets.thruster_mesh()),
@@ -237,7 +250,7 @@ pub fn ship_impact_listener(
// STEP 5: Play crash sound
commands.spawn((
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::{
Vec2,
primitives::{Circle, Triangle2d},
primitives::{Polyline2d, Segment2d, Triangle2d},
},
mesh::Mesh,
prelude::{Deref, DerefMut, Reflect, ReflectResource},
@@ -19,8 +19,9 @@ use bevy_inspector_egui::InspectorOptions;
use bevy_inspector_egui::inspector_options::ReflectInspectorOptions;
use crate::{
ASTEROID_SMALL_COLOR, BULLET_COLOR, PLAYER_SHIP_COLOR, SHIP_THRUSTER_COLOR_ACTIVE,
SHIP_THRUSTER_COLOR_INACTIVE, config::WINDOW_SIZE,
ASTEROID_LARGE_COLOR, ASTEROID_MEDIUM_COLOR, ASTEROID_SMALL_COLOR, BULLET_COLOR,
PLAYER_SHIP_COLOR, SHIP_THRUSTER_COLOR_ACTIVE, SHIP_THRUSTER_COLOR_INACTIVE,
config::WINDOW_SIZE,
};
#[derive(InspectorOptions, Reflect, Resource, Debug, Deref, Clone, Copy)]
@@ -58,7 +59,7 @@ impl Default for WorldSize {
pub struct GameAssets {
meshes: [Handle<Mesh>; 5],
materials: [Handle<ColorMaterial>; 7],
sounds: [Handle<AudioSource>; 1],
sounds: [Handle<AudioSource>; 4],
}
impl GameAssets {
@@ -83,15 +84,15 @@ impl GameAssets {
}
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>) {
(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>) {
(self.meshes[3].clone(), self.materials[3].clone())
(self.meshes[3].clone(), self.materials[5].clone())
}
pub fn bullet(&self) -> (Handle<Mesh>, Handle<ColorMaterial>) {
@@ -101,6 +102,18 @@ impl GameAssets {
pub fn wreck_sound(&self) -> Handle<AudioSource> {
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 {
@@ -112,10 +125,63 @@ impl FromWorld for GameAssets {
Vec2::new(-0.5, 0.45),
Vec2::new(-0.5, -0.45),
)),
world_meshes.add(Circle::new(10.0)),
world_meshes.add(Circle::new(20.0)),
world_meshes.add(Circle::new(40.0)),
world_meshes.add(Circle::new(0.2)),
world_meshes.add(Polyline2d::new(
[
Vec2::new(0.1, 0.0),
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 materials = [
@@ -124,12 +190,17 @@ impl FromWorld for GameAssets {
world_materials.add(SHIP_THRUSTER_COLOR_ACTIVE),
world_materials.add(ASTEROID_SMALL_COLOR),
// TODO: asteroid medium and large colors
world_materials.add(ASTEROID_SMALL_COLOR),
world_materials.add(ASTEROID_SMALL_COLOR),
world_materials.add(ASTEROID_MEDIUM_COLOR),
world_materials.add(ASTEROID_LARGE_COLOR),
world_materials.add(BULLET_COLOR),
];
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 {
meshes,
materials,

View File

@@ -180,9 +180,9 @@ fn spawn_get_ready(mut commands: Commands, mut timer: ResMut<ReadySetGoTimer>) {
height: Val::Percent(30.),
..default()
},
BackgroundColor(LIGHT_BLUE.into()),
BackgroundColor(Color::NONE),
children![
(Text::new("Get Ready!"), TextColor(BLACK.into())),
(Text::new("Get Ready!"), TextColor(WHITE.into())),
(
CountdownBar,
Node {

View File

@@ -12,6 +12,7 @@
margin: auto;
padding: 0.5em;
}
#prestart-controls,
canvas {
margin-top: 1em;
margin-left: auto;
@@ -22,6 +23,26 @@
border-radius: 8px;
background-color: rgb(40%, 40%, 40%);
}
#prestart-controls {
width: 800px;
height: 600px;
text-align: center;
align-content: center;
}
button {
font-size: 20px;
text-shadow: 0.2em 0.2em 0px rgba(0, 0, 0, 75%);
padding: 1em;
border-radius: 2em;
border-style: solid;
border-color: black;
color: rgb(90%, 90%, 90%);
background-color: rgb(15%, 15%, 15%);
}
button:hover {
background-color: rgb(25%, 25%, 25%);
border-color: rgb(90%, 90%, 90%);
}
main {
margin-left: auto;
margin-right: auto;
@@ -41,13 +62,21 @@
<h1>
Robert's Bad Asteroids Game
</h1>
<canvas id="game-canvas" width="1280" height="720"></canvas>
<!-- <canvas id="game-canvas" width="800" height="600"></canvas> -->
<div id="prestart-controls">
<button id="gameload-button">Load Game</button>
</div>
<main>
<article>
<h2>Description</h2>
<p>
A (work in progress) version of the Asteroids arcade game.
</p>
<p>
<em>Sound Warning!</em> The game now has sound effects, but there are no controls on the page for changing the
volume (including to mute them). You can mute the browser tab by pressing <code>ctrl</code> + <code>m</code>.
Proper volume controls are coming soon.
</p>
</article>
<article>
<h3>Controls</h3>
@@ -90,7 +119,7 @@
<tr>
<td>Program Version</td>
<!-- This version text is completely unchecked. I'll need to do something about that. -->
<td><code>v0.5.0</code></td>
<td><code>v0.6.0</code></td>
</tr>
</table>
</article>
@@ -98,15 +127,26 @@
<script type="module">
import init from './asteroids.js'
let compressed = await fetch("./asteroids_bg.wasm.gz")
let wasm_stream = compressed.body.pipeThrough(new DecompressionStream("gzip"))
let blob = await new Response(wasm_stream).blob();
let button = document.getElementById("gameload-button");
button.onclick = async function loadGame() {
console.log("Game Load button was pressed!");
let canvas = document.createElement("canvas");
// <canvas id="game-canvas" width="800" height="600"></canvas>
canvas.setAttribute("id", "game-canvas");
canvas.setAttribute("width", "800");
canvas.setAttribute("height", "600");
button.parentElement.replaceWith(canvas);
init(await blob.arrayBuffer()).catch((error) => {
if (!error.message.startsWith("Using exceptions for control flow, don't mind me. This isn't actually an error!")) {
throw error;
}
});
let compressed = await fetch("./asteroids_bg.wasm.gz")
let wasm_stream = compressed.body.pipeThrough(new DecompressionStream("gzip"))
let blob = await new Response(wasm_stream).blob();
init(await blob.arrayBuffer()).catch((error) => {
if (!error.message.startsWith("Using exceptions for control flow, don't mind me. This isn't actually an error!")) {
throw error;
}
});
}
</script>
</body>