Asteroids Destroyer Dev Log – 19 April 2026

Asteroids Destroyer โ€” Development Journal

Dev Log โ€” 19 April 2026

๐Ÿ“… Session 4
๐ŸŽฎ Godot 4.6
โšก Power-ups
๐ŸŽจ UI Polish
๐Ÿ› Bug Fixes
๐Ÿ“‹
Session Overview
Resumed from 14 April; the context window ran out mid-session again

I resumed the pending work from 14 April that was cut off when the context window ran out. Here’s what I got through:

  1. Container removal across all remaining menus (PauseMenu, Settings, LevelComplete, CharacterShop)
  2. Level Select grid overflow fix: 5ร—5 โ†’ 6ร—4 (exact 24 tiles)
  3. Endless button Sprite2D spin animation (node path debugging)
  4. Settings slider asset fix: wrong icon property and swapped textures
  5. Power-up system: Health and Shield pickups with full spawn logic
  6. Level completion retry bug: Retry was loading the next level instead of replaying the current one
๐Ÿ—‚๏ธ
Container Removal: All Menus
PauseMenu ยท Settings ยท LevelComplete ยท CharacterShop

CHANGED Continued the same fix applied to MainMenu on 14 April. All remaining menus that used VBoxContainer or HBoxContainer for button layout had those nodes converted to plain Control nodes with layout_mode = 0 (absolute positioning).

The problem: container-driven children get layout_mode = 2 which locks position and size to the container, so they cannot be freely moved or resized in the Godot 2D editor.

Scene Container removed Children affected
PauseMenu.tscn VBoxContainer โ†’ Control BtnResume, BtnSettings, BtnRestart, BtnMainMenu, all given absolute offsets
Settings.tscn Outer VBoxContainer โ†’ Control TitleLabel, RowMusic, RowSFX, RowFullscreen, BtnClose, each positioned absolutely. Inner HBoxContainers (label+toggle rows) kept but given absolute positions
LevelComplete.tscn HBoxContainer โ†’ Control BtnRetry (0โ†’300), BtnNext (320โ†’620) side by side with 20px gap
CharacterShop.tscn CoinsRow HBoxContainer โ†’ Control CoinsLabel given absolute size. Ship Grid GridContainer left as-is (code-driven)
The LevelSelect Grid and CharacterShop Grid were intentionally left as GridContainers, since they are populated entirely in code and benefit from automatic layout.
๐Ÿ—‚๏ธ
Level Select Grid: 6ร—4 Layout
scenes/menus/LevelSelect.tscn ยท scripts/menus/LevelSelect.gd

FIX The 5-column grid was producing 5 rows of tiles. The grid container had only 677px of available height but 5 rows of 175px tiles needed 955px, so the last two rows overflowed the panel frame entirely.

Fix: switched to 6 columns ร— 4 rows. 24 รท 6 = exactly 4 complete rows with no orphan tiles, and 4 rows of 152px tiles fit cleanly in the available height (650px needed vs 677px available).

columns = 6 theme_override_constants/h_separation = 12 theme_override_constants/v_separation = 14 btn.custom_minimum_size = Vector2(148, 152)

Confirmed working visually: all 24 level tiles displayed correctly within the panel frame.

โœจ
Endless Button Spin Animation
scripts/menus/MainMenu.gd

BUG The Endless button Sprite2D spin failed at runtime with:

E: Node not found: “UI/Centre/Buttons/MainButton2/Playbutton”

The node path in the @onready declaration didn’t match the actual scene tree. I found the correct path in the editor by right-clicking the node โ†’ Copy Node Path.

FIX Added a separate _spin2_active flag and _spin_endless_button() function mirroring the play button implementation. Connected to both btn_endless.mouse_entered and btn_endless.button_down (covers desktop hover and mobile touch).

Lesson: Sprite2D node paths must be verified from the editor. Right-click any node in the Scene panel โ†’ Copy Node Path to get the exact string. Don’t guess paths from .tscn source, because hand-edited scenes may differ from the written code.
โš™๏ธ
Settings Slider Icons Fixed
scenes/menus/Settings.tscn

BUG Two problems with the CheckButton toggle icons:

  1. Wrong property name:theme_override_icons/checked_mirrored is not a valid Godot 4 CheckButton icon property, so it was silently ignored. The correct property for the OFF state is theme_override_icons/unchecked.
  2. Swapped textures:settings_slider off.png was assigned to the checked (ON) state, and settings_slider on.png was on the invalid property. Both were wrong.

FIX Corrected all three toggles (Music, Sound Effects, Fullscreen):

theme_override_icons/checked = settings_slider off.png theme_override_icons/checked_mirrored = settings_slider on.png theme_override_icons/checked = settings_slider on.png theme_override_icons/unchecked = settings_slider off.png
โšก
Power-Up System
scripts/entities/PowerUp.gd ยท scenes/entities/PowerUp.tscn ยท Player.gd ยท GameScene.gd

New files

  • scripts/entities/PowerUp.gd: Area2D script with drift, pulse, fade, and collection logic
  • scenes/entities/PowerUp.tscn: Area2D root with Sprite2D + CollisionShape2D. Collision layer 4, mask 1 (detects player bodies)

Two pickup types

Type Asset Effect Drop weight
HEALTH (0) Game Assests/Powerups/Health-PU.png Calls gm.heal(), restores 1 HP (capped at 3) 60%
SHIELD (1) Game Assests/Powerups/Shield-PU.png Calls player.activate_shield(), 5s invincibility with animated overlay 40%

Pickup behaviour

  • Spawns at the asteroid’s death position and drifts in a random direction at 35px/s
  • Pulses gently (ยฑ8% scale using a sine wave)
  • Lifetime: 10 seconds. Fades out over the final 2.5 seconds
  • Collected on Area2D body_entered, filtered to “player” group only

Drop rates from asteroid destruction

LARGE asteroid (50 pts) โ†’ 25% chance to drop MEDIUM asteroid (25 pts) โ†’ 12% chance to drop SMALL asteroid (10 pts) โ†’ no drop

Size is inferred from the point value already passed by the destroyed signal, so no change to Asteroid.gd required.

Powerups spawn into a PowerupsContainer Node2D created at runtime in GameScene._ready(). The container is separate from AsteroidsContainer and BulletsContainer to keep the scene tree organised.

GameScene.gd: wiring

@export var powerup_scene: PackedScene # assign PowerUp.tscn in editor func _on_asteroid_destroyed(points: int, world_pos: Vector2) -> void: _gm.add_score(points) asteroids_alive -= 1 _try_spawn_powerup(points, world_pos) # new …
Editor step required: Open GameScene.tscn in the editor, select the root node, and assign res://scenes/entities/PowerUp.tscn to the new Powerup Scene export property in the Inspector.

Player.gd: shield system

NEWactivate_shield() method added:

  • Creates a Sprite2D child dynamically on first call, scaled 1.6ร— to wrap around the ship
  • Cycles through Game Assests/Shield/Shield1-6.png at ~80ms/frame
  • Sets is_invincible = true for the full 5 seconds, so take_damage() is silently ignored
  • Player sprite stays fully visible during shield (no blink, since the shield overlay is the feedback)
  • Shield and normal invincibility are mutually exclusive: if _shield_active: … elif is_invincible: …
  • _deactivate_shield() hides the shield sprite and clears invincibility cleanly when the timer expires
๐Ÿ›
Level Complete: Retry Bug Fixed
scripts/game/GameScene.gd ยท scripts/menus/LevelComplete.gd

BUG Pressing RETRY on the Level Complete screen loaded the next level instead of replaying the one just completed.

Root cause:GameScene._level_complete() incremented current_level before instantiating the LevelComplete overlay. LevelComplete._retry() then called reload_current_scene() with the already-incremented level number.

FIX Captured the completed level before incrementing and passed it explicitly to setup():

— GameScene._level_complete() — var completed_level: int = _gm.current_level # capture BEFORE increment … if _gm.current_level < 24: _gm.current_level += 1 … lc.setup(stars, _gm.session_score, completed_level) — LevelComplete._retry() — func _retry() -> void: _gm.current_level = _completed_level # restore before reload get_tree().reload_current_scene()

NEXT still works correctly, since _gm.current_level is already the next level by the time _next_level() is called.

๐Ÿ“Š
Project State at the End of 19 April
What is working and what remains

Complete

  • All menus: fully free-position layout (no containers locking children)
  • Level Select: 6ร—4 grid, 24 tiles, correct hover detection and scale tween
  • Settings: slider toggles display correctly (on/off assets, correct Godot properties)
  • Power-up system: Health and Shield pickups with full spawn, drift, pulse, fade, and collect logic
  • Shield: animated overlay from Shield1-6.png, 5 seconds, no blink, proper invincibility
  • Level Complete: stars display, correct Retry (replay same level), correct Next (advance to next)
  • Asteroid spawn: staggered queue from 8 edge points, level-scaled interval, 25% on-screen burst
  • Pause Menu: Resume / Settings / Restart / Main Menu
  • MainMenu: Play and Endless buttons both have Sprite2D 360ยฐ spin on hover/touch

Still outstanding

  • Editor step: Assign PowerUp.tscn to the powerup_scene export on GameScene root node
  • Endless button Sprite2D path: confirm correct path after user verifies in editor
  • Audio bus muting on session start (respect saved music_enabled / sfx_enabled)
  • Mobile joystick input not wired (HUD nodes exist, no logic)
  • Coin shop / IAP placeholder: not implemented
  • Panel frame visual alignment: still needs manual adjustment per screen in editor
  • Nuke / screen-clear weapon not yet implemented (GameManager has nukes_held but no activation)