Asteroids Destroyer Dev Log – 2 May 2026

Asteroids Destroyer โ€” Development Journal

Dev Log โ€” 2 May 2026

๐Ÿ“… Session 2
๐ŸŽฎ Godot 4.6
๐Ÿ›ธ Enemy Ships
๐Ÿ“ˆ Endless Scaling
๐ŸŽฎ Controller Support
๐Ÿ“‹

Session Overview

Big session: enemy ships, endless difficulty scaling, controller support, and several fixes. The full enemy ship system was built from scratch, starting with enemy units that spawn from level 4, a mothership from level 21, animated bullets for both, saucer-style spin, and sine-wave drift movement. Endless mode difficulty scaling was wired up with a virtual level system and sector banners, full controller support added with analog stick rotation, and a handful of smaller issues fixed including the endless mode blank background and enemy ship sizes.

  • New Enemy unit ship: spawns from level 4, saucer spin, sine-wave movement, fires animated bullets
  • New Enemy mothership: spawns from level 21, hovers and orbits, fires big bullets, spawns units
  • New Animated enemy bullets: 6-frame small and big variants
  • New Endless mode difficulty scaling: virtual level, score multiplier, sector banner
  • New Controller support: left stick rotate, right stick thrust, triggers fire, Y nuke, Start pause
  • Fix Endless mode blank background: random background chosen on each run
  • Fix Endless mode negative asteroid bonus at level 0
  • Change Enemy ship sizes increased to match player ship scale
  • Change Enemy bullet sizes increased: small 0.5โ†’1.0, big 0.7โ†’1.2
  • Fix Game Over screen: WindowWhole background panel added behind labels and buttons
๐Ÿ›ธ

Enemy Ship System: 6 New Files

Architecture

All enemies are Area2D nodes, which gives full position control in _process without physics engine interference.

Node collision_layer collision_mask Detects
Player bullet 4 10 Asteroids (2) + Enemies (8)
Enemy unit / mothership 8 5 Player body (1) + Player bullets (4)
Enemy bullet 16 1 Player body (1)

Enemy Unit

Spawns from level 4. The sprite child rotates independently at 40ยฐ/s (saucer effect) without affecting the root node’s movement direction. Movement is sine-wave weave: a perpendicular oscillation added to the base direction vector gives a spaceship-feel drift rather than straight asteroid-style movement. Two states: Patrol (random screen targets) and Attack (faster, higher fire rate when player within 520px).

  • HP: 1, Points: 100
  • Fire interval: max(2.0, 4.0 โˆ’ (level โˆ’ 4) ร— 0.06), gets faster each level
  • Count per wave: clamp(1 + floor((levelโˆ’4)/4) + wave_index, 0, 8)
  • Rendered size: 79 px, matching the player ship (116 px asset at scale 0.68)

Enemy Mothership

Spawns from level 21, final wave only. Slower spin (15ยฐ/s). Approaches a fixed hover point in the upper quarter of the screen, then enters a small circular orbit (45 px radius). Fires big animated bullets at the player and periodically deploys new enemy units via a unit_spawn_requested signal connected to GameScene. GameScene increments enemies_alive for each spawned unit so the wave won’t end until they’re all cleared.

  • HP: 3, Points: 300, Hit flash: red modulate tween
  • 2 motherships from level 41+
  • Rendered size: 150 px, clearly larger than the player (230 px asset at scale 0.65)

Animated Enemy Bullets

6-frame manual animation at 12 fps, same pattern as player thrusters. Small bullets for units, big bullets for mothership. Direction set via a launch(direction) method called after add_child, sets velocity and orients the sprite toward the direction of travel.

Wave Completion

The wave completion check now requires both asteroids_alive <= 0 AND enemies_alive <= 0. This applies in _on_asteroid_destroyed, _on_enemy_destroyed, and _remove_offscreen_asteroids.

Note:Bullet.tscncollision_mask was widened from 2 to 10 (adds enemy layer 8) so player bullets detect enemy Area2D nodes via _on_area_entered. The existing _on_body_entered enemies check remains for any future physics-body enemies.
๐Ÿ“ˆ

Endless Mode Difficulty Scaling: Virtual Level System

The Problem

Endless mode set current_level = 0 and looped waves forever at the same difficulty. Worse, level = 0 caused a negative asteroid bonus (floori((0โˆ’1)/3) = โˆ’1), so endless mode actually had fewer asteroids than level 1.

Virtual Level System

An _endless_level integer starts at 1 and increments every 3 waves. A single helper function routes all difficulty math through it:

func _effective_level() -> int:
    return _endless_level if _gm.current_level == 0 else _gm.current_level

This is swapped into _get_wave_composition(), _spawn_interval(), and _spawn_enemies_for_wave(), so all existing level-based scaling applies automatically in endless mode with no duplicated logic.

Waves Completed _endless_level What Changes
1โ€“3 1 Normal level-1 difficulty
4โ€“6 2 More asteroids, faster spawns
10โ€“12 4 Enemy units begin spawning
61โ€“63 21 Mothership appears
121โ€“123 41 Two motherships per wave

Score Multiplier

Each time _endless_level increases, the score multiplier steps up by 0.1ร— (capped at 3.0ร—). All kill points are multiplied before being passed to GameManager.add_score(). In normal levelled mode the multiplier stays at 1.0ร—, so it has no effect outside endless.

Sector Banner

When the virtual level increases, a purple banner flashes on screen showing the new sector number and current score multiplier. It holds for 1.2 s, fades out, then hands off to the existing wave warning. The purple colour is intentionally distinct from the red wave warning so the player can tell the difference at a glance.

๐ŸŽฎ

Controller Support: Analog Rotation

Controller bindings were added to all input actions in project.godot. The layout uses both sticks for movement so thumbs never leave the sticks during play, and triggers handle firing with no conflict.

Action Controller Binding Fallback
Rotate left Left stick โ† D-pad โ†
Rotate right Left stick โ†’ D-pad โ†’
Thrust Right stick โ†‘ D-pad โ†‘
Fire Left trigger Right trigger
Nuke Y / Triangle โ€”
Pause Start / Menu โ€”

Analog Rotation

_handle_rotation() was updated to use Input.get_axis() instead of two is_action_pressed() checks. This returns a float from โˆ’1.0 to +1.0 so gently pushing the stick gives slow rotation and fully pushing gives full speed. Keyboard behaviour is unchanged; it still snaps to โˆ’1/0/1.

# Before โ€” digital only, no analog feel
var dir: float = 0.0
if Input.is_action_pressed("rotate_left"):  dir -= 1.0
if Input.is_action_pressed("rotate_right"): dir += 1.0

# After โ€” smooth proportional rotation from analog stick
var dir: float = Input.get_axis("rotate_left", "rotate_right")
Note: Controller support is implemented as an accessibility feature. No changes to gameplay scripts were required, since Godot’s input system reads the bindings automatically.
๐ŸŒŒ

Endless Mode: No Background Fix

Bug Endless mode sets current_level = 0. The background loader had an early-return guard at level < 1, so endless mode always loaded a blank screen. A _BG_PATHS constant (all 13 backgrounds) and a level-0 branch that picks one at random on each run was added. Every endless session now starts with a different background including planet float animation.

# Before: level = 0 โ†’ guard fires โ†’ blank screen
# After:  level = 0 โ†’ randi() % 13 โ†’ random BG_01โ€“BG_13
๐Ÿ“‹

Files Changed on 2 May 2026

File Change
scripts/entities/EnemyBullet.gd New 6-frame animated bullet, launch(direction) method, body_entered โ†’ player.take_damage()
scenes/entities/EnemyBullet.tscn New Area2D, collision_layer=16, collision_mask=1
scripts/entities/EnemyUnit.gd New Saucer spin, sine-wave weave, patrol/attack states, 1 HP, 100 pts
scenes/entities/EnemyUnit.tscn New Area2D, collision_layer=8, collision_mask=5
scripts/entities/EnemyMothership.gd New Hover orbit, unit spawning, big bullets, 3 HP, 300 pts
scenes/entities/EnemyMothership.tscn New Area2D, collision_layer=8, collision_mask=5
scripts/entities/Bullet.gd Change _on_area_entered now handles enemies group
scenes/entities/Bullet.tscn Change collision_mask widened from 2 to 10 to detect enemy layer
scripts/entities/Player.gd Change _handle_rotation uses get_axis() for analog stick support
scripts/game/GameScene.gd Change Enemy system, endless scaling, _effective_level(), sector banner, enemies_alive tracking, _BG_PATHS random pick for endless
project.godot New Controller bindings added to all 5 input actions
scripts/entities/EnemyUnit.gd Change Sprite scale 0.24โ†’0.68 (79 px rendered)
scripts/entities/EnemyMothership.gd Change Sprite scale 0.36โ†’0.65 (150 px rendered)
scripts/entities/EnemyBullet.gd Change Bullet scale increased: small 0.5โ†’1.0, big 0.7โ†’1.2
scenes/ui/HUD.tscn Fix WindowWhole TextureRect added as first child of GameOverPanel, so the background now shows behind all labels and buttons
๐Ÿ“Œ

Open Items

Item Priority Notes
Mobile joystick wiring Medium Not connected to player movement
Add “Music” and “SFX” audio buses Medium Required to activate Settings mute toggles
Planet positions BG_02โ€“BG_13 Medium Editor adjustment needed per background scene
Level Complete visual polish Medium Polish pass still needed
Wave warning text Low Still says “Asteroids Incoming” on enemy-only waves
Enemy ship system playtest Low Untested in-engine; needs a full playtest
Coin shop / IAP Low Not started
AdMob, Vungle, AppLovin Low SDK wiring pending
GDPR consent Low Required before store submission
Google Play Games leaderboards Low Not started
Android / iOS export signing Low Export templates not configured