Asteroids Destroyer Dev Log – 16 May 2026

Asteroids Destroyer โ€” Development Journal

Dev Log โ€” 16 May 2026

๐Ÿ“… Session 9
๐ŸŽฎ Godot 4.6
๐Ÿš€ Thruster Textures
โš™๏ธ Ship Physics Profiles
๐Ÿ›ธ Viper & BGS Feel
๐Ÿš€

Session Overview

Short session focused on finishing the thruster visual editing workflow started yesterday and implementing distinct physics profiles for the Viper and BGS capital ships.

  • Fixed: Thruster nodes had no texture in the scene editor; they now show the correct thruster graphic so you can position and scale them visually
  • Copied: BGS-2 thruster position and scale matched to BGS-1 after BGS-1 was tuned in the editor
  • New: Per-series physics profile table: Viper and BGS now have completely distinct movement feel
๐Ÿ›

Thrusters Invisible in Scene Editor

After the scene restructure (yesterday), thruster Sprite2D nodes were positioned at the scene root but had no texture assigned. The runtime code assigns textures during the animation loop, but the Godot editor has no texture to show, so the nodes appeared as invisible reference points only.

Fix

Added a static default texture to each thruster node directly in the .tscn file. The texture is frame 1 of the animation set the ship actually uses at runtime, so what you see in the editor is a faithful representation of the in-game thruster size and shape.

  • Standard ships (CX16, DKO, WO84, CP2, PTM): Thrusters1.png (uid://hgoog44tayxj)
  • Viper + BGS ships: PTModelSprite_ID41378.png (uid://y7f2p77dgwyj), frame 1 of the BGS 10-frame set

Each ship .tscn now has two ext_resource declarations: id="1_sprite" for the hull, id="2_thrust" for the thruster preview texture. All thruster nodes reference ExtResource("2_thrust").

How to tune thrusters: Open any ship scene, select ThrusterCenter / ThrusterLeft / ThrusterRight, drag to reposition or adjust scale in the Inspector. The texture you see is the in-game sprite at world-space size. Save the scene and you’re done.
๐Ÿ›ธ

BGS-2 Thruster: Synced to BGS-1

BGS-1 thrusters were tuned visually in the editor. The final positions and scales were then copied directly into Ship_BGS2.tscn so both capital ships share the same exhaust placement.

Node Position Scale
ThrusterLeft (-91, 625) (1.5, 1.157)
ThrusterRight (92, 625) (1.5, 1.153)

The slight scale asymmetry (1.157 vs 1.153) is from the editor’s sub-pixel drag snapping and is visually imperceptible. Both ships now have identical thruster appearance.

โš™๏ธ

Per-Series Physics Profiles

Problem

All ships previously shared the same four flat constants for movement: BASE_ROTATION_SPEED, BASE_THRUST_FORCE, BASE_MAX_SPEED, and LEVEL_FRICTION. Every ship felt the same. The Viper (a fast nimble fighter) and BGS-1/2 (slow capital ships) had identical movement, both with the standard space-drift feel.

The SHIP_PHYSICS Table

Replaced the four flat constants with a single SHIP_PHYSICS array indexed by ship series. Each entry defines the full physics profile for that series:

const SHIP_PHYSICS: Array = [
    # [rotation, thrust,  max_spd, fric_l0, fric_l1, fric_l2]
    [200.0, 500.0,  600.0, 0.995, 0.991, 0.986],  # 0 โ€” CX16    standard feel
    [200.0, 500.0,  600.0, 0.995, 0.991, 0.986],  # 1 โ€” DKO     standard feel
    [200.0, 500.0,  600.0, 0.995, 0.991, 0.986],  # 2 โ€” WO84    standard feel
    [200.0, 500.0,  600.0, 0.995, 0.991, 0.986],  # 3 โ€” CP2     standard feel
    [200.0, 500.0,  600.0, 0.995, 0.991, 0.986],  # 4 โ€” PTM     standard feel
    [300.0, 3000.0, 750.0, 0.85,  0.85,  0.85 ],  # 5 โ€” Viper   fast nimble fighter, zero drift
    [50.0,  600.0,  260.0, 0.90,  0.90,  0.90 ],  # 6 โ€” BGS-1   slow capital ship, zero drift
    [50.0,  600.0,  260.0, 0.90,  0.90,  0.90 ],  # 7 โ€” BGS-2   slow capital ship, zero drift
]

How the Friction Value Works

Each physics frame, velocity *= _cur_friction. A value close to 1.0 means velocity barely decays and the ship drifts like it’s in frictionless space. Lower values produce a snappier stop:

Friction Stop time from full speed Feel
0.995 (standard) ~10+ seconds Floaty space drift (classic asteroid game)
0.90 (BGS) ~1 second Heavy, authoritative stop (capital ship)
0.85 (Viper) ~0.5 seconds Instant response (fighter jet)

Viper: Fast Nimble Fighter

  • Rotation: 300 deg/s (1.5ร— standard), turns extremely quickly
  • Thrust: 3000 px/sยฒ (6ร— standard), reaches top speed in ~0.25 seconds
  • Max speed: 750 px/s (1.25ร— standard), fastest ship in the game
  • Friction: 0.85, stops dead in ~0.5 seconds, zero drift

BGS-1 & BGS-2: Slow Capital Ships

  • Rotation: 50 deg/s (0.25ร— standard), must plan turns well in advance
  • Thrust: 600 px/sยฒ (1.2ร— standard numerically, but max speed is much lower)
  • Max speed: 260 px/s (0.43ร— standard), slow but deliberate movement
  • Friction: 0.90, stops in ~1 second, no floating

Implementation

In _apply_ship_setup(), the profile is loaded into four instance variables:

var phys: Array = SHIP_PHYSICS[clampi(series, 0, SHIP_PHYSICS.size() - 1)]
_phys_rotation  = phys[0]
_phys_thrust    = phys[1]
_phys_max_speed = phys[2]
_phys_friction  = [phys[3], phys[4], phys[5]]

In _physics_process(), the per-level multipliers are applied on top:

var li: int = clampi(_gm.get_ship_level(_gm.selected_series), 0, 2)
_cur_rotation_speed = _phys_rotation  * LEVEL_ROTATION_MULT[li]
_cur_thrust_force   = _phys_thrust    * LEVEL_SPEED_MULT[li]
_cur_max_speed      = _phys_max_speed * LEVEL_SPEED_MULT[li]
_cur_friction       = _phys_friction[li]

For standalone ships (Viper series 5, BGS series 6/7) the level is always 0, so LEVEL_ROTATION_MULT[0] and LEVEL_SPEED_MULT[0] are both 1.0, so the profile values are used exactly as defined. Standard multi-level ships continue to scale naturally with upgrades.

To tune any ship’s feel: Edit the values in the SHIP_PHYSICS table in scripts/entities/Player.gd lines 22โ€“31. Each row is one series. Changes take effect immediately on the next play session with no other code changes needed.
๐Ÿ›

Thrusters at the Wrong Position and Scale at Runtime

Although thrusters were positioned correctly in the .tscn editor, they rendered in the wrong place and size in-game. The cause: _apply_ship_setup() scales the ship’s body art to fit SHIP_DISPLAY_SIZES, but did not apply that same scale factor to the thruster nodes.

Fix

The scale factor s = disp / longest (where disp is the target display size and longest is the raw texture’s longest axis) is now applied to both the position and scale of all three thruster nodes, matching the hull transform exactly.

var s: float = disp / longest
body_art.scale = Vector2(s, s)
if thruster_center:
    thruster_center.position *= s
    thruster_center.scale    *= s
# same for thruster_left, thruster_right

Original thruster transforms are cached at setup time so the debug resize tool can correctly re-apply the scale when the ship size is adjusted live.

๐Ÿ”ง

Runtime Ship Scale Tuner

Added a keyboard-driven ship size tuner so display sizes can be dialled in while the game is running, with a one-key save that writes the value directly back into SHIP_DISPLAY_SIZES in Player.gd, with no copy-pasting required.

Key Action
- Shrink current ship 5 px
= Grow current ship 5 px
S Write current size back into Player.gd on disk (editor only)

Guarded by const DEBUG_SHIP_SCALE: bool = true at the top of Player.gd. Set to false before shipping; the key handlers and file-write code are compiled out entirely.

Save uses:ProjectSettings.globalize_path("res://scripts/entities/Player.gd") + FileAccess. It parses the current series row in SHIP_DISPLAY_SIZES and replaces only the value at the correct level index, leaving all other values untouched.
๐Ÿ›

Bug Fixes: Collision & Rendering

Enemy does not die on player collision

When an enemy rammed the player, _on_body_entered called body.take_damage() on the player but only called hit() once on the enemy. Since _hp = 2, the enemy survived the collision. Fixed by calling hit() twice, matching the asteroid collision path, so any enemy that rams the player is instantly destroyed.

Asteroids rendering over thrusters

Thrusters appeared behind asteroids because all nodes defaulted to z_index = 0. Fixed in _apply_ship_setup(): thruster nodes are forced to z_index = 0 and the hull sprite to z_index = 1, so the hull and thrusters always render above any game objects at the default z layer.

๐Ÿ›ก๏ธ

Shield Scale: Per-Ship Editor Control

The shield power-up sprite is the same asset for all ships, but each ship hull is a different size, so the shield circle needed independent scaling per series. The requirement was that this be adjustable in the editor with a live visual preview, not tuned at runtime.

Implementation

Eight individually named @export var properties were added to Player.gd, one per ship series, grouped under Shield Scale in the Inspector:

@export_group("Shield Scale")
@export var shield_scale_cx16:  float = 0.9
@export var shield_scale_dko:   float = 0.9
@export var shield_scale_wo84:  float = 0.9
@export var shield_scale_cp2:   float = 0.9
@export var shield_scale_ptm:   float = 0.9
@export var shield_scale_viper: float = 0.9
@export var shield_scale_bgs1:  float = 0.9
@export var shield_scale_bgs2:  float = 0.9

Each setter calls _editor_update_preview() immediately, so changing the value in the Inspector shows the shield overlay on the ship in real time. A ShieldPreview Sprite2D was added to Player.tscn at 50% alpha, visible only during editor preview and hidden at runtime.

At runtime, activate_shield() reads the correct value via a match helper:

func _shield_scale_for_series(s: int) -> float:
    match s:
        0: return shield_scale_cx16
        1: return shield_scale_dko
        # ...
    return 0.9
โš™๏ธ

CollisionScaler: Auto-Scaling Collision Meshes

Collision polygons in Player.tscn were authored at a fixed pixel size. When SHIP_DISPLAY_SIZES was changed to resize a ship, the collision shape no longer matched the visible hull, requiring a full re-author of all 18 polygons every time.

Solution

All 18 CollisionPolygon2D nodes were moved under a single CollisionScaler Node2D. At runtime, _apply_ship_setup() computes the ratio of the current display size to the size at which the polygons were authored, then sets CollisionScaler.scale to that ratio:

var ratio: float = cur_disp / authored_disp
_cs.scale = Vector2(ratio, ratio)

The authored sizes are recorded in COLLISION_AUTHORED_DISP, a table that mirrors SHIP_DISPLAY_SIZES. After re-authoring polygons, update COLLISION_AUTHORED_DISP to match and future size changes in SHIP_DISPLAY_SIZES will auto-scale correctly with no further re-authoring.

One-time cost: All 18 collision polygons must be re-authored once after this change. From then on, any size change in SHIP_DISPLAY_SIZES automatically scales the collision mesh to match.

The editor preview (_editor_update_preview()) also reads COLLISION_AUTHORED_DISP and scales CollisionScaler live, so the collision shape shown in the editor matches what will be used in-game at the selected display size.

๐Ÿ”

Code Review: Platform Compatibility & Robustness

Full code review pass targeting cross-platform correctness, null safety, and type safety. All changes confirmed before applying.

Asset Folder Typo: “Assests” โ†’ “Assets” (both folders)

Two folders had the same misspelling: Game Assests/ (renamed in a previous session) and a separate Assests/ folder containing fonts, GUI, HUD, and effects. The second folder was renamed to Assets/ and all references updated across .gd, .tscn, and .import files. Zero remaining references to the old spelling in code files.

GameScene.gd: Null Safety

  • get_node("/root/GameManager") โ†’ get_node_or_null with null guard and push_error, preventing a crash if the autoload is missing
  • $BackgroundRoot.add_child(bg) โ†’ get_node_or_null("BackgroundRoot") with null guard, safe on all platforms

GameScene.gd: Removed Redundant _BG_PATHS

A _BG_PATHS array of 13 background paths duplicated entries already present in LEVEL_BG. Removed entirely. Endless mode now picks a random background directly from LEVEL_BG:

bg_path = LEVEL_BG[randi_range(1, LEVEL_BG.size() - 1)]

GameScene.gd: Hardcoded Screen Dimensions Removed

Wave warning and sector banner overlays used hardcoded 1920.0 for width and fixed pixel offsets for vertical position, both invisible on non-1920 screens. Replaced with dynamic values: screen_size.x for full width, and screen_size.y * 0.5 with fixed offsets from centre so banners are centred on any resolution.

EnemyUnit.gd: State Reset Every Frame

_state was unconditionally reset to PATROL every frame and then immediately overwritten. Collapsed to a single conditional assignment, cleaner and marginally cheaper each frame.

Player.gd: Array Type Annotations

Six untyped Array constants annotated as Array[Array] for GDScript type safety and better editor tooling: SHIP_PHYSICS, SHIP_DISPLAY_SIZES, COLLISION_AUTHORED_DISP, COLLISION_NODE_NAMES, _EDITOR_SHIP_PATHS, SHIP_SCENE_PATHS.

Player.gd: LEVEL_SPEED_MULT Clarification

Added a comment clarifying that LEVEL_SPEED_MULT is applied to boththrust_force and max_speed, not just speed. The name alone implied only one use.

๐Ÿ“‹

Files Changed on 16 May 2026

File Change
scenes/entities/ships/Ship_CX16_X1โ€“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
NEW ext_resource “2_thrust” added; thruster nodes now have a default texture visible in the editor.
scenes/entities/ships/Ship_BGS1.tscn NEW Thruster texture. CHANGE Thruster positions & scales tuned visually in editor.
scenes/entities/ships/Ship_BGS2.tscn NEW Thruster texture. CHANGE Thruster transforms copied from BGS-1.
scenes/entities/Player.tscn NEWShieldPreview Sprite2D (50% alpha, editor-only). CollisionScaler Node2D: all 18 CollisionPolygon2D nodes moved under it.
scripts/entities/Player.gd CHANGE SHIP_PHYSICS table replaces flat physics constants. Thruster scale fix (s = disp/longest). Debug ship scale tool (-/=/S). Shield scale @export vars (ร—8) + ShieldPreview in editor. CollisionScaler ratio system + COLLISION_AUTHORED_DISP. Array[Array] type annotations (ร—6). LEVEL_SPEED_MULT comment.
scripts/entities/EnemyUnit.gd FIX Enemy now dies on player collision (double hit). FIX State assignment collapsed to single conditional. CHANGE Asset path typo corrected.
scripts/game/GameScene.gd FIX get_node โ†’ get_node_or_null (ร—2). FIX Hardcoded 1920.0 โ†’ screen_size.x / screen_size.y ร— 0.5. CHANGE _BG_PATHS removed; endless mode uses LEVEL_BG directly. Asset path typo corrected.
Assets/ (formerly Assests/) CHANGE Folder renamed. All .gd, .tscn, and .import references updated.
scripts/menus/CharacterShop.gd
scripts/menus/Achievements.gd
scripts/menus/LevelSelect.gd
scenes/menus/*.tscn (ร—8)
scenes/ui/HUD.tscn
CHANGE Asset path typo corrected (res://Assests/ โ†’ res://Assets/).
๐Ÿ“‹

Open Items

Item Priority Notes
Re-author all 18 player collision polygons High Required after CollisionScaler restructure. Use Editor Preview in Player.tscn. After authoring, update COLLISION_AUTHORED_DISP to match SHIP_DISPLAY_SIZES.
Set DEBUG_SHIP_SCALE = false before shipping Pre-ship Top of scripts/entities/Player.gd line 15
Remove DEBUG_AddCoins button before shipping Pre-ship In CharacterShop.tscn and CharacterShop.gd
Tune thruster positions per ship variant High BGS done. Viper tuned in editor. Standard ships need review.
Tune gun barrel positions per ship variant High Open each ship .tscn, drag GunBarrel Marker2D nodes under Body.
Tune shield scale per ship High Use the Shield Scale group in Player.tscn inspector; live ShieldPreview shows the result.
Play-test Viper and BGS physics feel High Values calculated but untested. Tune SHIP_PHYSICS table as needed.
Add all 12 sound effects High SOUND NEEDED comments mark every trigger point in the codebase.
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).
BGS rocket mechanic / Viper level-up benefit Medium Standalone ships have no level-up reward yet.
Mobile joystick wiring Medium MobileControls visible=false; input not connected.
Audio buses Medium Create Master โ†’ Music and SFX buses once sounds are added.
Coin shop / IAP, AdMob, GDPR, leaderboards, export signing Low Not started.