Dev Log โ 16 May 2026
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").
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.
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.
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.
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_nullwith null guard andpush_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. |