Dev Log — 13 June 2026
Session Overview
Long session covering five distinct areas: a long-standing collision shape warning, a shield always-visible bug, impact and death visual effects, random colour enemy units, and a fully separate death sequence for BGS capital ships.
- Fixed: “This node has no shape” collision warning on
Player.tscn; the root cause was aNode2Dintermediate parent breaking Godot 4’s physics engine detection - Fixed: Shield sprite visible during gameplay at all times
- New: Small explosion animation at point of impact when player is hit
- New: Random colour variants for enemy units (matching existing mothership approach)
- New: Full ship death effect: debris pieces flying outward + seam explosions for fighters; 13-blast hull-covering grid for BGS capital ships
The “This node has no shape” Collision Warning
Fixscenes/entities/Player.tscn was logging a warning for every CollisionPolygon2D in the scene, even though all 18 polygons had valid geometry assigned.
Root Cause
All 18 CollisionPolygon2D nodes were children of an intermediate Node2D called CollisionScaler, which was itself a child of the CharacterBody2D root. Godot 4’s physics engine only registers collision shapes that are direct children of a physics body; grandchildren through a plain Node2D are silently ignored and produce the warning.
Fix
Removed the CollisionScaler node entirely and re-parented all 18 CollisionPolygon2D nodes directly under the CharacterBody2D root. All references in Player.gd to CollisionScaler were removed and the enable/disable logic now targets the polygons directly by name.
Code Changes
_editor_update_preview() and _apply_ship_setup() both now iterate the collision name list and set disabled = true on all, then disabled = false on only the active one, with no CollisionScaler involved.
Shield Always Visible in Game
Fix The ShieldPreviewSprite2D was left visible in the scene file for editor tuning purposes and was never hidden at runtime.
Fix
Added two lines at the top of _ready() in Player.gd to hide the node at runtime:
var sp_prev := get_node_or_null("ShieldPreview") as Sprite2D
if sp_prev:
sp_prev.visible = false
The ShieldPreview node remains visible in the editor (for per-ship tuning) but is hidden the moment the game starts.
Impact Explosion at the Point of Hit
New When the player takes damage the ship previously only flashed. A small explosion animation now spawns at the exact point of contact.
Impact Position Propagation
take_damage() was extended to accept an optional impact_pos: Vector2 parameter (defaults to global_position for backwards compatibility). All four call sites were updated:
EnemyBullet.gdpassesglobal_positionof the bulletEnemyUnit.gdpassesglobal_positionof the unitEnemyMothership.gdpassesglobal_positionof the mothership- Player-asteroid collision passes
KinematicCollision2D.get_position()(exact contact point)
Animation
_spawn_hit_effect(impact_pos) loads the 9-frame Explosion1.png–Explosion9.png sequence, spawns a temporary Sprite2D at the impact position, and drives it with a Timer at 25 fps. Display size is 30 px.
GDScript Lambda Closure Bug: Fixed
The first implementation used var frame_idx: int captured by the timer lambda. The animation never advanced because GDScript captures int as a value copy, so mutations inside the lambda don’t persist between calls.
var frame: Array[int] = [1]. Arrays are reference types so frame[0] += 1 inside the lambda mutates the same object and the frame counter advances correctly. This pattern is now the project standard for timer-driven animation state inside lambdas.
Random Colour Enemy Units
New Enemy units previously always used the single default grey texture. They now pick a random colour variant on spawn, matching the mothership behaviour introduced in an earlier session.
Implementation
In EnemyUnit.gd, the single TEXTURE_PATH constant was replaced with a TEXTURE_PATHS: Array[String] containing 9 variants:
const TEXTURE_PATHS: Array[String] = [
"res://Game Assets/Enemys/enemy_unit.png",
"res://Game Assets/Enemys/Enemy Unit Green.png",
"res://Game Assets/Enemys/Enemy Unit Light Blue.png",
"res://Game Assets/Enemys/Enemy Unit Light Green.png",
"res://Game Assets/Enemys/Enemy Unit Light Orange.png",
"res://Game Assets/Enemys/Enemy Unit Light Purple.png",
"res://Game Assets/Enemys/Enemy Unit Light Red (1).png",
"res://Game Assets/Enemys/Enemy Unit Light White.png",
"res://Game Assets/Enemys/Enemy Unit Light Yellow.png",
]
_ready() picks one at random and scales it to the 55 px target display size, identical to how the mothership handles its 15 variants.
Fighter Ship Death Effect
New When a fighter-series player ship is destroyed it now produces a full multi-part explosion rather than just disappearing.
Debris Pieces
Five Sprite2D nodes are spawned, each showing a cropped region of the ship’s own texture using region_enabled = true and region_rect. This avoids needing separate artwork for every ship variant.
- Nose section: flies upward
- Left wing: flies left and slightly down
- Right wing: flies right and slightly down
- Engine section: flies downward
- Centre core: flies in a random direction
Each piece launches at 90–160 px/s with ±15° rotation jitter on the initial heading. Velocity and offset are rotated by the ship’s global_rotation at the time of death so pieces fly in the correct world-space directions regardless of heading. A Tween fades each piece to transparent over 0.8 s then frees it.
Seam Explosions
The crop edges between pieces are straight lines and were clearly visible. Small explosions are spawned in a zig-zag pattern along each of the four cut lines to disguise them.
- 4 cut lines defined (nose bottom, engine top, left wing right edge, right wing left edge)
- Each cut sampled at
nevenly spaced points (6–16 depending on ship size) - Alternating ±
zzpx perpendicular offset creates a jagged break appearance - Explosion size and point count scale with ship display size using
sqrtsub-linear scaling so they don’t become enormous on large ships
Final Blast Chain
6 staggered explosions fire outward from the ship centre over 0.34 s to complete the destruction sequence.
BGS Capital Ship Death Effect
New BGS ships (series 6 & 7) are 392×1057 px portrait textures displayed at ~495 px tall, far larger than fighter ships. The region-crop debris approach produced massively overlapping pieces and the cut seams were very obvious. A dedicated capital ship destruction sequence was implemented instead.
Approach
BGS ships skip the debris pieces and seam explosions entirely (if not _is_bgs guard). Instead they receive a grid of 13 precisely positioned blasts that march down the length of the hull over 0.8 seconds, covering nose to engines.
Blast Grid
Each blast is defined as [local_x_frac, local_y_frac, delay_sec, size_px]. The fractions are multiplied by the ship’s on-screen half-width and half-height, then rotated by global_rotation to match the ship’s actual heading:
# [local_x_frac, local_y_frac, delay_sec, explosion_size_px]
[ 0.0, -0.85, 0.00, 100.0], # nose tip
[-0.3, -0.60, 0.08, 80.0],
[ 0.3, -0.50, 0.13, 85.0],
[ 0.0, -0.30, 0.19, 110.0],
[-0.4, -0.06, 0.26, 80.0],
[ 0.4, 0.04, 0.32, 85.0],
[ 0.0, 0.16, 0.37, 130.0], # centre — biggest
[-0.3, 0.36, 0.45, 80.0],
[ 0.3, 0.44, 0.51, 85.0],
[ 0.0, 0.56, 0.57, 100.0],
[-0.2, 0.72, 0.64, 80.0],
[ 0.2, 0.80, 0.70, 85.0],
[ 0.0, 0.0, 0.80, 150.0], # final massive central flare
Each delayed blast uses the same Timer + one-shot lambda pattern established by the hit effect. The final blast at 0.80 s is a 150 px flare centred on the ship to cap the sequence.
Branching Logic
if not _is_bgs and ship_tex != null:
# ... debris pieces ...
if not _is_bgs and ship_tex != null:
# ... seam explosions along cut lines ...
if _is_bgs and ship_tex != null:
# ... 13-blast capital ship grid ...
else:
# ... 6-blast fighter chain ...
Files Changed
| File | Change | Status |
|---|---|---|
| scenes/entities/Player.tscn | Removed CollisionScaler Node2D; re-parented all 18 CollisionPolygon2D nodes directly under CharacterBody2D | Done |
| scripts/entities/Player.gd | ShieldPreview hidden at runtime; take_damage() accepts impact_pos; _spawn_hit_effect(); _spawn_death_effect() with fighter debris + seam explosions + BGS blast grid | Done |
| scripts/entities/EnemyUnit.gd | Single texture replaced with 9-variant random colour array; display size scaling added; take_damage() call passes impact position | Done |
| scripts/entities/EnemyBullet.gd | take_damage() call updated to pass bullet global_position as impact point | Done |
| scripts/entities/EnemyMothership.gd | take_damage() call updated to pass mothership global_position as impact point | Done |
Late Session: Game Over Screen Navigation & Enemy Size
Game Over: Main Menu & Ships Buttons
New Two navigation buttons added to the game over panel matching the exact visual style used elsewhere in the game (transparent StyleBoxEmpty background, icon on top, Neuropol text below):
- Main Menu uses
game_logo_small.pngicon, same asset as the main menu button in the pause menu. Navigates toMainMenu.tscn. - Ships uses
Shop Button.pngicon, same asset as the Ships button on the main menu. Navigates toCharacterShop.tscn.
Both buttons unset get_tree().paused before navigating so the scene transition is clean. Wired in HUD.gd as _on_go_main_menu_pressed() and _on_go_ships_pressed().
Final positions set manually in the Godot editor after initial coordinate calculations placed them partially off-screen (the GameOverPanel extends to x=2090 on a 1920px screen, so only local x ≤ 1280 is visible). Editor-confirmed positions: top-right and bottom-right of the visible panel area.
Enemy Ship Size Increase
Change Both enemy types were slightly too small in-game. DISPLAY_SIZE constants bumped:
| Entity | Before | After |
|---|---|---|
| EnemyUnit.gd | 55 px | 75 px |
| EnemyMothership.gd | 150 px | 200 px |
Sound Effects Audit
Full audit of audio assets on disk vs. events in code. Only one sound (thruster loop) is currently wired. All others are missing.
Assets on Disk: res://Sounds/
| File | Likely Purpose | Wired? |
|---|---|---|
| Main Menu.mp3 | Main menu background music | Not wired |
| Spaceshuttle_Rocket_Steady_LV.mp3 | Thruster loop | Wired ✓ |
| Rocket Fire.mp3 | Player shooting / rocket fire | Not wired |
| Asteroid Hit.mp3 | Asteroid takes damage | Not wired |
| Nuke Exp.mp3 | Nuke explosion | Not wired |
| science_fiction_spaceship_or_rocket_fly_by_fast_001.mp3 | Unknown / fly-by | Not wired |
| PTModelSound_ID116707.mp3 | Unknown | Not wired |
| PTModelSound_ID193943.mp3 | Unknown | Not wired |
| PTModelSound_ID195419.mp3 | Unknown | Not wired |
| PTModelSound_ID195429.mp3 | Unknown | Not wired |
| PTModelSound_ID26529.mp3 | Unknown | Not wired |
| PTModelSound_ID70321.mp3 | Unknown | Not wired |
Missing Sound Events: Priority Order
| Sound Needed | Triggered By | Script / Line |
|---|---|---|
| Player shoot | Player fires bullet (X1/X2/X3 and BGS variants) | Player.gd · _handle_fire() |
| Enemy fire | EnemyUnit or Mothership fires a bullet | EnemyUnit.gd / EnemyMothership.gd · _shoot_at() |
| Asteroid hit | Asteroid takes a hit, may split | Asteroid.gd · hit() |
| Asteroid explosion | Asteroid fully destroyed | Asteroid.gd · _destroy() |
| Enemy explosion | EnemyUnit or Mothership destroyed | EnemyUnit.gd / EnemyMothership.gd · hit() |
| Player hit / damage | Player takes damage from enemy/asteroid | Player.gd · take_damage() |
| Player death | Player health reaches 0 | Player.gd · _die() |
| Crystal collect | Player collects a crystal | Crystal.gd · marked SOUND NEEDED |
| Power-up collect | Health / shield / nuke pickup | PowerUp.gd · _apply() |
| Nuke explosion | Nuke detonates, destroys asteroids in radius | NukeExplosion.gd |
| Ship level-up | Ship crosses X1→X2 or X2→X3 threshold | GameManager.gd / Player.gd · marked SOUND NEEDED |
| Rank promotion fanfare | Player promoted to new rank | HUD.gd · marked SOUND NEEDED |
| Shield activate | Shield power-up collected and activated | Player.gd · activate_shield() |
| Ship purchase success | Successful ship buy in CharacterShop | CharacterShop.gd · marked SOUND NEEDED |
| Insufficient funds | Failed ship purchase attempt | CharacterShop.gd · marked SOUND NEEDED |
| Main menu music | Main menu scene loads | MainMenu.gd · asset exists, not wired |
| Button click (UI) | Any menu button press | All menu scripts |
PTModelSound_ID*.mp3 files need to be auditioned to assign them to events before wiring begins. Open Items
| Item | Notes | Status |
|---|---|---|
| Shield scale per ship | Tuning only; Shield Scale inspector group already wired | Tuning |
| Collision polygons | 18 polygons need geometry authored to match each ship hull | Pending |
| Thruster / gun barrel positions | Per-ship offset tuning in editor | Pending |
| BGS rocket mechanic / Viper level-up benefit | Gameplay feature, not yet designed | Pending |
| Mobile joystick wiring | Input layer not yet implemented | Pending |
| Sound effects: 17 events unwired | Audit complete. 6 PTModelSound files need auditioning before assignment. Main menu music asset exists unwired. | Pending |
| Enemy movement to _physics_process | Currently in _process; should move for consistent collision | Pending |
| Coin shop / IAP / AdMob / GDPR / leaderboards | Monetisation layer | Pending |
| DEBUG_SHIP_SCALE = false | Must be set before shipping | Before ship |
| Remove DEBUG_AddCoins button | Must be removed before shipping | Before ship |