Dev Log โ 5 May 2026
Day Overview: Four Sessions
A day of deep technical work across four sessions. The day started with adding mothership colour variants and performance optimisation, then pivoted to a major ship architecture overhaul that spanned three iterations and three sessions, ending with the correct dynamic per-variant scaling solution that accounts for the 8:1 range in ship texture sizes.
| Session | Focus | Files |
|---|---|---|
| Session 4 | 15 mothership colour variants with dynamic scaling; 5 performance optimisations | 5 files |
| Session 5 | Ship architecture overhaul: 18 per-variant ship scenes, @tool editor preview, 2D scene path array | 22 files |
| Session 6 | Bug fix: ships 2ร too large (missing ShipBody scale); bug fix: thrusters always visible | 4 files |
| Session 7 | Dynamic per-ship scaling: replaced fixed 0.5 scale with texture-driven calculation per variant | 2 files |
Mothership Colour Variants: 15 Variants, Random Spawn
14 new mothership sprites were added alongside the original, giving 15 total colour variants. Each time a mothership spawns it picks one at random, so no two waves look the same. The three source sizes (230ร230, 370ร385, 1024ร1024) would render at wildly different sizes with a fixed scale, so the same dynamic scaling pattern used across the codebase was applied.
const DISPLAY_SIZE: float = 150.0 # target longest-side size in px
var tex: Texture2D = load(TEXTURE_PATHS[randi() % TEXTURE_PATHS.size()])
if tex:
_sprite.texture = tex
var longest: float = maxf(tex.get_width(), tex.get_height())
var s: float = DISPLAY_SIZE / longest if longest > 0.0 else 1.0
_sprite.scale = Vector2(s, s)
To adjust mothership size: change DISPLAY_SIZE in EnemyMothership.gd. To add variants: append the path to TEXTURE_PATHS.
Performance Optimisation: 5 Key Fixes
A full audit of the gameplay-hot scripts was run. Five issues identified and fixed, ranked by impact:
1. Cache GameManager Reference in Player.gd
_physics_process called get_node_or_null("/root/GameManager") on every frame (60+ times/sec). A _gm: Node instance var is now populated once in _ready() and reused everywhere. All four separate get_node* calls replaced.
2. Shared Texture Cache in EnemyBullet.gd
Every bullet called load(path) for all 6 animation frames in _ready(). Two static var arrays (_small_tex_cache and _big_tex_cache) are now shared across all instances, loaded only once on the first instantiation.
3. Preloaded Font in GameScene.gd
Both _show_wave_warning() and _show_sector_banner() called load(neuropol.ttf) each time they ran. Replaced with a class-level const _NEUROPOL: FontFile = preload(...), at zero runtime cost.
4. Cached Edge Spawn Points in GameScene.gd
_fixed_edge_point() allocated a typed Array[Vector2] with 8 entries on every call (36+ per wave). The 8 spawn points are now computed once in _ready() and stored in _edge_points. The function is now a single-line array index.
5. Crystal Double-Collect Guard
A _collected: bool = false flag was added. If two physics frames fire collision callbacks simultaneously (possible at high frame-rate peaks), the second call returns immediately before touching the GameManager. Signal disconnect remains as secondary safety.
Remaining Deferred Audit Items
| Issue | File | Suggested Fix |
|---|---|---|
| Asteroid dodge loop every frame | EnemyUnit.gd ยท _get_asteroid_dodge() |
Throttle to once per 0.15s using a timer, or use Area2D overlap detection |
Enemy movement in _process |
EnemyUnit.gd, EnemyMothership.gd | Move position logic to _physics_process for fixed-timestep accuracy |
Ship Architecture Overhaul: 18 Per-Variant Scenes + @tool Preview
The root problem: it was impossible to visually edit player ship collision shapes and gun barrel positions in the Godot editor because ship sprites load at runtime from GameManager, so the editor showed a blank Sprite2D. The session went through three iterations to arrive at a clean, warning-free solution.
Node2D-rooted ship scenes. Final Architecture: Two-Layer Split
Layer 1: 18 Ship Scenes (pure visuals, no warnings): Located in scenes/entities/ships/. Each is a Node2D with: Sprite2D (texture baked in), ThrusterCenter/Left/Right (visible=false), GunBarrel/GunBarrelLeft/GunBarrelRight Marker2Ds.
| Series | Scenes |
|---|---|
| CX16, DKO, WO84, CP2, PTM | X1, X2, X3 (3 scenes each = 15 total) |
| Viper, BGS-1, BGS-2 | Standalone (1 scene each = 3 total) |
Layer 2: Player.tscn (physics shell): Contains only ThrustAudio, ShipBody placeholder Node2D (ship scene instanced at runtime), and 18 Col_* CollisionPolygon2D nodes as direct CharacterBody2D children.
Player.gd: Key Changes
- @tool editor preview:
@export_range(-1,7) var editor_ship_seriesand@export_range(0,2) var editor_ship_level. Changing either slider shows the correct ship sprite in the editor without affecting runtime. - SHIP_SCENE_PATHS: Changed from flat
Array[String]to 2D array indexed[series][level]. - _apply_ship_setup(): Loads
SHIP_SCENE_PATHS[series][level]directly. Oldget_ship_texture()call removed; the correct sprite is baked into each variant scene. - Ship node refs: Changed from
@onreadyto plain vars populated after instantiation, with null guards throughout.
Editor Workflows
Gun barrels and thrusters: Open the relevant ship scene (e.g. Ship_CX16_X2.tscn). Sprite is already visible. Drag marker nodes to the correct positions. Save.
Collision polygons: Open Player.tscn. Set editor_ship_series and editor_ship_level in Inspector and the sprite appears live. Select the matching Col_* node and drag vertices. Set editor_ship_series back to -1 when done.
Ship Scaling: Two Bugs Found and Fixed
Bug 1 (Session 6): Ships Rendering 2ร Too Large
The original Player.tscn had scale = Vector2(0.5, 0.5) on its Sprite2D. New ship scenes defaulted to Vector2(1,1), so ships appeared at twice the intended size in-game.
Session 6 Fix: Applied scale = Vector2(0.5, 0.5) once to the ShipBodyNode2D in Player.tscn, which fixes all 18 scenes simultaneously. The @tool preview sprite also inherits the scale so collision polygon editing shows the ship at the correct in-game size.
Bug 2 (Session 6): Thrusters Always Visible
Some ship scenes had thruster nodes temporarily made visible during editor positioning then saved without re-hiding. Godot 4 never writes visible = true (it’s the default), so visible = false was missing from those files.
Fixed by adding visible = false to: Ship_CX16_X1.tscn (ThrusterCenter), Ship_CX16_X2.tscn (ThrusterLeft, ThrusterRight), Ship_WO84_X3.tscn (all three thrusters).
Bug 3 (Session 7): Fixed Scale Fails for Large-Texture Ships
The Session 6 fix (scale = 0.5 on ShipBody) works for CX16, DKO, and WO84 (all 160ร160 px textures), but CP2 is 609px, PTM X2 is 1178px, Viper is 957px, BGS ships are 1057px, so at 0.5 scale these would render 4โ7ร too large.
| Ship | Longest Axis | Old Scale (0.5) | Rendered Size | New Scale |
|---|---|---|---|---|
| CX16 / DKO | 160 px | 0.500 | 80 px โ | 0.500 |
| CP2 | 609 px | 0.500 | 305 px โ | 0.131 |
| PTM X2 | 1178 px | 0.500 | 589 px โ | 0.068 |
| Viper | 957 px | 0.500 | 479 px โ | 0.084 |
| BGS-1 / BGS-2 | 1057 px | 0.500 | 529 px โ | 0.076 |
Dynamic Scale from Texture Dimensions
The fixed scale was replaced with a runtime calculation: each ship is scaled so its longest axis always renders at exactly 80 px on screen, regardless of original texture dimensions. Same pattern used in Crystal.gd and EnemyMothership.gd.
const SHIP_DISPLAY_SIZE: float = 80.0
# Inside _apply_ship_setup(), after caching sprite ref:
if sprite and sprite.texture:
var longest: float = maxf(float(sprite.texture.get_width()),
float(sprite.texture.get_height()))
if longest > 0.0:
var s: float = SHIP_DISPLAY_SIZE / longest
_ship_body.scale = Vector2(s, s)
The same calculation is applied in _editor_update_preview() so the @tool viewport preview also scales correctly, so collision polygon vertices sit on the ship at the exact size they will appear in-game.
Ship Tuning Status After All Sessions
| Ship Scene | Gun Barrels | Thrusters | Collision Polygon |
|---|---|---|---|
| Ship_CX16_X1.tscn | Done | Done | Done |
| Ship_CX16_X2.tscn | Done | Done | Done |
| Ship_CX16_X3.tscn | Pending | Pending | Done |
| Ship_WO84_X3.tscn | Done | Done | Pending |
| Ship_DKO / WO84 X1–X2 / CP2 / PTM | Pending | Pending | Pending |
| Ship_Viper / BGS1 / BGS2 | Pending | Pending | Pending |
All Files Changed on 5 May 2026
| File | Change | Session |
|---|---|---|
| scripts/entities/EnemyMothership.gd | CHANGE TEXTURE_PATH โ TEXTURE_PATHS (15 entries); DISPLAY_SIZE constant; dynamic scale from texture dimensions | S4 |
| scripts/entities/Player.gd | OPT _gm cached; 4ร get_node* removed; CHANGE @tool; editor preview exports; SHIP_SCENE_PATHS 2D array; _apply_ship_setup() loads exact variant; SHIP_DISPLAY_SIZE const; dynamic scale block; editor preview scaling | S4โS7 |
| scripts/entities/EnemyBullet.gd | OPT Static _small_tex_cache / _big_tex_cache; textures loaded once per class | S4 |
| scripts/game/GameScene.gd | OPT _NEUROPOL preloaded; _edge_points cached; _fixed_edge_point() simplified; FIX revive_player() โ player.sprite.visible instead of get_node(“Sprite2D”) | S4โS5 |
| scripts/entities/Crystal.gd | OPT _collected bool guard prevents double-award | S4 |
| scenes/entities/Player.tscn | CHANGE Stripped to physics shell; 18 Col_* collision nodes; ShipBody placeholder; removed fixed scale | S5โS7 |
| scenes/entities/ships/Ship_CX16_X1/X2/X3.tscn | NEW CX16: 3 variant scenes (X1 thruster fix applied) | S5โS6 |
| scenes/entities/ships/Ship_DKO_X1/X2/X3.tscn | NEW DKO: 3 variant scenes | S5 |
| scenes/entities/ships/Ship_WO84_X1/X2/X3.tscn | NEW WO84: 3 variant scenes (X3 thruster fix applied) | S5โS6 |
| scenes/entities/ships/Ship_CP2_X1/X2/X3.tscn | NEW CP2: 3 variant scenes | S5 |
| scenes/entities/ships/Ship_PTM_X1/X2/X3.tscn | NEW PTM: 3 variant scenes | S5 |
| scenes/entities/ships/Ship_Viper.tscn | NEW Viper standalone | S5 |
| scenes/entities/ships/Ship_BGS1.tscn | NEW BGS-1 standalone | S5 |
| scenes/entities/ships/Ship_BGS2.tscn | NEW BGS-2 standalone | S5 |
| Old 8 shared-series ship scenes | REMOVED Deleted; replaced by 18 per-variant scenes | S5 |
Open Items
| Item | Priority | Notes |
|---|---|---|
| Add all 12 sound effects | High | SOUND NEEDED comments mark every trigger point in the codebase |
| Tune player ship collision polygons (ร18) | High | Open Player.tscn, use Editor Preview sliders, edit Col_* vertices |
| Tune GunBarrelLeft / GunBarrelRight per variant | High | Open each ship scene, drag markers to match wing gun art |
| Enemy movement โ _physics_process | Medium | EnemyUnit + EnemyMothership position logic is in _process (frame-rate dependent) |
| Throttle asteroid dodge loop | Medium | EnemyUnit._get_asteroid_dodge() scans all asteroids every frame; add a 0.15s throttle or Area2D detection |
| Wire real achievement data | Medium | 12 slots are placeholder (unlocked = false). Connect to GameManager progress. |
| Standalone ship level-up (rockets) | Medium | Viper, BGS-1, BGS-2 gain no level-up benefit |
| Level Complete visual polish | Medium | SHIP_DISPLAY_CONFIG offsets/scales need tuning per ship |
| Tune asteroid collision polygons (ร12) | Medium | A1 traced. A3โA13 use default octagon. |
| Mobile joystick wiring | Medium | MobileControls visible=false; input not connected |
| Audio buses | Medium | Create Master โ Music and SFX buses once sounds are added |
| Wave warning text | Low | Still says “Asteroids Incoming” on enemy-only waves |
| Planet positions BG_02โBG_13 | Low | Editor adjustment needed per background scene |
| Coin shop / IAP | Low | Not started |
| AdMob, Vungle, AppLovin | Low | SDK integration pending |
| GDPR consent | Low | Required before store submission |
| Google Play Games leaderboards | Low | Not started |
| Android / iOS export signing | Low | Export templates not configured |