11 Commits

Author SHA1 Message Date
172b528138 Mark v0.6.2 release: Removed debug noise
Some checks failed
Basic checks / Basic build-and-test supertask (push) Failing after 1m10s
I've removed a bit of the debug stuff from the program -- mainly the
egui-inspector UI, but also some debug prints that were still happening
in the physics module.
2025-12-22 10:41:52 -06:00
70de4bb67d Remove debug prints in physics.rs 2025-12-22 10:39:40 -06:00
ebee953955 Hide egui-inspector window behind feature flag
The World Inspector widget shouldn't be enabled by default. This hides
it behind the "debug_ui" feature flag, along with the bevy_rapier2d
debug drawings.

The bevy_rapier2d debug plugin is now added when in debug mode, which
was not previously the case.
2025-12-21 16:45:52 -06:00
d2cb75c3a1 Apply clippy fixes 2025-12-20 09:38:46 -06:00
099926d368 Hotfix for broken install target in Makefile
Some checks failed
Basic checks / Basic build-and-test supertask (push) Failing after 48s
I didn't check that it also *installs* all of it's components! That's
what I get for not having a list of package contents... although I'd
probably just end up missing something there instead.
2025-12-19 16:11:13 -06:00
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
9 changed files with 89 additions and 46 deletions

6
Cargo.lock generated
View File

@@ -242,7 +242,7 @@ dependencies = [
[[package]]
name = "asteroids"
version = "0.6.0-dev3"
version = "0.6.2"
dependencies = [
"bevy",
"bevy-inspector-egui",
@@ -4295,9 +4295,9 @@ dependencies = [
[[package]]
name = "portable-atomic"
version = "1.11.1"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483"
checksum = "f59e70c4aef1e55797c2e8fd94a4f2a973fc972cfde0e0b05f683667b0cd39dd"
[[package]]
name = "portable-atomic-util"

View File

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

View File

@@ -40,10 +40,10 @@ target/$(CARGO_TARGET)/$(CARGO_PROFILE)/asteroids.wasm: $(SRCS) Cargo.lock Cargo
out:
mkdir $@
out/assets: out
out/assets: | out
mkdir $@
out/assets/%.ogg: out/assets assets/%.ogg
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
@@ -76,6 +76,8 @@ full-clean: clean
# output into the web root. Only supports the "bundle-able" mode.
install: web
install -dm0755 $(DESTDIR)
install -dm0755 $(DESTDIR)/assets
install -m0644 out/asteroids.js $(DESTDIR)/
install -m0644 out/asteroids_bg.wasm.gz $(DESTDIR)/
install -m0644 out/asteroids.html $(DESTDIR)/
install -m0644 $(ASSETS) $(DESTDIR)/assets/

View File

@@ -217,7 +217,7 @@ fn input_ship_shoot(
physics::Velocity(bullet_vel),
Mesh2d(game_assets.bullet().0),
MeshMaterial2d(game_assets.bullet().1),
ship_pos.clone(), // clone ship transform
*ship_pos, // 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

@@ -1,20 +1,28 @@
use bevy::{prelude::*, window::WindowResolution};
use asteroids::{AsteroidPlugin, config::WINDOW_SIZE};
#[cfg(feature = "debug_ui")]
use bevy_inspector_egui::{bevy_egui::EguiPlugin, quick::WorldInspectorPlugin};
#[cfg(feature = "debug_ui")]
use bevy_rapier2d::render::RapierDebugRenderPlugin;
fn main() {
App::new()
.add_plugins(DefaultPlugins.set(WindowPlugin {
primary_window: Some(Window {
canvas: Some("#game-canvas".to_owned()),
resolution: WindowResolution::new(WINDOW_SIZE.0, WINDOW_SIZE.1),
..default()
}),
let mut app = App::new();
app.add_plugins(DefaultPlugins.set(WindowPlugin {
primary_window: Some(Window {
canvas: Some("#game-canvas".to_owned()),
resolution: WindowResolution::new(WINDOW_SIZE.0, WINDOW_SIZE.1),
..default()
}))
.add_plugins(AsteroidPlugin)
.add_plugins(EguiPlugin::default())
}),
..default()
}))
.add_plugins(AsteroidPlugin);
#[cfg(feature = "debug_ui")]
app.add_plugins(EguiPlugin::default())
.add_plugins(WorldInspectorPlugin::new())
.run();
.add_plugins(RapierDebugRenderPlugin::default());
app.run();
}

View File

@@ -238,7 +238,7 @@ pub fn ship_impact_listener(
Sparkler::at_interval(0.15),
Mesh2d(game_assets.thruster_mesh()), // borrow the thruster mesh for now
MeshMaterial2d(game_assets.thruster_mat_active()), // ... and the active thruster material
player.0.clone(), // clone the player transform
*player.0, // clone the player transform
Velocity(vel),
));
}

View File

@@ -111,29 +111,21 @@ pub fn collision_listener(
if *one == *player {
if rocks.contains(*two) {
// player-asteroid collision
dbg!("Writing ShipDestroy event");
ship_writer.write(messages::ShipDestroy);
} // else, we don't care
} else if *two == *player {
if rocks.contains(*one) {
dbg!("Writing ShipDestroy event");
ship_writer.write(messages::ShipDestroy);
}
} else if *two == *player && rocks.contains(*one) {
ship_writer.write(messages::ShipDestroy);
}
// Option 2: Bullet & Asteroid
if bullets.contains(*one) {
if rocks.contains(*two) {
dbg!("Writing AsteroidDestroy & BulletDestroy events");
asteroid_writer.write(messages::AsteroidDestroy(*two));
bullet_writer.write(messages::BulletDestroy(*one));
}
} else if rocks.contains(*one) {
if bullets.contains(*two) {
dbg!("Writing AsteroidDestroy & BulletDestroy events");
asteroid_writer.write(messages::AsteroidDestroy(*one));
bullet_writer.write(messages::BulletDestroy(*two));
}
} else if rocks.contains(*one) && bullets.contains(*two) {
asteroid_writer.write(messages::AsteroidDestroy(*one));
bullet_writer.write(messages::BulletDestroy(*two));
}
}
}

View File

@@ -8,7 +8,7 @@ use crate::{
};
use bevy::{
color::palettes::css::{BLACK, DARK_GRAY, GREEN, LIGHT_BLUE, RED, WHITE},
color::palettes::css::{BLACK, DARK_GRAY, GREEN, RED, WHITE},
prelude::*,
};
@@ -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 {
@@ -263,6 +263,7 @@ fn animate_get_ready_widget(
/// on the HUD, this system would quit the game. The same will happen for
/// returning to the title screen. This should be useful for making a pause
/// menu, too.
#[allow(clippy::type_complexity)]
fn operate_buttons(
mut interactions: Query<
(

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,23 +119,34 @@
<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.1</code></td>
</tr>
</table>
</article>
</main>
<script type="module">
import init from './asteroids.js'
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);
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;
}
});
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>