Quick answer: Use the 8-Direction behavior for player movement, rotate the player toward the mouse cursor every tick, spawn bullets at a muzzle image point on click, give enemies the Bullet or MoveTo behavior to chase the player, and manage waves with a global counter and timer. Construct 3’s built-in behaviors handle most of the physics, letting you focus on gameplay design and enemy variety.
Top-down shooters are one of the most satisfying genres to prototype quickly. Within an hour you can have a character moving, aiming at the mouse, and mowing down enemies. Construct 3 is particularly well-suited for this genre because its 8-Direction behavior, Bullet behavior, and collision system cover the core mechanics. This guide takes you from a blank project to a wave-based shooter with polish effects and a scoring system.
Player Movement with 8-Direction
Create a new project with a layout size of 2048 x 2048 — larger than the viewport so the camera can follow the player around the arena. Create a Sprite for your player (a spaceship, soldier, or simple arrow for prototyping) and add the 8-Direction behavior.
Configure the movement properties for a responsive feel:
// 8-Direction behavior properties
// Set these in the Properties panel
// Max Speed: 250 (pixels per second)
// Acceleration: 1200 (snappy response)
// Deceleration: 1000 (quick stop, not instant)
// Set angle: No (we control rotation via mouse)
// IMPORTANT: Set "Set angle" to No
// We want the player to face the mouse, not the movement direction
Set the "Set angle" property to No. This is critical — in a top-down shooter, the player’s facing direction (toward the mouse) is independent of their movement direction (WASD keys). If you leave it on, the sprite will rotate to face the direction of travel instead of the cursor.
Mouse Aiming
Add the Mouse object to your project (it is a global plugin, not per-sprite). Then add an event to rotate the player toward the cursor every tick:
// Basic mouse aiming
System: Every tick
→ Player: Set angle toward position
(Mouse.X, Mouse.Y)
// Smoother aiming with angular interpolation
// Replace the above with this for a less snappy feel:
System: Every tick
→ Player: Set angle to
anglerp(Player.Angle, angle(Player.X, Player.Y, Mouse.X, Mouse.Y), 15 * dt)
Add an image point called Muzzle on your player sprite at the tip of the weapon or the front of the ship. This is where bullets will spawn. Open the sprite editor, click the image points tool, and place the point at the correct location on the sprite.
Shooting Mechanics
Create a small Sprite for your bullet and add the Bullet behavior to it. Set its speed to 800 and check "Set angle" to Yes so it travels in the direction it was created at. Also add the Destroy Outside Layout behavior so stray bullets are cleaned up automatically.
// Basic shooting - single bullet on click
// Create instance variable on Player: "CanShoot" (boolean, default true)
// Create instance variable on Player: "FireRate" (number, default 0.15)
Mouse: On Left button clicked
Player: CanShoot = true
→ System: Create object Bullet
at (Player.ImagePointX("Muzzle"), Player.ImagePointY("Muzzle"))
→ Bullet: Set angle to Player.Angle
→ Player: Set CanShoot to false
→ System: Wait Player.FireRate seconds
→ Player: Set CanShoot to true
→ Audio: Play "shoot" not looping
// For full-auto (hold to shoot), use:
Mouse: Left button is down
// instead of "On Left button clicked"
For weapon variety, create different bullet types with varying speeds, sizes, and damage values. A shotgun fires multiple bullets in a spread pattern:
// Shotgun spread pattern
System: Repeat 5 times
→ System: Create object Bullet
at (Player.ImagePointX("Muzzle"), Player.ImagePointY("Muzzle"))
→ Bullet: Set angle to
Player.Angle + random(-15, 15)
→ Bullet: Set Bullet speed to
random(600, 900)
Enemies and AI
Create an Enemy sprite with instance variables for health, speed, and damage. For basic chase AI, use the MoveTo behavior which handles pathfinding and smooth deceleration:
// Enemy chase AI with MoveTo behavior
// Instance variables on Enemy:
// Health (number, default 3)
// Speed (number, default 120)
// Damage (number, default 1)
// ScoreValue (number, default 10)
// Move toward player every 0.5 seconds (recalculate path)
System: Every 0.5 seconds
→ Enemy: Move to (Player.X, Player.Y)
at speed Enemy.Speed
// Face the player
System: Every tick
→ Enemy: Set angle toward position
(Player.X, Player.Y)
// Bullet hits enemy
Bullet: On collision with Enemy
→ Enemy: Subtract 1 from Health
→ Bullet: Destroy
→ Enemy: Flash (white, 0.1s)
// Enemy dies when health reaches 0
Enemy: Health ≤ 0
→ System: Create object ExplosionEffect
at (Enemy.X, Enemy.Y)
→ System: Add Enemy.ScoreValue to Score
→ System: Subtract 1 from EnemiesAlive
→ Enemy: Destroy
// Enemy touches player = damage
Enemy: On collision with Player
→ System: Subtract Enemy.Damage from PlayerHealth
→ Enemy: Destroy
For variety, create different enemy types. A ranged enemy stops at a distance and fires at the player. A fast enemy has high speed but low health. A tank enemy is slow with high health and deals more damage. Use families to group all enemies for shared collision events.
Wave Spawning System
A wave system keeps gameplay escalating. Track the current wave, enemies remaining, and spawn enemies from outside the camera view:
// Wave system
// Global variables:
// WaveNumber (number, default 0)
// EnemiesAlive (number, default 0)
// WaveActive (boolean, default false)
// Score (number, default 0)
Function: On "StartNextWave"
→ System: Add 1 to WaveNumber
→ System: Set WaveActive to true
// Spawn enemies around the player, outside viewport
System: Repeat (5 + WaveNumber * 3) times
→ Local variable SpawnAngle = random(0, 360)
→ Local variable SpawnDist = random(500, 700)
→ System: Create object Enemy at
(Player.X + cos(SpawnAngle) * SpawnDist,
Player.Y + sin(SpawnAngle) * SpawnDist)
→ Enemy: Set Health to
2 + floor(WaveNumber / 3)
→ System: Add 1 to EnemiesAlive
// Check for wave completion
System: EnemiesAlive = 0
System: WaveActive = true
→ System: Set WaveActive to false
→ WaveText: Set text to "Wave " & WaveNumber & " Complete!"
→ System: Wait 3.0 seconds
→ Function: Call "StartNextWave"
Health System and UI
Create a UI layer with Parallax 0,0 for health bars and score display. Use a Sprite with its width scaled to represent health as a bar:
// Health bar update
System: Every tick
→ HealthBarFill: Set width to
(PlayerHealth / PlayerMaxHealth) * HealthBarBackground.Width
// Color the bar based on health percentage
System: PlayerHealth > PlayerMaxHealth * 0.5
→ HealthBarFill: Set effect color to rgb(80, 200, 80)
System: PlayerHealth ≤ PlayerMaxHealth * 0.5
→ HealthBarFill: Set effect color to rgb(200, 200, 50)
System: PlayerHealth ≤ PlayerMaxHealth * 0.25
→ HealthBarFill: Set effect color to rgb(220, 50, 50)
// Score display
System: Every tick
→ ScoreText: Set text to "Score: " & Score
→ WaveDisplay: Set text to "Wave " & WaveNumber
// Game over
System: PlayerHealth ≤ 0
→ System: Go to layout "GameOver"
Polish: Screen Shake and Particles
Add screen shake when the player takes damage or a large explosion occurs. Create muzzle flash particles at the muzzle point when firing. Add shell casing sprites that fly out with the Bullet behavior at a low speed and the Fade behavior to disappear after a moment. These small details transform a basic prototype into a game that feels impactful and responsive.
Related Issues
If your bullets are spawning at the wrong position after rotating the player, see Fix: Construct 3 Image Point Wrong Position After Rotation. For performance issues with many bullets on screen, check Fix: Construct 3 Performance Low FPS Lag. If enemies are overlapping and stacking on the player, see Fix: Construct 3 Enemies Overlapping and Stacking.
Screen shake and muzzle flash make every shot feel powerful.