Asteroids Destroyer Dev Log – 15 May 2026

Asteroids Destroyer โ€” Development Journal

Dev Log โ€” 15 May 2026

๐Ÿ“… Session 8
๐ŸŽฎ Godot 4.6
๐Ÿ› Playtesting Fixes
๐Ÿ”ง BGS Ship Overhaul
โš–๏ธ Drop Rate Tuning
๐Ÿš€ Thruster Visual Editing
๐Ÿš€

Session Overview

This session was driven entirely by playtesting. Two critical runtime bugs were discovered in the ship system: ships were rendering at the wrong size and the first ship was always visible regardless of which ship was selected. These were diagnosed and fixed correctly. The session also overhauled the BGS-1 and BGS-2 ships with a unique thruster and gun mechanic, tuned crystal/power-up drop rates, and added a debug coin button to the Character Shop.

  • Fixed: Double-scaling bug: ships rendering at wrong size at runtime
  • Fixed: Placeholder sprite always visible at game start
  • Improved: Editor collision polygon workflow: active polygon now highlighted
  • New: BGS-1 and BGS-2 unique thruster animation and side-firing gun mechanic
  • Tuned: Coin drop rates reduced, power-up drop rates increased
  • Added: Debug +1000 coins button in Character Shop
  • Fixed: Viper thruster: now uses BGS-style 10-frame animation, single centre exhaust
  • Rearchitected: All 18 ship scenes restructured so thruster position and scale can be set visually in the Godot editor without any code overrides
๐Ÿ›

Ship Double-Scaling and an Always-Visible Placeholder Sprite

Bug 1: Ships at Wrong Size (Double-Scaling)

Ships were appearing at the wrong size at runtime. The SHIP_DISPLAY_SIZE dynamic scaling calculation appeared correct but the rendered result did not match the target 80 px.

Root cause:_ship_body was being added as a child of ShipBody (the placeholder Node2D), which still had an editor-saved scale baked into the .tscn file from previous tuning sessions (e.g. scale = Vector2(0.131, 0.131) for CP2). The dynamic scaling code then set a scale on _ship_body relative to that already-scaled parent, multiplying the two values together and producing a ship far smaller than intended.

Fix:_ship_body is now added directly to self (the CharacterBody2D) instead of inside ShipBody. Scale is set on _ship_body in world space, independent of any editor-saved parent scale. The dynamic calculation now produces the exact target size every time.

# Before (wrong โ€” inherits ShipBody's editor-saved scale):
placeholder.add_child(_ship_body)

# After (correct โ€” added directly to CharacterBody2D):
add_child(_ship_body)

Bug 2: Placeholder Sprite Always Visible

The first ship sprite (whichever was last previewed in the editor) was always visible at game start, overlaid on top of the actual equipped ship’s sprite. This was the ShipBody/Sprite2D placeholder node with a baked texture from the last @tool editor preview session.

Fix: In _ready(), ShipBody.visible = false is called immediately (when Engine.is_editor_hint() is false). The placeholder is editor-only; it must be invisible at runtime. The actual ship body is instantiated separately and added to self.

func _ready() -> void:
    if Engine.is_editor_hint():
        return
    # Hide the editor placeholder โ€” it serves no runtime purpose
    var sb: Node2D = $ShipBody
    if sb:
        sb.visible = false
    _gm = get_node("/root/GameManager")
    ...
    _apply_ship_setup()
Rule going forward:ShipBody is an editor-only node. It is always hidden at runtime. The real ship body (_ship_body) is a dynamically instantiated scene added directly to the CharacterBody2D root, so it inherits no editor-saved scale from ShipBody.
โœ๏ธ

Editor Improvement: Active Collision Polygon Highlighted

When editing collision polygons in Player.tscn, it was unclear which of the 18 Col_* nodes corresponded to the ship currently being previewed via the editor sliders. All 18 nodes were visible simultaneously, making it hard to select and trace the right one.

Option C: Active Polygon Made Visible

The _editor_update_preview() function now iterates all Col_* nodes and hides them all, then makes only the active one visible. The visible property on CollisionPolygon2D has no effect on physics; it only controls whether the gizmo outline is drawn in the editor viewport. This means the correct polygon is always highlighted in orange when the preview sliders are set.

func _editor_update_preview() -> void:
    # ... load sprite texture + apply scale ...

    # Show only the active collision polygon in the editor
    for child in get_children():
        if child is CollisionPolygon2D:
            child.visible = false
    var active_col_name: String = COLLISION_NODE_NAMES[series][level]
    var col: CollisionPolygon2D = get_node_or_null(active_col_name)
    if col:
        col.visible = true
        editor_active_collision = active_col_name
    else:
        editor_active_collision = "(none)"

An @export var editor_active_collision: String = "(none)" is also shown in the Inspector so you can confirm at a glance which node is active before editing it.

๐Ÿ›ธ

BGS-1 and BGS-2: Unique Thruster System

Neither BGS ship has a centre thruster. Both use a dedicated 10-frame thruster animation loaded from Game Assests/Thrusters/BGS Thrusters/. The standard thruster loading path in _apply_ship_setup() was extended to detect BGS ships and load these frames instead of the standard set.

const BGS_THRUSTER_PATHS: Array[String] = [
    "res://Game Assests/Thrusters/BGS Thrusters/thruster_1.png",
    "res://Game Assests/Thrusters/BGS Thrusters/thruster_2.png",
    # ... 10 frames total
]

When a BGS ship is detected: _use_center = false, _use_sides = true. BGS thruster frames are loaded into _thruster_frames. The ThrusterLeft and ThrusterRight nodes both have visible = false set in their .tscn files and are controlled at runtime by the existing thruster animation loop.

๐Ÿ”ซ

BGS-1 and BGS-2: Gun Barrel Layout and Side-Firing Mechanic

15 Gun Barrel Marker2D Nodes

The BGS ships have a unique gun layout: 3 front barrels and 6 per side (12 side barrels total). All 15 are placed as Marker2D nodes in the ship .tscn files so positions can be tuned visually in the editor without touching code.

  • GunBarrelFront1/2/3: Front-facing, fire forward (same direction as ship heading)
  • GunBarrelSideLeft1โ€“6: Left hull edge, fire perpendicular (left of heading)
  • GunBarrelSideRight1โ€“6: Right hull edge, fire perpendicular (right of heading)

Level-Gated Side Firing

The upgrade mechanic for BGS ships is the side guns, not speed or size. Front guns always fire. Side guns unlock progressively by level:

Level Front Guns Side Guns (per side) Total Barrels
X1 (Level 0) 3 0 3
X2 (Level 1) 3 3 each side 9
X3 (Level 2) 3 6 each side 15
func _handle_fire_bgs(level: int) -> void:
    for barrel in _bgs_gun_front:
        bullet_fired.emit(barrel.global_position, rotation)
    if level < 1:
        return
    var side_count: int = 3 if level == 1 else 6
    var rot_left:  float = rotation - (PI * 0.5)
    var rot_right: float = rotation + (PI * 0.5)
    for i in side_count:
        if i < _bgs_gun_side_left.size():
            bullet_fired.emit(_bgs_gun_side_left[i].global_position,  rot_left)
        if i < _bgs_gun_side_right.size():
            bullet_fired.emit(_bgs_gun_side_right[i].global_position, rot_right)

Side guns fire at exactly 90ยฐ to the ship heading (rotation ยฑ PI/2). The _is_bgs: bool flag is set in _apply_ship_setup() and routes _handle_fire() to the BGS-specific handler when true.

Player.gd BGS References

Three new BGS-specific arrays are populated in _apply_ship_setup() when a BGS ship is loaded:

var _bgs_gun_front:      Array[Marker2D] = []
var _bgs_gun_side_left:  Array[Marker2D] = []
var _bgs_gun_side_right: Array[Marker2D] = []
var _is_bgs: bool = false

These are cleared in _clear_ship_refs() each time a new ship loads, preventing stale references when switching ships.

๐Ÿ’Ž

Drop Rate Tuning: Fewer Coins, More Power-ups

Playtesting showed coin drops from asteroids were too frequent (screen clutter, economy unbalanced) and power-up drops were too rare (players rarely received buffs). Both adjusted in GameScene.gd.

Item Change Old Value New Value
Coin drop: LARGE asteroid CHANGE 100% chance 70% chance
Coin drop: MEDIUM asteroid (2nd coin) CHANGE 40% chance 20% chance
Coin drop: SMALL asteroid CHANGE 15% chance 8% chance
Power-up: LARGE asteroid spawn chance CHANGE 25% 35%
Power-up: MEDIUM asteroid spawn chance CHANGE 12% 20%

Files changed: scripts/game/GameScene.gd ยท _spawn_crystals() and _try_spawn_powerup().

๐Ÿ”ง

Debug +1000 Coins Button in the Character Shop

A debug button was added to the Character Shop to allow rapid coin testing without playing through levels. Positioned on the right-hand side of the screen, mid-height, outside the main panel area. Yellow text to make it visually distinct as a debug element. Remove before shipping.

  • Scene:DEBUG_AddCoins Button node added to UI CanvasLayer in CharacterShop.tscn
  • Position: offset_left=1545, offset_top=510, offset_right=1905, offset_bottom=580
  • Style: Yellow font (Color(1, 0.9, 0, 1)), font_size=22, text="DEBUG +1000 Coins"
  • Wiring:_gm.add_coins(1000), which uses the canonical GameManager method, triggers coins_changed signal and updates the live coin display
๐Ÿ›

Viper Using Wrong Thruster Frames

The Viper (series 5) was loading the standard 7-frame THRUSTER_PATHS animation set, which includes a small diamond-style exhaust graphic that looks wrong on the Viper hull. The Viper should use a single centre exhaust using the BGS-style 10-frame animation, scaled down proportionally.

Root Cause

The Viper is a standalone ship (no X1/X2/X3 levels) and falls into the else branch of _apply_ship_setup(), the same branch as all standard multi-level ships. That branch unconditionally loaded THRUSTER_PATHS. There was no Viper-specific check.

Fix

Added a series == 5 check inside the standard else-branch that overrides the thruster layout and frame source for the Viper specifically:

# Viper (series 5) โ€” single centre exhaust, BGS-style thruster frames scaled down
if series == 5:
    _use_center = true
    _use_sides  = false
# Load thruster frames โ€” Viper shares BGS animation frames (scaled down)
_thruster_textures.clear()
if series == 5:
    for p in BGS_THRUSTER_PATHS:
        _thruster_textures.append(load(p) as Texture2D)
else:
    for p in THRUSTER_PATHS:
        _thruster_textures.append(load(p) as Texture2D)

The Viper now plays the same 10-frame BGS exhaust animation on its centre thruster. The visual size is controlled by the scale value set on ThrusterCenter in Ship_Viper.tscn, starting at (3.6, 3.6), which can be tuned visually in the editor.

๐Ÿš€

Thruster Visual Editing via Scene Restructure

Problem

Thrusters on all ships were sized and positioned entirely in code, via a block of ratio constants (TC_Y_R, TS_X_R, etc.) computed world-space values and forced them onto the nodes every time a ship loaded. Any values placed on thruster nodes in the .tscn editor were silently overwritten at runtime. Visual editing was impossible.

The deeper issue was structural: thruster Sprite2D nodes were direct children of the ship scene root. The ship root itself got scaled at runtime (e.g. BGS scale = 0.151), so the thrusters inherited that scale, and world-space size collapsed to near-zero on large-texture ships. The code override was a workaround for this inheritance.

The Body Sub-Node

All 18 ship .tscn files were restructured to introduce a Body Node2D child. The ship sprite and all gun barrel Marker2D nodes moved under Body. The thruster nodes stayed at the scene root.

Before After
Ship_XXX (Node2D root) โ† scaled at runtime
  โ”œโ”€ Sprite2D            โ† correct โœ“
  โ”œโ”€ GunBarrel           โ† correct โœ“
  โ”œโ”€ ThrusterCenter      โ† inherits scale โœ—
  โ”œโ”€ ThrusterLeft        โ† inherits scale โœ—
  โ””โ”€ ThrusterRight       โ† inherits scale โœ—
Ship_XXX (Node2D root) โ† NOT scaled
  โ”œโ”€ Body (Node2D)       โ† only this scaled
  โ”‚    โ”œโ”€ Sprite2D       โ† correct โœ“
  โ”‚    โ””โ”€ GunBarrel      โ† correct โœ“
  โ”œโ”€ ThrusterCenter      โ† world-space โœ“
  โ”œโ”€ ThrusterLeft        โ† world-space โœ“
  โ””โ”€ ThrusterRight       โ† world-space โœ“

At runtime, _apply_ship_setup() now scales Body only. Thrusters remain at their scene-defined positions and scales, which are now direct world-space values: what you set in the editor is exactly what appears in-game.

Player.gd Changes

  • Sprite lookup changed from "Sprite2D" โ†’ "Body/Sprite2D"
  • Standard gun barrel lookups: "GunBarrel" โ†’ "Body/GunBarrel", "Body/GunBarrelLeft", "Body/GunBarrelRight"
  • BGS gun barrel lookups: "GunBarrelFront%d" โ†’ "Body/GunBarrelFront%d", etc.
  • Scale applied to body_art (the Body sub-node) instead of _ship_body
  • Entire thruster position/scale override block removed, with no more ratio constants in code
# Before โ€” scales entire scene root (thrusters inherit it):
_ship_body.scale = Vector2(s, s)

# After โ€” scales only the art sub-node:
var body_art: Node2D = _ship_body.get_node_or_null("Body") as Node2D
if body_art and longest > 0.0:
    body_art.scale = Vector2(disp / longest, disp / longest)

Starting Thruster Values

All thruster nodes were given pre-computed world-space starting positions so they appear in approximately the right place immediately. These are derived from the same proportional ratios that were previously computed in code, now baked into the .tscn as editable values.

Ship Group Display Size ThrusterCenter ThrusterLeft / Right
CX16, DKO, WO84, CP2, PTM (all variants) 80 px (0, 36) scale (2.2, 2.2) (ยฑ24, 32) scale (1.8, 1.8)
Viper 130 px (0, 59) scale (3.6, 3.6) (ยฑ39, 52), unused, no sides
BGS-1, BGS-2 160 px n/a (no centre) (ยฑ48, 64) scale (3.5, 3.5)

BGS Thruster Visibility Bug Fixed

Both BGS ship .tscn files had visible = false set on ThrusterLeft and ThrusterRight. Since _update_thrust_effects() controls thruster visibility at runtime based on is_thrusting and _use_sides, the static visible = false in the scene permanently suppressed the BGS thrusters even when thrusting. These flags have been removed; the code handles all visibility toggling.

How to Tune Thruster Positions Going Forward

  1. Open a ship scene (e.g. scenes/entities/ships/Ship_CX16_X1.tscn)
  2. Select ThrusterCenter, ThrusterLeft, or ThrusterRight in the scene tree
  3. Drag to reposition or use the Inspector to set scale; both update the .tscn
  4. Save the scene. No code changes needed, changes are live immediately
๐Ÿ“‹

Files Changed on 15 May 2026

File Change
scripts/entities/Player.gd FIX _ship_body added to self (not ShipBody), eliminating double-scaling. FIX ShipBody.visible=false in _ready(). CHANGE BGS_THRUSTER_PATHS const; _bgs_gun_* arrays; _is_bgs flag; _apply_ship_setup() BGS branch; _handle_fire() routes to _handle_fire_bgs(); _clear_ship_refs() clears BGS arrays. CHANGE _editor_update_preview() hides all Col_* then shows active one. NEW SHIP_DISPLAY_SIZES 2D array; editor_active_collision export.
scenes/entities/Player.tscn FIX Removed any editor-saved scale from ShipBody node; scale is now set exclusively in code.
scenes/entities/ships/Ship_BGS1.tscn CHANGE Removed ThrusterCenter; added 15 gun barrel Marker2D nodes (GunBarrelFront1โ€“3, GunBarrelSideLeft1โ€“6, GunBarrelSideRight1โ€“6); all thrusters visible=false.
scenes/entities/ships/Ship_BGS2.tscn CHANGE Same structure as BGS1; gun barrel positions tuned independently for BGS-2 hull shape.
scripts/game/GameScene.gd CHANGE _spawn_crystals(): LARGE 100%โ†’70%, MEDIUM 2nd coin 40%โ†’20%, SMALL 15%โ†’8%. _try_spawn_powerup(): LARGE 25%โ†’35%, MEDIUM 12%โ†’20%.
scenes/menus/CharacterShop.tscn NEW DEBUG_AddCoins Button node under UI CanvasLayer; yellow text; positioned right-side mid-height.
scripts/menus/CharacterShop.gd NEW @onready var _debug_add_coins; connected in _ready() to add_coins(1000).
scripts/entities/Player.gd FIX Viper (series 5) now loads BGS_THRUSTER_PATHS with _use_center=true, _use_sides=false. CHANGE Sprite lookup โ†’ "Body/Sprite2D". Scale now applied to Body sub-node only. Gun barrel paths prefixed "Body/". FIX Entire thruster position/scale override block removed, with no more ratio constants in code.
scenes/entities/ships/Ship_CX16_X1.tscn
Ship_CX16_X2.tscn
Ship_CX16_X3.tscn
Ship_DKO_X1โ€“X3.tscn
Ship_WO84_X1โ€“X3.tscn
Ship_CP2_X1โ€“X3.tscn
Ship_PTM_X1โ€“X3.tscn
Ship_Viper.tscn
Ship_BGS1.tscn
Ship_BGS2.tscn
CHANGE All 18 scenes restructured: Body Node2D sub-node added; Sprite2D and GunBarrel* nodes moved under Body (parent="Body"); ThrusterCenter/Left/Right stay at scene root with pre-computed world-space positions and scales. FIX BGS1 and BGS2: removed visible=false from thruster nodes.
๐Ÿ“‹

Open Items

Item Priority Notes
Remove DEBUG_AddCoins button before shipping Pre-ship In CharacterShop.tscn and CharacterShop.gd
Add all 12 sound effects High SOUND NEEDED comments mark every trigger point in the codebase
Tune player ship collision polygons (ร—18) High Use Editor Preview sliders in Player.tscn; the active polygon is now highlighted
Tune thruster positions per ship variant High Open each ship .tscn, select ThrusterCenter/Left/Right and drag. Fully visual now, no code changes needed. Starting positions pre-set.
Tune gun barrel positions per ship variant High Open each ship .tscn, drag GunBarrel Marker2D nodes under Body. BGS ships have 15 barrels each, tuned by level.
Tune asteroid collision polygons (ร—12) Medium A1 traced. A3โ€“A13 use default octagon.
Enemy movement โ†’ _physics_process Medium EnemyUnit + EnemyMothership position logic is in _process
Throttle asteroid dodge loop Medium EnemyUnit._get_asteroid_dodge() scans all asteroids every frame
Wire real achievement data Medium 12 slots are placeholder (unlocked = false)
Standalone ship level-up (BGS rocket mechanic) Medium BGS side guns now level-gated. Viper and additional standalone ships still need a level-up benefit.
Level Complete visual polish Medium SHIP_DISPLAY_CONFIG offsets/scales need tuning per ship
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