Dev Log โ 19 April 2026
I resumed the pending work from 14 April that was cut off when the context window ran out. Here’s what I got through:
- Container removal across all remaining menus (PauseMenu, Settings, LevelComplete, CharacterShop)
- Level Select grid overflow fix: 5ร5 โ 6ร4 (exact 24 tiles)
- Endless button Sprite2D spin animation (node path debugging)
- Settings slider asset fix: wrong icon property and swapped textures
- Power-up system: Health and Shield pickups with full spawn logic
- Level completion retry bug: Retry was loading the next level instead of replaying the current one
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) |
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).
Confirmed working visually: all 24 level tiles displayed correctly within the panel frame.
BUG The Endless button Sprite2D spin failed at runtime with:
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).
BUG Two problems with the CheckButton toggle icons:
- 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.
- 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):
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
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
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
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():
NEXT still works correctly, since _gm.current_level is already the next level by the time _next_level() is called.
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)