Dev Log โ 3 May 2026
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 |
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.
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.
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
.tscnparser requires all[ext_resource]declarations before[sub_resource]blocks. StyleBox sub-resources were placed out of order, which silently failed scene instantiation. Fixed by rewritingHUD.tscnwith correct section order. - Duplicate font UID in HUD.tscn: A second
FontFileext_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.pngtoviper.png. BothGameManager.gdandLevelComplete.gdstill referenced the old name. - GDScript warnings in GameScene.gd: Integer division warning fixed with
floori(float(...)/float(...)); unusedworld_posparameter renamed to_world_pos.
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. |