Asteroids Destroyer Dev Log -3 May 2026

Asteroids Destroyer โ€” Development Journal

Dev Log โ€” 3 May 2026

๐Ÿ“… Sessions 1โ€“3
๐ŸŽฎ Godot 4.6
๐Ÿ’ฅ Collision Overhaul
๐Ÿš€ Ship System Redesign
๐Ÿค– Enemy AI
๐Ÿš€

Day Overview: Three Sessions

A full day of work split across three sessions. The day started with a major collision shape overhaul (moving from runtime circle shapes to baked per-variant polygons), followed by a complete ship system and shop redesign, and finished with gameplay polish, enemy AI improvements, achievements, and a full sound gap analysis.

Session Focus Files
Session 1 Collision overhaul: 12 asteroid scenes, all entity polygons baked into scenes, per-variant scaling 22 files
Session 2 Ship system redesign: 8 ships, per-ship scores, shop rewrite, crystal drops, space coin UI, game over panel 16 files
Session 3 Ship controls per level, ship level-up mid-game, enemy AI overhaul, achievements, sound gap analysis 18 files
Session 1 ยท Collision Overhaul
๐Ÿ—๏ธ

Collision Overhaul: Per-Variant Polygon Shapes

Every asteroid variant has a completely different outline. The old system created a CircleShape2D at runtime using min(w, h) * 0.42 as the radius, a rough approximation that could never match irregular asteroid shapes. Player ships and enemy units had the same problem.

Decision: Replace all runtime circle shapes with CollisionPolygon2D nodes baked into each scene file. Every entity now has a polygon that can be adjusted by dragging vertices in the Godot editor viewport, with no code required.

12 Separate Asteroid Scene Files

The previous design used a single Asteroid.tscn and picked the variant at runtime, which meant one collision shape for all 12 shapes. Each new scene locks in its size and variant_index as baked export values and provides a CollisionPolygon2D with a default octagon hull. All 12 scenes share the same Asteroid.gd script.

  • LARGE (Size 0): Asteroid_A1, A3, A4, A5: variant_index 0โ€“3
  • MEDIUM (Size 1): Asteroid_A6, A7, A8, A10: variant_index 0โ€“3
  • SMALL (Size 2): Asteroid_A9, A11, A12, A13: variant_index 0โ€“3

GameScene.gd: 12-Scene Lookup

The old single @export var asteroid_scene was replaced with a constant dictionary mapping size group to four variant paths. spawn_asteroid() selects randomly within the size group when vid == -1. Godot caches load() so the same path returns instantly after the first call.

All Other Entity Collision Shapes

Entity Old Shape New Shape
Player.tscn CapsuleShape2D (r22, h52) 4-point arrowhead polygon (0,โˆ’52), (28,16), (0,8), (โˆ’28,16)
Bullet.tscn CapsuleShape2D (r9, h30) Vertical diamond matching missile sprite
EnemyUnit.tscn CircleShape2D (runtime override) Octagon polygon r50, matches saucer at scale 0.68
EnemyMothership.tscn CircleShape2D (runtime override) Octagon polygon r55
EnemyBullet.tscn CircleShape2D (runtime) Small diamond polygon

Script Cleanup

Four scripts had runtime collision shape creation removed: Asteroid.gd (removed _fit_collision_shape()), EnemyUnit.gd, EnemyMothership.gd, and EnemyBullet.gd.

๐Ÿ›

Asteroid Scaling: RigidBody2D Warning + Per-Variant Scale Fix

Godot showed a Node Configuration Warning on every asteroid scene: “Size change to RigidBody2D will be overridden by the physics engine when running.” Caused by scale set on the root RigidBody2D node. Fixed by applying scale to child nodes only (sprite.scale).

This revealed a second issue: A single scale per size group is wrong. A1 source is 600px wide, A5 is only 350px, so at the same scale they render at very different sizes. A12 at the small scale was only 27ร—28px, effectively invisible.

Per-Variant Scale Factors

Each variant now has its own scale calculated from its texture’s longest dimension to hit a consistent target display size: LARGE ~260px, MEDIUM ~155px, SMALL ~85px.

const SCALE_FOR_VARIANT: Array = [
    # LARGE  (A1=600px, A3=514px, A4=500px, A5=350px)
    [Vector2(0.43,0.43), Vector2(0.51,0.51), Vector2(0.52,0.52), Vector2(0.74,0.74)],
    # MEDIUM (A6=450px, A7=300px, A8=240px, A10=260px)
    [Vector2(0.34,0.34), Vector2(0.52,0.52), Vector2(0.65,0.65), Vector2(0.60,0.60)],
    # SMALL  (A9=250px, A11=240px, A12=170px, A13=190px)
    [Vector2(0.34,0.34), Vector2(0.35,0.35), Vector2(0.47,0.47), Vector2(0.45,0.45)],
]

The collision polygon vertices are stored at native (unscaled) coordinates and scaled at runtime together with the sprite. Tracing polygons in the editor is done at native texture size, which makes it easier to place vertices accurately.

Per-Ship Collision Polygons in Player.tscn

Player.tscn now contains 9 CollisionPolygon2D nodes named CollisionPolygon2D_0 through _8. Only the one matching the equipped ship is active at runtime. All 9 ship textures are embedded as ext_resources so you can swap the Sprite2D texture in the Inspector while tracing each polygon.

Session 2 ยท Ship System & Shop Redesign
๐Ÿ›ธ

Ship System Redesign: Buy to Own, Level Up to Advance

The old system sold each ship variant (X1, X2, X3) as a separate purchase. The new design buys a ship once and levels it up through gameplay, earning stronger variants as a reward for play rather than as a purchase.

Ship Roster: 8 Total

  • Series ships (5): CX16, DKO, WO84, CP2, PTM: each has X1, X2, X3 levels
  • Standalone ships (3): Viper, BGS-1, BGS-2: single variant, level-up behaviour reserved for later

Shop order (index 0โ€“7): CX16, DKO, WO84, CP2, PTM, Viper, BGS-1, BGS-2.

Level-Up Architecture: Per-Ship Score Tracking

Each ship has an independent score counter in ship_scores[series]. Score only accumulates while that ship is equipped. Level is derived at runtime, never stored separately.

const SHIP_LEVEL_THRESHOLDS: Array[int] = [0, 1000, 6000]  # raised to 8000/40000 in Session 3

func get_ship_level(series: int) -> int:
    if SHIP_IS_STANDALONE[series]: return 0
    var score: int = ship_scores[series]
    if score >= SHIP_LEVEL_THRESHOLDS[2]: return 2
    elif score >= SHIP_LEVEL_THRESHOLDS[1]: return 1
    return 0

Gun Count by Level

  • X1: Single centre gun
  • X2: Twin side guns
  • X3: Triple guns (centre + both sides)
๐Ÿ’ฐ

Character Shop ยท Crystal Drops ยท Space Coin UI

Character Shop Redesign

The shop was rebuilt from scratch to show 8 tiles in a 4-column grid. Each tile displays the ship image (grayed if not owned), name, owned/price status, current level, and score to next level.

  • Not owned: grayed image, Space Coin icon + price. Purchase on tap.
  • Owned (not selected): full image, cyan “OWNED” label, cyan border
  • Selected: orange border only; the border is the indicator

The duplicate CoinsRow display was removed. BtnCoins in the top-right corner is wired to coins_changed and shows a live Space Coin icon + count.

Crystal Drop Pickups

Asteroids now drop coloured space crystals when destroyed. Crystals drift slowly, pulse gently, and fade out after 10 seconds if not collected. Five colours with increasing point/coin rewards: Blue (5pts/1c), Green (8pts/1c), Purple (12pts/2c), Red (15pts/2c), Yellow (20pts/3c).

Asteroid Size Drop Amount
LARGE (50pts) Always 2 crystals, 50% chance of 3rd
MEDIUM (25pts) Always 1, 60% chance of 2nd
SMALL (10pts) 30% chance of 1

Crystals share the powerups_container node. Crystal.gd is an independent Area2D, and no PowerUp.gd code was modified.

Space Coin Icon

The Space Coin.png asset (657ร—655px) replaced all ๐Ÿช™ emoji occurrences. Large source required explicit size capping, because Godot Button icons render at native resolution without a width cap. Fixed with add_theme_constant_override("icon_max_width", 36) and TextureRect with EXPAND_IGNORE_SIZE + SIZE_SHRINK_CENTER flags on tile price rows.

Level Complete Priority Display

The Level Complete screen was rewritten with three-priority display: (1) Ship leveled up this session, (2) Rank promoted this session, (3) Neither, which shows current ship with rank and coins earned. SHIP_DISPLAY_CONFIG[series][level] allows per-ship offset and scale tuning without touching logic.

Game Over Panel Style

Retry and Continue buttons restyled with rounded dark-blue panels, cyan borders, neuropol font, matching the Level Complete screen. Three sub-resources added: GO_SBF_normal, GO_SBF_hover, GO_SBE_focus. Text: “TAP TO RETRY” โ†’ “Retry”, “CONTINUE” โ†’ “Continue”.

๐Ÿ›

Bug Fixes: Session 2

  • HUD.tscn parse error / null crash: Godot 4’s .tscn parser requires all [ext_resource] declarations before [sub_resource] blocks. StyleBox sub-resources were placed out of order, which silently failed scene instantiation. Fixed by rewriting HUD.tscn with correct section order.
  • Duplicate font UID in HUD.tscn: A second FontFile ext_resource was added with the same UID as the existing one. Duplicate UIDs fail the resource loader. Removed the duplicate.
  • Viper asset path mismatch: Asset was renamed from viper copy.png to viper.png. Both GameManager.gd and LevelComplete.gd still referenced the old name.
  • GDScript warnings in GameScene.gd: Integer division warning fixed with floori(float(...)/float(...)); unused world_pos parameter renamed to _world_pos.
Session 3 ยท Gameplay Polish & Enemy AI
๐Ÿ›ธ

Ship Controls Scale With Level ยท Mid-Game Level-Up

Per-Level Movement Stats

All three ship levels now feel meaningfully different. X2 is faster and tighter; X3 is most responsive with the least drift. Stats are recomputed at the top of every _physics_process call, so the upgrade takes effect the moment the threshold is crossed.

Level Rotation Thrust / Max Speed Friction
X1 (baseline) 200 deg/s 500 / 600 px/s 0.995
X2 (+15% / +10%) 230 deg/s 550 / 660 px/s 0.991
X3 (+25% / +20%) 250 deg/s 600 / 720 px/s 0.986

Mid-Game Ship Level-Up Animation

When a ship crosses the X2 or X3 threshold, the new sprite loads immediately and a rapid white flash signals the upgrade. Signal chain: GameManager.add_score() detects level โ†’ emits ship_leveled_up โ†’ GameScene calls player._apply_ship_setup() + player.trigger_level_up_effect() (4 white flashes over ~0.56 s).

Wing Gun Marker2D Nodes

Wing gun positions changed from hardcoded Vector2 offsets to named Marker2D nodes in Player.tscn: GunBarrelLeft (โˆ’18, โˆ’25) and GunBarrelRight (+18, โˆ’25). Positions can now be adjusted visually without touching any script.

Level-Up Thresholds Increased

Previous thresholds (X2 at 1,000pts, X3 at 6,000pts) were reachable in ~2 waves. New thresholds require genuine investment in a specific ship: X2 at 8,000pts, X3 at 40,000pts.

Crystal Burst Animation on Collection

Crystals now play a short tween on collection: scale to 2.5ร— while fading to transparent over 0.18โ€“0.20 seconds. Drop rates also reduced: LARGE โ†’ always 1 (was 2โ€“3); MEDIUM โ†’ 1 with 40% chance of 2nd (was always 1โ€“2); SMALL โ†’ 15% chance (was 30%).

๐Ÿค–

Enemy AI Overhaul ยท Game Over Overlay ยท Rank Badge Animation

Enemy AI: Asteroid Dodge + Active Targeting

Three major changes to EnemyUnit.gd:

  • Asteroid avoidance:_get_asteroid_dodge() scans all asteroids within 180px. Each contributes a repulsion vector scaled by proximity, added to movement each frame.
  • Active targeting in ATTACK state: Movement target becomes the player’s current position, so the enemy actively tracks and closes in.
  • Two-hit health + asteroid collision: HP changed from 1 to 2 (takes two bullets). Asteroid collision deals 2 damage (instant kill), creating a risk/reward mechanic.

Enemy bullets now call queue_free() on asteroid contact, so no more bullets flying through rock.

Game Over Overlay + Scene Pause

A GameOverOverlayColorRect (full-screen, black 75% alpha) was added behind the panel. show_game_over() now calls get_tree().paused = true. The HUD CanvasLayer has process_mode = PROCESS_MODE_ALWAYS so buttons still receive input while paused.

Rank Badge Animation on Promotion

The rank badge now reacts on promotion: scale pop 1.0โ†’1.4โ†’1.0 (0.38s) followed by a 2-second cyan glow that fades to white. Wired via a new _on_rank_up() handler instead of directly connecting to _update_rank_badge.

Shop Insufficient Funds Flash

Tapping a ship you can’t afford now flashes the tile red twice (double pulse on modulate). _tile_buttons: Array[Button] stores each tile button indexed by series for fast lookup. Sound placeholder comments added: # SOUND NEEDED: ship_purchase.wav and # SOUND NEEDED: insufficient_funds.wav.

Level Select: Shop Button Wired

BtnShop existed in the scene but was never connected. @onready ref added, wired to navigate to CharacterShop.tscn. Also added _make_number_display() for level numbers above 99 using atlas-sliced digit art.

๐Ÿ†

Achievements Screen ยท Sound Gap Analysis

Achievements Placeholder Menu

An Achievements button was added to the Main Menu top-left. The new scene uses a scrollable VBoxContainer with 12 placeholder achievements: First Flight, Asteroid Slayer, Crystal Collector, Coin Hoarder, Promoted!, Ship Upgrade, Max Firepower, Fleet Commander, Sharpshooter, Nuke ’em, Endless Pilot, High Scorer. All currently unlocked = false.

Sound Effects Still Needed (12 total)

Sound File Where to Wire
shoot.wav Player.gd ยท _handle_fire()
explosion_asteroid.wav Asteroid.gd / GameScene _on_asteroid_destroyed()
explosion_enemy.wav EnemyUnit.gd ยท hit() when HP = 0
crystal_collect.wav Crystal.gd ยท _burst_and_free()
ship_level_up.wav Player.gd ยท trigger_level_up_effect()
rank_up_fanfare.wav HUD.gd ยท _on_rank_up()
ship_purchase.wav CharacterShop.gd ยท success path
insufficient_funds.wav CharacterShop.gd ยท _flash_tile_red()
game_over.wav GameScene.gd ยท _on_player_died()
button_click.wav General UI ยท any menu button press
enemy_fire.wav EnemyUnit.gd ยท _shoot_at()
powerup_collect.wav PowerUp.gd ยท pickup handler

# SOUND NEEDED: filename.wav comments mark every trigger point in the codebase.

๐Ÿ“‹

All Files Changed on 3 May 2026

File Change Session
scenes/entities/Asteroid_A1โ€“A13.tscn (ร—12) NEW Per-variant scenes with CollisionPolygon2D, baked textures, size/variant_index exports S1
scripts/entities/Asteroid.gd CHANGE Removed _fit_collision_shape(); SCALE_FOR_SIZE โ†’ SCALE_FOR_VARIANT; sprite.scale + polygon both scaled per variant S1
scripts/game/GameScene.gd CHANGE ASTEROID_SCENE_PATHS dict; spawn_asteroid() uses path dict; crystal_scene preload; _spawn_crystals(); integer division fix; _world_pos rename; ship_leveled_up connected; _on_ship_leveled_up() added S1โ€“S3
scenes/entities/Player.tscn CHANGE CapsuleShape2D โ†’ arrowhead polygon; 9 CollisionPolygon2D nodes; GunBarrelLeft + GunBarrelRight Marker2D nodes S1โ€“S3
scenes/entities/Bullet.tscn CHANGE CapsuleShape2D โ†’ vertical diamond polygon S1
scenes/entities/EnemyUnit.tscn CHANGE CircleShape2D โ†’ octagon polygon r50 S1
scripts/entities/EnemyUnit.gd CHANGE Removed runtime shape; _hp=2; asteroid dodge + active targeting; _on_body_entered asteroid handling S1, S3
scenes/entities/EnemyMothership.tscn CHANGE CircleShape2D โ†’ octagon polygon r55 S1
scripts/entities/EnemyMothership.gd CHANGE Removed runtime shape creation S1
scenes/entities/EnemyBullet.tscn CHANGE CircleShape2D โ†’ small diamond polygon S1
scripts/entities/EnemyBullet.gd CHANGE Removed runtime shape; asteroid contact โ†’ queue_free() S1, S3
scripts/autoload/GameManager.gd CHANGE 8-ship system; owned_ships[]; ship_scores[]; get_ship_level(); buy_ship(); ship_leveled_up signal; viper.png path fix; SHIP_LEVEL_THRESHOLDS โ†’ [0, 8000, 40000] S2โ€“S3
scripts/entities/Player.gd CHANGE COLLISION_NODES for 18 named polygons; BASE_* constants; LEVEL_* multiplier arrays; GunBarrelLeft/Right refs; trigger_level_up_effect() S2โ€“S3
scripts/menus/CharacterShop.gd CHANGE 8-tile grid; BtnCoins live update; Space Coin icon on price tiles; _tile_buttons array; _flash_tile_red(); sound comments S2โ€“S3
scenes/menus/CharacterShop.tscn CHANGE Removed CoinsRow + CoinsLabel; columns 3 โ†’ 4 S2
scripts/menus/LevelComplete.gd CHANGE LC_SHIP_PATHS; SHIP_DISPLAY_CONFIG; three-priority _populate(); viper.png path fix S2
scripts/entities/Crystal.gd NEW Area2D pickup: 5 colours, drift, pulse, fade, burst tween on collection, _collected guard S2โ€“S3
scenes/entities/Crystal.tscn NEW Area2D + Sprite2D + CollisionShape2D S2
scenes/ui/HUD.tscn FIX Ext_resource order fix (parse error); GO_SBF_normal/hover/focus sub-resources; GameOverOverlay ColorRect; process_mode=3 S2โ€“S3
scripts/ui/HUD.gd CHANGE game_over_overlay ref; show_game_over() shows overlay + pauses; retry/continue unpause; _on_rank_up() badge animation S2โ€“S3
scripts/menus/MainMenu.gd CHANGE Space Coin icon; btn_achievements wired; _on_achievements() S2โ€“S3
scenes/menus/MainMenu.tscn CHANGE BtnAchievements at top-left (3,3) S3
scripts/menus/LevelSelect.gd CHANGE btn_shop wired; _on_shop(); _make_number_display() + digit sheet constants S3
scripts/menus/Achievements.gd NEW 12 placeholder achievements, scrollable row builder S3
scenes/menus/Achievements.tscn NEW CanvasLayer + ScrollContainer + VBoxContainer list S3
๐Ÿ“‹

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 All Col_* nodes use the default arrowhead polygon. Adjust per ship in Player.tscn using the texture swap workflow.
Tune GunBarrelLeft / GunBarrelRight per ship High Default offsets (ยฑ18, โˆ’25) work for CX16 only. Adjust for DKO, WO84, CP2, PTM, standalone ships.
Tune asteroid collision polygons (ร—12) Medium A1 traced manually. A3โ€“A13 have default octagon.
Wire real achievement data Medium All 12 slots are placeholder (unlocked = false). Connect to persistent progress in GameManager.
Standalone ship level-up (rockets) Medium Viper, BGS-1, BGS-2 gain no level-up benefit. Rocket unlock planned.
Mobile joystick wiring Medium MobileControls visible=false; input not connected.
Audio buses Medium Create Master โ†’ Music and SFX buses in Godot Audio panel 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.