Skip to content

Releases: ikostan/SkyLockAssault

Milestone 8: Audio crackling and delays in non threaded web exports main thread blocking warning in threaded exports, Part #13

23 Dec 07:49
5fc95d4

Choose a tag to compare

Summary @sourcery-ai

Refine web export security and analysis workflows while extending audio configuration to support a dedicated rotors channel.

New Features:

  • Add configurable rotors audio bus and volume setting integrated into global settings and the options volume slider.
  • Enable progressive web app support for the web export preset.

Bug Fixes:

  • Improve robustness of the index.js patching script by switching to fixed-string detection and adding an idempotent pre-check to avoid double patching.
  • Audio crackling and delays in non threaded web exports main thread blocking warning in threaded exports (#266) @ikostan
  • Rotors Volume Level Not Persisted Across Sessions #264

Enhancements:

  • Centralize audio bus names into global constants to avoid hard-coded strings and reduce configuration drift.
  • Reorder the options menu input handler without changing behavior to keep node initialization grouped logically.

Build:

  • Enable advanced export options and configure PWA settings for the Web export preset.
  • Add a flatten-export step to the CodeQL web export job to normalize the export directory layout before scanning.

CI:

  • Re-enable the CodeQL workflow in the main lint/test/deploy pipeline and make deploy depend on its completion.
  • Update CodeQL GitHub Action versions across CodeQL and Snyk workflows and refresh the pinned SHA for the Trivy SARIF upload action.
  • Invoke the index.js patch script in both CodeQL and itch.io deploy workflows to keep browser exports consistently patched.

Deployment:

  • Ensure web export archives are unzipped, patched, flattened, and re-zipped before deployment to itch.io.

🧰 Maintenance

  • Audio crackling and delays in non threaded web exports main thread blocking warning in threaded exports (#266) @ikostan
  • Bump github/codeql-action from 4.31.8 to 4.31.9 (#265) @ikostan @dependabot

Reviewer's Guide

Refines audio bus handling with a new rotor SFX volume path, reorders the options menu input callback, and hardens the web export/index.js patching plus CI/security workflows (CodeQL, Snyk, Trivy, itch deploy) to work on flattened, patched web exports with PWA enabled.

File-Level Changes

Change Details Files
Introduce dedicated rotor SFX volume support and centralize audio bus naming via Globals constants.
  • Add BUS_* constants (including BUS_SFX_ROTORS) in Globals and use them instead of hard-coded strings.
  • Persist rotors_volume alongside other volumes in the settings config and apply it on startup to the corresponding bus.
  • Extend volume_slider.gd handler to read/write rotor SFX volume via Globals and log changes consistently.
scripts/globals.gd
scripts/volume_slider.gd
Reorder options menu input handling without changing its behavior.
  • Move the _input handler in options_menu.gd to live after _ready, keeping the click-logging logic intact for organization/readability.
scripts/options_menu.gd
Make the index.js web export patch script idempotent and more robust via fixed-string matching.
  • Switch from regex-based detection to fixed-string grep checks for both original and patched handler code.
  • Add an early exit when the file is already patched to avoid duplicate injections.
  • Use Perl's literal quoting (\Q) to safely replace the exact original snippet and verify success with a fixed-string check.
.github/scripts/patch_index_js.sh
Tighten CI/security workflows and ensure web exports are flattened and patched before CodeQL/Snyk/Trivy scanning and itch.io deploy.
  • Re-enable CodeQL as a job in the main lint_test_deploy workflow and make deploy-itch depend on CodeQL, Snyk, and Trivy.
  • Update CodeQL workflow to export web builds without archiving, flatten the export directory before scanning, patch index.js, and bump github/codeql-action versions.
  • Update Snyk and Trivy workflows to newer/pinned [email protected] and pinned SHA respectively.
  • Patch index.js in the itch.io deployment pipeline after unzipping and before re-zipping the web export.
.github/workflows/lint_test_deploy.yml
.github/workflows/codeql.yml
.github/workflows/snyk.yml
.github/workflows/trivy.yml
.github/workflows/deploy_to_itch.yml
Adjust web export preset to enable advanced options and PWA support for the Web build.
  • Turn on advanced_options for the Web export preset to expose additional configuration.
  • Enable progressive web app support for the Web export preset while keeping existing COOP/COEP-related settings.
export_presets.cfg

Assessment against linked issues

Issue Objective Addressed Explanation
#262 Resolve audio crackling and delays in non-threaded web exports (e.g., Web_thread_off) so that audio playback is smooth. The PR modifies audio volume handling (new rotor SFX bus, use of bus name constants, persistence) but does not change any Godot or Web export settings that are specific to the non-threaded Web_thread_off preset, nor does it adjust audio buffering or driver settings. No changes are made to that export preset or to non-threaded audio behavior, so the root cause of crackling in non-threaded web exports is not addressed.
#262 Eliminate or mitigate the Emscripten warning about blocking on the main thread in threaded web exports (the Web preset), reducing the risk of main-thread stalls. The PR enables PWA and advanced options for the Web export and hardens a custom index.js patch script, but the patch only wraps the Module[handler] function for print/printErr and is described as a security/reliability improvement. There is no explicit change to remove or alter the synchronous behavior Emscripten warns about, nor any mention that this patch addresses the main-thread blocking warning. The warning condition itself is not clearly targeted in the diff.
#262 Adjust web export configuration so that both threaded and non-threaded web builds have stable audio without regressions or new warnings. Export configuration changes are limited to the threaded Web preset (enabling advanced options and PWA). The non-threaded preset mentioned in the issue (Web_thread_off) is not modified at all. While PWA and SharedArrayBuffer support may improve threaded behavior, they do not address non-threaded audio quality, and there is no clear link in the PR between these configuration changes and resolving the reported audio issues or warnings across both export modes.
#264 Add a dedicated rotors_volume setting in globals.gd and ensure it is loaded, saved, and applied to the appropriate audio bus across sessions.
#264 Wire the rotors volume slider to the rotors audio bus so that changing the slider updates Globals.rotors_volume and participates in the existing debounced settings save logic.

Milestone 8: Ignore Godot web export directories, Part #12

22 Dec 07:33
9b0771e

Choose a tag to compare

Summary

Adjust web export workflows to rely on Godot-generated export directories while stopping tracking of generated web build artifacts.

To remove the contents of the export/web and export/web_thread_off directories from the GitHub repository (while keeping the directories themselves if needed, or deleting them entirely), we'll use Git commands. This is common in Godot 4.5 game dev workflows because export folders contain generated build files (like index.js, .html, .wasm, etc.) that shouldn't be versioned—they bloat the repo and can cause issues with CI/CD or sharing the SkyLockAssault project. Instead, regenerate them via Godot's export menu when needed.

Update the CodeQL workflow to export the Godot project to Web before analysis and apply a security patch to the generated index.js.

Generate a Godot Web export as part of the CodeQL workflow so the scan runs on actual built web artifacts and centralize shared export post-processing logic into reusable scripts.

Bug Fixes:

  • Prevent unintended exposure and misuse of the Godot web worker message handler by restricting patched index.js behavior to print and printErr handlers.

Enhancements:

  • Ensure CI creates and flattens Godot web export directories in both deployment and browser test workflows to match preset export paths.
  • Update the Web_thread_off export preset to be non-runnable to better reflect its intended use as a test/export target.

Build:

  • Stop versioning generated Godot web export files by removing tracked artifacts from export/web and export/web_thread_off.
  • Extend the CodeQL GitHub Actions workflow to perform a Godot 4.5 Web export with caching and directory flattening before scanning.
  • Update the CodeQL workflow to run a Godot 4.5 Web export with caching and directory flattening before initializing analysis.
  • Add Godot Web export, directory flattening, and index.js security patching steps to the CodeQL analysis workflow so it no longer assumes pre-existing exports.
  • Refactor browser_test and deploy_to_itch workflows to use shared scripts for flattening export directories and applying the index.js security patch.

CI:

  • Add Web export, directory normalization, and a post-processing security patch of index.js to the CodeQL analysis workflow.

Chores:

  • Introduce shared flatten_export.sh and patch_index_js.sh scripts to deduplicate and harden export directory handling and index.js patching across workflows.

Reviewer's Guide

Configure Godot web exports to use preset export paths in CI, post-process the generated index.js for security, disable running the non-threaded web preset directly, and stop tracking generated web export artifacts in version control.

Updates the CodeQL workflow to perform a Godot 4.5 web export (including directory flattening and index.js security patching) before analysis, and centralizes shared export/post-processing logic into reusable bash scripts used by CodeQL, browser test, and deployment workflows.

File-Level Changes

Change Details Files
Update CI deploy workflow to prepare web export directory, honor Godot preset export path, normalize layout, and patch exported index.js for safer message handling.
  • Create export/web directory before running the Godot web export action to ensure the target path exists.
  • Enable use_preset_export_path in the Godot export step so artifacts go to the path defined in export_presets.cfg.
  • Add a step to flatten the export directory structure by moving files out of an extra export/web/Web subdirectory if present.
  • List the contents of export/web after export to aid debugging and verification in CI logs.
  • Add a step that fails the workflow if export/web/index.js is missing and otherwise patches its Module[handler] message handling so only print and printErr are exposed.
.github/workflows/deploy_to_itch.yml
Update browser test workflow to export to a non-threaded web preset directory using preset paths, normalize layout, and apply the same index.js security patch.
  • Create export/web_thread_off directory before running the Godot web export for tests.
  • Enable use_preset_export_path for the Web_thread_off preset so CI uses the configured export path.
  • Flatten the export/web_thread_off directory structure by removing an extra export/web_thread_off/Web_thread_off nesting if present.
  • List the contents of export/web_thread_off after export for easier CI debugging.
  • Patch export/web_thread_off/index.js in the same way as the main web export, failing if the file is missing and otherwise restricting Module[handler] to print and printErr.
.github/workflows/browser_test.yml
Make the Web_thread_off Godot export preset non-runnable to prevent accidental use in development or deployment.
  • Change the Web_thread_off export preset runnable flag from true to false while leaving other configuration intact.
export_presets.cfg
Stop tracking generated Godot web export artifacts and rely on Godot to regenerate them.
  • Remove committed web export outputs (HTML, JS, worklets, icons, and import metadata) from export/web and export/web_thread_off so they are no longer in the repository.
  • Update ignore rules so future Godot web export outputs in these directories are ignored by Git.
.gitignore
export/web/index.apple-touch-icon.png.import
export/web/index.audio.position.worklet.js
export/web/index.audio.worklet.js
export/web/index.html
export/web/index.icon.png.import
export/web/index.js
export/web/index.png.import
export/web_thread_off/index.audio.position.worklet.js
export/web_thread_off/index.audio.worklet.js
export/web_thread_off/index.html
export/web_thread_off/index.js
Have CodeQL analyze a built Godot web export instead of an empty export/web directory.
  • Create export/web directory before build in the CodeQL workflow job.
  • Add a Godot 4.5 Web export step using the pinned firebelley/godot-export action with caching and Web preset configuration.
  • Flatten the generated Web export directory into export/web via a shared bash script.
  • List the contents of export/web for debug visibility before analysis.
  • Run a shared index.js security patch script against export/web before initializing CodeQL.
.github/workflows/codeql.yml
Deduplicate and harden export directory flattening logic across workflows.
  • Introduce a shared flatten_export.sh script that validates the existence and non-emptiness of the target subdirectory before flattening.
  • Replace inline mv/rmdir logic in browser_test workflow to call flatten_export.sh for the threaded web export.
  • Replace inline mv/rmdir logic in itch.io deployment workflow to call flatten_export.sh for the standard web export.
.github/scripts/flatten_export.sh
.github/workflows/browser_test.yml
.github/workflows/deploy_to_itch.yml
Centralize and strengthen the index.js security patch for web exports.
  • Add a reusable patch_index_js.sh script that validates index.js existence, detects the target handler pattern using a robust regex, conditionally applies a perl-based patch, and verifies it applied.
  • Update browser_test workflow to call patch_index_js.sh on export/web_thread_off instead of inlined sed logic and explicit file checks.
  • Update deploy_to_itch workflow to call patch_index_js.sh on export/web instead of inlined sed logic and explicit file checks.
  • Add a security patch step in the CodeQL workflow to run patch_index_js.sh on export/web before analysis.
.github/scripts/patch_index_js.sh
.github/workflows/browser_test.yml
.github/workflows/deploy_to_itch.yml
.github/workflows/codeql.yml

Assessment against linked issues

Issue Objective Addressed Explanation
#259 Update the CodeQL GitHub Actions workflow to perform a Godot Web export (mirroring the deploy_to_itch.yml steps) so that export/web is populated before listing files and running CodeQL analysis, preventing failures due to an empty export/web directory.

Possibly linked issues

  • #N/A: PR adds Godot web export steps to codeql.yml so export/web is populated, resolving the CodeQL failure.

Milestone 8: Multi-Thread for Production; Disabled for Playwright Testing., Part #11

20 Dec 21:35
81a3590

Choose a tag to compare

Description:
Enable multi-thread in HTML5 export for production to fix music lags; disable for Playwright tests to avoid SAB/COI issues.

Why is this useful? (e.g., for learning Godot web exports)
Improves audio perf (e.g., rotors/music loop smooth) in browser prod; keeps tests stable without headers. Reason: Single-thread causes audio lags (e.g., music stutters after ~6s); threads require COOP/COEP headers for SAB, which local/CI servers lack, failing tests with "DataCloneError" – disable for test reliability, enable for prod perf.

Proposed Implementation (optional):

  • Workflow (browser_test.yml): Use special preset "Web_thread_off" with threads off for Playwright testing (avoids SAB/COI errors).
Image * Production: use preset "Web" with threads on for production. Image

Additional Context:
Lags from single-thread; tests fail with threads (SAB error). Win10/Linux CI tested.

Changes:

The approach of using two separate presets is clean and robust for Godot 4.5 – "Web" with threads on for production (smooth audio like rotors/music without lags), and "Web_thread_off" with threads off for Playwright testing (avoids SAB/COI errors).

  • In browser_test.yml (CI workflow): Use "Web_thread_off" to disable threads in "Export Godot to Web" step for testing. @ikostan
  • Updated browser_test.yml snippet (no other changes): @ikostan
        - name: "Export Godot to Web"
          id: "export"
          uses: "firebelley/godot-export@930577654862a320eef793f399ee911b4479efb9"
          with:
            # yamllint disable rule:line-length
            godot_executable_download_url: "https://github.com/godotengine/godot/releases/download/4.5-stable/Godot_v4.5-stable_linux.x86_64.zip"
            godot_export_templates_download_url: "https://github.com/godotengine/godot/releases/download/4.5-stable/Godot_v4.5-stable_export_templates.tpz"
            relative_project_path: "./"
            relative_export_path: "./export/web"
            archive_output: false
            cache: false
            presets_to_export: "Web_thread_off"
            # yamllint enable rule:line-length
    • changed archive_output: true to archive_output: false in the browser_test.yml workflow to avoid unnecessary zipping of the web export folder during CI tests. This speeds up the export step (no archive creation) while keeping the files ready for the local server – essential for learning efficient CI setups without extra overhead for Playwright testing. @ikostan
    • the line use_preset: "Web (Runnable)" in the browser_test.yml workflow (using the firebelley/godot-export action) refers to selecting a specific export preset from your project's Export dialog. In Godot, export presets are customizable configurations for building your game for different platforms. "Web (Runnable)" is a standard preset for HTML5/web exports that sets up the game to be runnable in browsers – it includes essential options like JavaScript evaluation, file compression, and potentially threads (which we disabled for testing to avoid SAB issues). This preset ensures the exported files (index.html, .js, .wasm, .pck) are optimized for web deployment, allowing quick CI testing without manual tweaks. @ikostan
    • cache: false -> To ensure fresh HTML5 exports for each CI test run, preventing stale builds from cached artifacts that could cause inconsistent Playwright results (e.g., audio loops like rotors). @ikostan

Milestone 8: Player Upgrade: New P-38 Sprite, Dual Rotors with Animation & Stereo Sound, Part #9

20 Dec 07:41
362f8f7

Choose a tag to compare

New P-38 Sprite for player, Dual Rotors with Animation & Stereo Sound + Disable Thread Support

This release combines two PRs:

  1. [FEATURE] Player Upgrade: New P-38 Sprite, Dual Rotors with Animation. #252:
    Enhances P-38 realism in SkyLockAssault: Visual rotor spin + immersive stereo audio (left/right engines). Teaches 2D animation, audio buses/panning, scene instancing, and options UI/sliders. Scalable for throttle/fuel sync later.
Image
  1. Disable Thread Support in the Godot 4.5 export preset #253
    Disabling Thread Support avoids the SharedArrayBuffer (SAB) issues in browsers, letting the web build load smoothly without needing COOP/COEP headers on the local server. It's a solid workaround for stable testing while learning game dev on Win10 64-bit, especially since your rotors animation and sound loop fine in single-thread mode for now (though threads could optimize perf later).
image

Why is this useful? (e.g., for learning Godot web exports)

Enhances P-38 realism in SkyLockAssault: Visual rotor spin + immersive stereo audio (left/right engines). Teaches 2D animation, audio buses/panning, scene instancing, and options UI/sliders. Scalable for throttle/fuel sync later.

Summary

Add animated dual-rotor visuals and stereo engine audio to the P-38 player, and adjust player bounds and projectile scaling to match the new art assets.

New Features:

  • Introduce left and right rotor scenes with animated sprites and stereo AudioStreamPlayer2D nodes for the player aircraft. @ikostan
  • Add a rotor SFX volume control scene and hook it into the options menu for in-game audio adjustments. @ikostan

Enhancements:

  • Update the Player scene to use a new P-38 sprite and polygon-based collision, recalculating movement bounds from the sprite size instead of the collision shape. @ikostan
  • Reduce projectile sprite scale to better fit the updated visual style. @ikostan
  • Adjust the default audio bus layout to support the new rotor SFX routing. @ikostan
  • Rotor Sound Stops After ~6s – Switch to Stream Playback (Godot 4.5, Win10 64-bit):
    Cause (Game Dev Learning): Godot's HTML5 exports default to Sample playback mode for low-latency audio in browsers (WebAudio API). This mode prioritizes quick SFX (e.g., guns) but has bugs with looping (your ~6s stop = file end without repeat). OGG loops fail despite import ☑ or script .loop=true – web ignores them in Sample. PC uses full Stream mode (no issue). Weaker hardware/browsers worsen it (freezes/crackles). @ikostan
  • In _ready() you only null-check rotor_left_sfx but then unconditionally access rotor_right_sfx; consider independently validating both nodes (or failing fast with a clear error) to avoid a potential null dereference if only one rotor SFX is missing. @sourcery-ai
  • issue (bug_risk): Avoid shadowing the member screen_size with a local variable.
    This line redeclares screen_size as a local var, so the existing member var screen_size: Vector2 at the top of the script is never updated in _ready(). If other methods read the member, it will keep its default value. Assign to the member directly (remove var here) or rename the local variable. @sourcery-ai
  • The movement bounds now depend on player_sprite.texture.get_size(), but there’s no guard for a missing texture; adding a check with a sensible fallback (or asserting the texture’s presence) will prevent hard-to-debug crashes if the sprite setup changes. @sourcery-ai
  • The use of / 4 scale factors when computing player_x_min/max and player_y_min/max is a bit opaque; it might be clearer to derive these from explicit constants (e.g., margin or hitbox scale) or document why a quarter of the texture size is used so future sprite swaps remain straightforward. @sourcery-ai

Build:

  • Update the web export index.html configuration to reference the new, larger .pck size generated by the updated assets.

1. New Player Sprite

  • Replaced old Sprite2D texture in player.tscn (CharacterBody2D/Sprite2D).
  • New: Colorful top-down P-38 Lightning (e.g., cyan/yellow/green pixel art from free asset packs like OpenGameArt).
  • Inspector: Texture imported as PNG/JPG, Centered ☑, Offset (0,0).
  • Position: Centered under CharacterBody2D for collision/pivot.
  • Learning: Improves visual fidelity; adjust scale (e.g., 0.25) for game view.

2. Dual Rotors Setup (Left/Right Scenes)

  • Created rotor_left.tscn & rotor_right.tscn: Node2D root ("RotorLeft/Right") > AnimatedSprite2D child + AudioStreamPlayer2D child.
  • Instanced in player.tscn under CharacterBody2D: RotorLeft at Position (-12.8, -25.0), RotorRight at (+12.8, -25.0) – wing-aligned for P-38 symmetry.
  • Script (player.gd): @onready var rotor_left: Node2D = $CharacterBody2D/RotorLeft & right. Gets children for play().
  • Learning: Scene instancing = reusable (e.g., add particles later). Positions via gizmo for precision.

3. Rotor Animation

  • AnimatedSprite2D in each rotor.tscn: SpriteFrames resource with 11 JPEG frames (propeller rotations).
  • Settings: FPS=24.0, Loop ☑, All frame durations=0.04 (select all > set).
  • Auto-start: In _ready() (player.gd): rotor_left.get_node("AnimatedSprite2D").play("default") & right.
  • Matches ~2600 RPM P-38 visual at game speed.
  • Learning: Frame animation > rotation for detailed blur/realism. Throttle bonus: speed_scale linkable to fuel/speed.

4. Rotor Sound (Stereo SFX)

  • AudioStreamPlayer2D in each rotor.tscn: Stream = res://sounds/rotor.ogg (free twin-prop loop from Freesound/OpenGameArt, imported with Loop ☑).
  • Settings: Autoplay ☑ (backup), Volume dB=-5 (balance), Max Distance=2000, Attenuation=1/2000, Panning Strength=1.0.
  • Script (player.gd): @onready var rotor_left_sfx: AudioStreamPlayer2D = rotor_left.get_node("AudioStreamPlayer2D") & right.
  • In _ready(): rotor_left_sfx.play() & right (after buses set).
  • OGG loop: ogg_stream.loop = true; ogg_stream.loop_offset = 0.0 (prep in Audacity for seamless).
  • Learning: 2D nodes = positional audio (slight pan on plane turns).

5. Audio Buses & Panning (L/R Split)

  • New buses: SFX Rotors (child of Master, for group volume).
  • Sub-buses: SFX Rotor Left (Panner Pan=-1.0, hard LEFT speaker) & SFX Rotor Right (Pan=+1.0, hard RIGHT).
  • Script: rotor_left_sfx.bus = "SFX Rotor Left" & right.
  • Detune realism: In throttle setter – rotor_left_sfx.pitch_scale = engine_throttle * 0.98 (low) & right *1.02 (high).
  • Learning: Buses inherit volume (SFX slider controls all). Panner = cheap stereo immersion (headphones test!).
Image

6. Options Menu: Rotors Volume Slider

  • In options_menu.tscn: Added SFXRotorsVolumeControl (HSlider or similar, child of VBoxContainer).
  • Script (e.g., sfx_rotors_volume_control.gd): Links to "SFX Rotors" bus volume (e.g., AudioServer.set_bus_volume_db(AudioServer.get_bus_index("SFX Rotors"), value)).
  • UI: Label "ROTORS VOLUME", slider 0-100 (map to dB -72 to 0).
  • Learning: Teaches AudioServer API + UI signals for dynamic settings.
Image

7. Overall Integration & Testing

  • All in player.gd _ready(): Auto-starts visuals/sounds on main_scene load.
  • Throttle/Fuel link: Export engine_throttle, setter updates sprite speed_scale + audio pitch_scale (ties to fuel drain).
  • Test: F5 main_scene.tscn – spin + whir instant, L/R split, options slider fades rotors (inherits SFX).
  • No errors: Fixed OGG loop (bool, not enum). Positions/collisions unaffected.

Additional Context:

  • Assets: Rotor frames (JPEG), sound (OGG) – import/reimport for changes.
  • Future: Sync throttle to input/fuel, add engine start/stop.
  • Win10 64-bit: VSync ☑ (Project Settings) for smooth animation/audio.

Reviewer's Guide

Implements animated dual-rotor visuals and stereo audio for the P-38 player ship, updates collision and boundary logic to match the new sprite/scene setup, tweaks projectile visuals, and adds scene/assets for rotor instances and audio volume control (reflected in the web export size).

File-Level Changes

Change Details Files
Hook up animated dual rotors and stereo rotor audio to the player ship.
  • Add onready references to left/right rotor Node2D children and their AnimatedSprite2D nodes.
  • Start rotor animations automatically in _ready at a fixed FPS.
  • Lookup and cache left/right AudioStreamPlayer2D nodes under each rotor.
  • Autoplay and play both rotor audio streams on startup with debug logging and fallback logging when SFX nodes are missing.
scripts/player.gd
scenes/Player.tscn
scenes/rotor_left.tscn
scenes/rotor_right.tscn
scenes/sfx_rotors_volume_control.tscn
default_bus_layout.tres
scenes/options_menu.tscn
Align player collision and screen-boundary logic with the new P-38 sprite/scene configuration.
  • Replace CollisionShape2D onready reference with CollisionPolygon2D to match the updated player scene.
  • Remove rectangle-based half-width/height calculations in favor of using the Sprite2D texture size.
  • Recompute player movement bounds using fractions of the player sprite’s texture dimensions for both X and Y limits.
  • Log updated configuration via Globals to help validate screen boundary behavior.
scripts/player.gd
scenes/Player.tscn
Adjust projectile visual scale to better match th...
Read more

Milestone 8: Game Assets Pack, Part #8

19 Dec 05:43
38add4b

Choose a tag to compare

Changes

I compiled this list based on searches across OpenGameArt. Chabull has about 15-20 submissions, mostly from 2013, focusing on 2D sprites suitable for top-down views. Not all are WW2-themed (some are fantasy icons), but since we like them all, I've included them. For each, download the zip/file from the page.

Asset Title Description Link
Explosions Pixel art explosion animations in various sizes, useful for combat effects in your airplane game. https://opengameart.org/content/explosions-0
Train Top-down train sprites including locomotive and wagons, great for railroad backgrounds or targets. https://opengameart.org/content/train
Bridges Assorted bridge sprites (wooden, stone) in top-down view, with segments for building longer ones—perfect for engineering structures or destructible elements. https://opengameart.org/content/bridges
Boss Aircraft Large boss-level airplane sprites in WW2 style, ideal for end-level enemies in your multi-level game. https://opengameart.org/content/boss-aircraft
Buildings / Bunkers / Weapon Platforms Top-down structures like buildings, bunkers, and turrets, with normal and destroyed states—excellent for city views, ruins, or targets. https://opengameart.org/content/buildings-bunkers-weapon-platforms
Tanks and Trucks Ground vehicles like tanks and trucks in top-down WW2 style, for adding variety to terrain or enemies. https://opengameart.org/content/tanks-and-trucks
Aircrafts Multiple airplane sprites (fighters, bombers) in top-down view, core for your combat and player planes. https://opengameart.org/content/aircrafts
Trees and Bushes Natural vegetation sprites for grass/forest backgrounds, top-down style to layer in levels. https://opengameart.org/content/trees-and-bushes
Ships with Ripple Effect Naval ships with water ripple animations, top-down—great for water-based levels or backgrounds. https://opengameart.org/content/ships-with-ripple-effect
Game HUD // Top Down Shoot Em Up UI elements like health bars, ammo counters for shoot-em-up games, fits your weapons and fuel management. https://opengameart.org/content/game-hud-top-down-shoot-em-up
Book Icon Simple book icon sprite, possibly for menus or collectibles (fantasy style). https://opengameart.org/content/book-icon
Shield and Swords Weapon icons (shields, swords), could repurpose for upgrades or menus. https://opengameart.org/content/shield-and-swords
Game Icons Assorted icons for items, useful for UI in difficulty adjustments or levels. https://opengameart.org/content/game-icons
Potions Potion sprites, fantasy but could be fuel or power-ups in your game. https://opengameart.org/content/potions
Boxes and Barrels Crates and barrels for destructible props or factory backgrounds. https://opengameart.org/content/boxes-and-barrels
Upgrade Screen / Shoot Em Up Game Upgrade menu assets for shoot-em-ups, ties into multiple weapons and difficulty. https://opengameart.org/content/upgrade-screen-shoot-em-up-game

@ikostan

Milestone 8: Implement Controller Support, Part #7

19 Dec 02:58
356c174

Choose a tag to compare

Implement Gamepad/Controller Input Support with Remapping for Enhanced Accessibility and Gameplay.

Add joypad support for Gamepad PC Controller and arrow keys for movement, inspired by Godot tutorial on controller input management.

Summary

Add robust controller and keyboard remapping support, extend input settings persistence to handle multiple input types and formats, and refine player fuel UI/logic and projectile lifecycle, with comprehensive tests for all changes.

New Features:

  • Support remapping of keyboard, joypad buttons, and joypad axes via an in‑game InputRemapButton, including readable labels and multi-event bindings. @ikostan
  • Persist input mappings for keys and controllers across sessions, including multiple events per action and new actions such as pause, with migration from the old config format. @ikostan

Bug Fixes:

  • Prevent potential errors when freeing projectiles by validating instances before queue_free and using a dedicated lifetime timer node.
  • Ensure player movement and fuel systems respect new input actions and clamped fuel values, avoiding negative fuel and inconsistent UI state.

Enhancements:

  • Refactor settings loading/saving to serialize all input events per action, add robust deserialization with validation and logging, and preserve project defaults when no overrides exist. @ikostan
  • Improve the fuel bar to initialize and update via a dedicated helper that smoothly interpolates colors across fuel thresholds. @ikostan
  • Simplify and harden player tests to use direct scene instantiation and add coverage for movement, clamping, and fuel behavior. @ikostan
  • Expand and modernize tests for settings and input remap behavior, covering controller inputs, backward compatibility, malformed configs, and migration flags. @ikostan

Tests:

  • Greatly expand GdUnit tests for settings, input remapping, and player behavior, including keyboard/controller persistence, migration paths, label rendering, remap flows, movement, clamping, and fuel UI. @ikostan

Reviewer's Guide

Adds configurable keyboard and controller input support (including axes) with persistent remapping, updates player movement and fuel UI to use the new actions, and significantly expands tests and settings handling to cover the new input model, migration, and edge cases. @sourcery-ai

File-Level Changes

Change Details Files
Implement multi-device, multi-event input mapping persistence with backward-compatible serialization and migration logic.
  • Extend settings singleton to support saving/loading arrays of serialized InputEvent mappings for each action instead of single keycodes.
  • Introduce serialize_event and _deserialize_and_add helpers to handle keys, joypad buttons, and joypad motion events with robust validation.
  • Preserve project defaults when no saved mapping exists, add default key bindings for new speed_up/speed_down/pause actions, and handle unbound actions cleanly.
  • Add migration flag to detect old single-int configs, trigger one-time rewrite to new format, and log or skip malformed mappings safely.
scripts/settings.gd
test/test_settings.gd
Enhance InputRemapButton to support remapping and displaying keyboard keys, joypad buttons, and axes, and add comprehensive tests.
  • Refactor InputRemapButton to manage listening state, erase/replace events at a configurable index, and save mappings via Settings after remap.
  • Add custom label dictionaries for keys, joypad buttons, and axes, including directional labels for common stick/trigger directions, with sensible fallbacks.
  • Update input handling to normalize joypad axis values, treat device as -1 (any controller), and log remap operations at debug level.
  • Create extensive GdUnit4 tests covering label display for multiple input types, remapping flows for keys/buttons/axes, unbound states, fallback labels, and invalid index handling.
scripts/input_remap_button.gd
test/test_input_remap_button.gd
Align player movement and fuel UI behavior with the new input actions and improve fuel bar feedback logic and tests.
  • Switch player movement to use speed_up/speed_down instead of move_forward/move_backward in Input.get_vector and update tests accordingly.
  • Factor fuel bar updates into update_fuel_bar, clamping fuel before UI updates and interpolating the fill color smoothly between green, yellow, red, and dark red across thresholds.
  • Ensure fuel never goes negative, stop movement and fuel timer at zero, and add tests for fuel depletion, clamping, and color transitions using approximate comparisons.
  • Simplify logging by commenting out noisy fire debug logs in player and bullet scripts and tightening projectile lifetime/cleanup with an attached Timer node and safe queue_free usage.
scripts/player.gd
scripts/bullet.gd
test/test_player.gd

Assessment against linked issues

Issue Objective Addressed Explanation
#238 Add controller/gamepad input support (buttons and axes) for core gameplay actions (movement, fire, next_weapon) and integrate it into the existing input system and gameplay code.
#238 Extend the input remapping system (including the options menu wiring) to support remapping keyboard keys, controller buttons, and controller axes, and persist these mappings via the Settings singleton with backward compatibility.
#238 Provide arrow keys as an alternative movement scheme alongside existing controls (e.g., WASD) within the new action setup.
#240 Guard against out-of-range access when logging the newly bound input event in InputRemapButton by checking the events array size before indexing with action_event_index.
#240 Refactor the logging code to use a local events array variable and safely skip or simplify the log when the index is out of range, avoiding runtime errors.
#241 Guard deserialization of joybtn: mappings in settings.gd so that malformed strings (e.g. missing or invalid indices/devices) do not index beyond parts.size(), are skipped safely, and optionally log a warning.
#241 Guard deserialization of joyaxis: mappings in settings.gd so that malformed strings (e.g. missing or invalid axis/values/devices) do not index beyond parts.size(), are skipped safely, and optionally log a warning.
#242 Clamp current_fuel before any UI or percentage calculations, and compute fuel_percent / lerp_factor and fuel bar color based on this clamped value so movement and UI stay consistent.
#243 Eliminate the HTML5 runtime error caused by the projectile lifetime lambda in bullet.gd ("Lambda capture at index 0 was freed" and subsequent null queue_free), ensuring bullets are safely freed on hit or timeout without console errors.
#242 Clamp current_fuel before any UI or percentage calculations, and compute fuel_percent / lerp_factor and fuel bar color based on this clamped value so movement and UI stay consistent.
#244 Using event.key_label may be invalid in Godot 4 and could break key display.
#245 lerp_factor and progress_bar_bg_color are set but no longer used in the new fuel bar logic.
#247 Treating all ConfigFile load errors as “no settings file” hides parse/IO problems..

Possibly linked issues

  • #ISSUE_NUMBER: PR delivers the requested controller support, remapping (keys, buttons, axes), and updates gameplay and settings accordingly.
  • #FEATURE: PR implements the requested persistent keyboard remapping (via Settings and InputRemapButton), plus extra controller and gameplay changes.

Milestone 8: Bug Fixes & Maintenance, Part #6

16 Dec 23:55
896ca87

Choose a tag to compare

Summary

  • Prevent the options menu from briefly allowing pause toggling when opened and refresh CI and build-related configuration.
  • Ensure the global options menu state is managed centrally and robustly, while refreshing CI workflows and web export configuration.

🚀 Features

  • 224 feature options open is only set in options menu ready which leaves a small window where esc can still toggle pause (#236) @ikostan

🧰 Maintenance/CI

  • 224 feature options open is only set in options menu ready which leaves a small window where esc can still toggle pause (#236) @sourcery-ai
  • Bump github/codeql-action to v4.31.8 across CodeQL and Snyk workflows and refresh the pinned upload-sarif SHA in the Trivy workflow. @dependabot
  • Dependabot/GitHub actions/GitHub/codeql action 4.31.8 (#235) @dependabot
  • Dependabot/GitHub actions/actions/cache 5 (#234) @dependabot
  • Upgrade all uses of actions/cache in browser tests, deployment, linting, and yamllint workflows to v5. @dependabot
  • Merge from master (#233) @ikostan
  • Merge from master (#232) @ikostan

Issue Addressed:

  • Now that Globals.options_open is set in load_options, the commented-out assignment in options_menu.gd is dead code—either remove it or replace it with a brief comment explaining that the flag must be controlled centrally from load_options to avoid future confusion or accidental reintroduction.
  • Since Globals.options_open is now flipped before instantiating the options menu, consider what should happen if instantiation fails or the node is freed unexpectedly (e.g., wrap instantiation in a match/if and reset the flag or connect to a destruction signal) to avoid leaving the global flag stuck in the open state.
  • In globals.gd, the comments ## globals.gd (add after load_options func) and ## globals.gd (update handler) read like review notes rather than in‑code documentation—consider removing or replacing them with a concise description of the handler’s behavior to avoid confusion.
  • In test_unexpected_exit_resets_flag, the spy_globals variable is created but never used; consider removing it to keep the test focused and avoid confusion about intended spying behavior.

Bug Fixes:

  • Set the global options_open flag before instantiating the options menu to eliminate the short window where the pause state could still be toggled with ESC.
  • Set the global options_open flag before instantiating the options menu and handle failed instantiation or unexpected exits to avoid stuck pause state and brief ESC toggling window.

Enhancements:

  • Centralize options menu lifecycle handling in Globals, including an unexpected-exit handler that cleans up global references.

Build:

  • Update the exported web index.html configuration to align with the current web build output.

Tests:

  • Expand options teardown tests to cover load_options-based flag management, hidden menu restoration, and unexpected options exit handling. @sourcery-ai

Reviewer's Guide

Refactors options menu loading/teardown so the global options_open flag is set and cleared centrally and robustly, adds tests for unexpected teardown behavior, removes now-redundant flag setting in options_menu.gd, and refreshes several CI/build workflows (CodeQL, actions/cache, SARIF upload) to newer versions.

File-Level Changes

Change Details Files
Centralize and harden options menu lifecycle handling so options_open is set before instantiation and reliably cleared, including on failed or unexpected exits.
  • Update load_options to set Globals.options_open before instantiating the options menu so ESC-based pause is blocked immediately.
  • Add guards in load_options to reset the options_open flag and restore the hidden menu if options scene instantiation fails.
  • Connect options_instance.tree_exited to a new handler that logs and resets options_open and clears the instance reference on unexpected exits.
  • Remove the redundant options_open assignment from options_menu._ready now that the flag is controlled entirely from load_options.
scripts/globals.gd
scripts/options_menu.gd
Expand options teardown tests to reflect the new lifecycle behavior and cover unexpected exit handling.
  • Adjust tests to use Globals.load_options instead of manually instantiating the options scene and to assert options_open is set by load_options.
  • Use Globals.options_instance for teardown in tests rather than local variables to match production behavior.
  • Add a new test that simulates an unexpected exit of options_instance and verifies the global flag and instance reference are reset by the handler.
  • Improve test file header documentation and type annotations for clarity.
test/test_options_teardown.gd
Refresh CI and security scan workflows to newer action versions and correct pinned SHAs.
  • Bump github/codeql-action from v4.31.7 to v4.31.8 in CodeQL and Snyk-related workflows, including SARIF upload.
  • Update the pinned SHA for github/codeql-action/upload-sarif in the Trivy workflow to a valid current stable commit.
  • Upgrade uses of actions/cache from v4 to v5 across browser tests, gdlint, yamllint, and deployment workflows.
  • Keep workflow behavior otherwise unchanged aside from action versions.
.github/workflows/codeql.yml
.github/workflows/browser_test.yml
.github/workflows/snyk.yml
.github/workflows/deploy_to_itch.yml
.github/workflows/gdlint.yml
.github/workflows/trivy.yml
.github/workflows/yamllint.yml
Update exported web configuration to match current build output.
  • Adjust export/web/index.html to reflect the latest asset size or configuration for the main PCK file (exact diff truncated in snippet).
export/web/index.html

Assessment against linked issues

Issue Objective Addressed Explanation
#224 Set Globals.options_open to true in Globals.load_options before the options menu is instantiated/added, and ensure it is only reset via the options menu teardown/close path (including safe handling of instantiation failure or unexpected exit).
#224 Remove responsibility for managing Globals.options_open from options_menu._ready so that it no longer sets (or resets) the flag during menu creation.
#224 Ensure all code paths that open the options menu go through Globals.load_options so the options_open flag is set before any add_child(options_instance) call.

Possibly linked issues

  • #223: Yes. The PR moves options_open into load_options, centralizes its reset, adds tests, and fixes the ESC pause window.
  • #unknown: PR adds central load_options handling and unexpected-exit reset for options_open, fixing the stuck-true options teardown bug

Milestone 8: Maintenance, Part #5

16 Dec 02:51
4d4f614

Choose a tag to compare

Summary

Update documentation and CI configuration for maintenance and formatting improvements.

CI:

  • Bump the pinned SHA for the markdownlint-cli2 GitHub Action in the README lint workflow.

Documentation:

  • Align SECURITY policy Godot version references with current supported release and clarify example environment. @sourcery-ai
  • Reflow documentation text in Docker local test server and contributing guide for better readability. @sourcery-ai
  • Annotate README music credits section with a markdownlint directive to allow longer lines. @sourcery-ai

🧰 Maintenance/Bump actions


Reviewer's Guide

Maintenance PR that aligns documentation with the current Godot version, tidies markdown formatting for readability and linting, and updates the markdownlint GitHub Action pin to a newer commit for security/maintenance.

File-Level Changes

Change Details Files
Align security policy and examples with the current supported Godot version.
  • Updated references in the security policy from Godot v4.5 to v4.4 to match current target runtime.
  • Adjusted the support matrix table spacing so columns visually align in markdown.
.github/SECURITY.md
Improve markdown readability and satisfy markdownlint by wrapping long lines and adding explicit lint directives where needed.
  • Rewrapped long prose lines in documentation to keep line lengths reasonable without changing content.
  • Split long reference lines in the contributing guide for better formatting.
  • Inserted a markdownlint directive in the README to disable line-length checks for a long Music credits list.
files/docs/Docker_Local_Test_Server.md
CONTRIBUTING.md
README.md
Update the markdownlint GitHub Action pin to a newer, vetted commit for maintenance/security.
  • Bumped the pinned SHA for DavidAnson/markdownlint-cli2-action to a newer commit while keeping configuration the same.
.github/workflows/lint_readme.yml

Milestone 8: Ensure menu items do not overlap eg hide main menu when options opens, Part #4

16 Dec 01:36
1fd84f5

Choose a tag to compare

Summary

Ensure only one menu is visible at a time when the options menu is opened, and centralize menu visibility handling through Globals.

New Features:

  • Track the currently hidden menu and options menu state globally to coordinate UI visibility across scenes. @ikostan

Bug Fixes:

  • Prevent main and pause menus from overlapping with the options menu by hiding the invoking menu while options are open and restoring it when options close. @ikostan
  • Guard options menu loading against re-entrancy to avoid multiple overlapping options instances and restore the caller menu if loading fails. @ikostan
  • Ignore pause toggle input when the options menu is open to avoid unintended state changes. @ikostan

Enhancements and addressed issues: @sourcery-ai

  • Improve pause and options menu scripts with clearer logging, documentation comments, and safer signal connections.
  • The new Globals.load_options(menu_to_hide: Node) assumes a non-null, still-in-tree node; consider guarding against null or freed nodes and logging a warning instead of blindly dereferencing menu_to_hide.visible to avoid runtime errors from unexpected callers.
  • The get_tree().paused = false line in options_menu._on_back_pressed() is now fully commented out; if only options opened from the pause menu should remain paused, consider restoring the prior pause state instead of hardcoding or removing it so behavior is consistent across entry points.
  • When restoring Globals.hidden_menu in options_menu.gd, consider guarding with is_instance_valid before accessing .visible and .name to avoid errors if the menu was freed while options were open.
  • get_tree().paused = false was removed from in _on_back_pressed; double-check that the game is still unpaused appropriately when exiting options (especially when opened from the pause menu), or move that responsibility explicitly to the caller to keep the behavior clear.
  • The test_menu_visibility.gd mock for Globals relies on dynamically adding properties to a generic Node; using a small dedicated script/class or a Dictionary for the mock would make the test setup more robust and easier to maintain.
  • In options_menu.gd._on_back_pressed you set Globals.options_open = false twice (before and after clearing hidden_menu); you can remove the duplicate assignment to keep the teardown logic simpler and clearer.
  • The options menu teardown logic (restoring hidden_menu, clearing options_open and options_instance) is duplicated between _exit_tree and _on_back_pressed; consider centralizing this into a single helper on Globals or the options menu to avoid divergence and make future changes safer.

Fixing GDUnit Test State Leaks

Problem description

n the GDUnit tests you directly mutate global singletons (Globals.options_scene, Globals.options_instance) without always resetting them in before_test, which can leak state across tests; it would be more robust to fully restore any changed Globals fields after each test or to isolate them behind a dedicated test helper.

We'll fix by:

  • Adding before_test() and after_test(): Save originals in before (e.g., var orig_scene = Globals.options_scene), reset to defaults. Restore in after (e.g., Globals.options_scene = orig_scene).
  • For test_load_options_reentrancy.gd: Add these hooks—guards mutations (null scene, mock instance) without affecting other tests.
  • For test_options_teardown.gd: Enhance existing before_test() to include options_scene (though not mutated here, consistency wins).
  • Why? Godot singletons persist across tests; this "snapshot/restore" pattern (from GDUnit docs) isolates them. Educational bonus: Use auto_free() for mocks to avoid Win10 memory spikes in long test runs.

Tests:

  • Add GDUnit tests to verify options menu state flags are set and cleared correctly, hidden menus are restored on exit, menu visibility toggling behaves as expected, and load_options correctly guards against re-entrancy.

Reviewer's Guide

Adds global tracking and coordination so only one options menu can exist at a time, hides the invoking menu while options is open, blocks pause toggling during options, and introduces GDUnit tests to verify options teardown, menu visibility, and re-entrancy guards.

File-Level Changes

Change Details Files
Coordinate options menu visibility with the menus that invoke it via new global state and a guarded load_options API.
  • Introduce Globals.options_instance, Globals.hidden_menu, and Globals.options_open to track the active options menu and its invoking menu.
  • Refactor Globals.load_options to accept a menu_to_hide node, hide it when valid, guard against re-entrancy using options_instance, and restore the hidden menu if options_scene fails to load.
  • Update main_menu options button handler to pass ui_panel into Globals.load_options so the main menu is hidden while options is open.
scripts/globals.gd
scripts/main_menu.gd
Ensure pause menu respects the global options state and participates in the hide/show flow when opening options.
  • Guard pause menu ESC handling so it ignores ui_cancel while invisible and an options menu is open.
  • Connect pause, back-to-main, and options button signals with idempotent is_connected checks and ensure the pause menu processes input even while paused using PROCESS_MODE_ALWAYS.
  • Change pause menu options button handler to call Globals.load_options(self) so the pause menu is hidden while options is open and to improve logging and documentation comments.
scripts/pause_menu.gd
Make the options menu responsible for setting and clearing global options state and restoring any hidden menu on close.
  • Set Globals.options_open to true in options_menu._ready to mark options as active.
  • Add options_menu._exit_tree to restore visibility of Globals.hidden_menu when valid, clear hidden_menu and options_instance, and reset options_open on exit.
  • Adjust the Back button handler to restore and clear hidden_menu/options_open/options_instance instead of unpausing the game tree directly, while preserving web overlay teardown.
scripts/options_menu.gd
Add automated tests to validate menu visibility behavior and load_options re-entrancy/teardown logic.
  • Create test_options_teardown.gd to verify Globals.options_open is set in options_menu._ready, cleared in _exit_tree, and that hidden_menu visibility is restored and the reference is cleared.
  • Create test_menu_visibility.gd with a lightweight mock Globals dictionary to confirm load_options hides and later restores both main UI (Panel) and pause menu (CanvasLayer) style menus.
  • Create test_load_options_reentrancy.gd to assert load_options restores menu visibility when options_scene is null, leaves options_instance null in that case, and ignores subsequent calls when options_instance is already valid.
test/test_options_teardown.gd
test/test_menu_visibility.gd
test/test_load_options_reentrancy.gd

Assessment against linked issues

Issue Objective Addressed Explanation
#166 Implement menu visibility management so that when the Options submenu opens, the invoking parent menu (e.g., Main Menu, Pause Menu) is hidden, and that parent menu is restored when Options closes, preventing overlapping menus.
#166 Extend the visibility handling to all relevant menus (at least main and pause menus) and coordinate state so that menu interactions (e.g., pause toggling) do not conflict while the Options menu is open.
#166 Add automated tests to verify menu visibility toggling and options load behavior (including open/close and re-entrancy guards).
#212 Guard all accesses to Globals.hidden_menu.visible and Globals.hidden_menu.name in scripts/options_menu.gd with a validity check (Globals.hidden_menu and is_instance_valid(Globals.hidden_menu)), and clear Globals.hidden_menu afterward.
#213 Ensure the global options_open flag accurately reflects the lifetime of the options menu, including being cleared when the options menu node is freed via scene change or queue_free (e.g., by clearing it in _exit_tree in addition to back button handling).
#214 Update Globals.load_options so that when options_scene is null or fails to load, any previously hidden caller menu is made visible again and the global hidden_menu reference is cleared, preventing the UI from getting stuck hidden.
#220 Add guards in Globals.load_options to prevent multiple concurrent options menus / re-entrancy and avoid overwriting the stored hidden_menu while an options menu is already open.
#220 Track the active options menu and its invoking menu so that opening options hides the invoking menu and closing options reliably restores that menu’s visibility and clears global state.
#221 Ensure that when the options menu node exits the scene tree (e.g., via scene change) without _on_back_pressed, the previously hidden menu is correctly restored or Globals.hidden_menu is cleared so that no menu remains permanently invisible.
#221 Ensure teardown consistency of global state in options_menu.gd by resetting Globals.options_open and Globals.options_instance when the options menu is removed from the scene tree, not only when...
Read more

Milestone 8: Fixing issue with the black screen when the game starts, Part #3

12 Dec 03:37
10eaab0

Choose a tag to compare

About

In our custom HTML shell, the default Godot loading splash and progress indicators aren't automatically included because we're overriding the built-in template. This leads to the black screen while the browser downloads and initializes the .wasm and .pck files (which can take a few seconds depending on file sizes and connection speed). To fix this, we'll modify custom_shell.html to manually add a loading screen that displays the boot splash image (the Godot logo by default, unless you've changed it in Project Settings > Application > Boot Splash > Image). We'll also hook into the engine's startup promise to hide the loading screen once everything is ready.

Summary

Add a custom loading screen to the web HTML shell to replace the initial black screen while the Godot WebAssembly game loads and initializes. For full PR info see here.

image

New Features:

  • Display a full-screen loading overlay with the Godot boot splash image, progress bar, and status text while the web game loads. @ikostan
  • Provide a retry button on the loading screen to allow users to reload the page if the engine fails to start. @sourcery-ai

Enhancements:

  • Improve accessibility of the loading UI with ARIA attributes and status updates tied to load progress. @sourcery-ai
  • Hide the loading overlay once the Godot engine has successfully initialized to reveal the game canvas. @ikostan

Reviewer's Guide

Adds an explicit HTML loading overlay to the custom web shell, wires it into the Godot engine startup progress, and improves accessibility and error handling for web initialization failures.

File-Level Changes

Change Details Files
Add a full-screen loading overlay with splash image, progress bar, status text, and retry button to the custom HTML shell.
  • Define CSS rules for #loading, progress bar elements, status text, and retry button instead of inline styles.
  • Insert a #loading container into the body with a $GODOT_SPLASH image, ARIA-aware progress bar, live status text, and a hidden retry button.
custom_shell.html
Hook the custom loading UI into the Godot engine startup lifecycle with progress updates, completion handling, and error recovery.
  • Update engine.startGame call to pass an onProgress callback that updates the visual progress bar width, ARIA valuetext, and status label.
  • On successful startup, hide the loading overlay and mark it aria-hidden while signaling initialization completion via window.godotInitialized.
  • On startup failure, surface the error in the status text, visually mark failure, hide the progress bar, show a retry button, and leave the error state accessible.
  • Attach a click handler to the retry button that reloads the page to fully reset the engine startup sequence.
custom_shell.html

Assessment against linked issues

Issue Objective Addressed Explanation
#205 Display a loading UI (boot splash image and progress indication) during web export startup when using the custom HTML shell, instead of a black screen.
#205 Automatically hide the loading UI once the Godot engine has finished initializing and the main scene is ready to show.
#205 Integrate the loading behavior with the engine startup flow (including progress updates and error handling), similar to the default web template.
#207 When engine.startGame rejects, update or hide the loading UI so the user is not left with a stuck loading screen showing misleading loading progress.
#208 Keep an error state visible instead of auto-hiding the overlay when the Godot engine fails to initialize, avoiding a blank canvas while window.godotInitialized remains false.
#208 Provide a clear user-facing recovery path (e.g., a Retry action) from the error state instead of leaving users with no way to recover.
#208 Ensure that a true error state is distinguishable from a normal loading state (for both users and automated flows), by persisting visible error UI while window.godotInitialized stays false.

Possibly linked issues

  • #(not specified): PR adds a splash/progress loading overlay in the custom shell, fixing the reported black loading screen issue.
  • #bug_risk: They both address engine.startGame failure leaving a stuck loading overlay; PR adds explicit error messaging and retry.
  • #(unnumbered in prompt): PR replaces auto-hiding error with persistent error view and retry button, preventing blank unrecoverable screens during startup.