Space Invader

A fast-paced arcade shooter where you move your spaceship, blast alien waves, and survive as long as possible.

Introduction

This is a classic Space Invaders game, and the objective is simple: defend your spaceship against waves of alien ships descending from above. You control a ship at the bottom of the screen using the joystick to move left and right, while your cannon fires automatically for nonstop action. The game features colorful enemy sprites, smooth movement, and fast-paced arcade gameplay. When an alien is hit, it disappears and you gain points, but if enemies reach the bottom of the screen, the game ends. Press Button A to restart after a game over, and try to destroy all enemy ships to win.

Programming Design

Source Code

By default, your device comes with the necessary firmware and libraries preinstalled. If you have modified or replaced the firmware or libraries, make sure the following files are present (download if need by click the link)

  • Assets: board.py, button.py, buzzer_music.py, mma7660.py, st7789.py

  • Fonts: vga_16x32.py, vga2_8x8.py

Below is the main program file along with any additional files required for this specific app.

Github Code

Main.py
import machine
from machine import Pin, ADC, SPI
import test.st7789 as st7789
import time
from test.fonts import vga1_16x32 as font2
from test.fonts import vga2_8x8 as font1

# === Display setup ===
st7789_res = 0
st7789_dc  = 1
disp_width = 240
disp_height = 240

spi_sck = machine.Pin(2)
spi_tx  = machine.Pin(3)
spi0 = machine.SPI(0, baudrate=8000000, phase=0, polarity=1, sck=spi_sck, mosi=spi_tx)

display = st7789.ST7789(spi0, disp_width, disp_height,
    reset=machine.Pin(st7789_res, machine.Pin.OUT),
    dc=machine.Pin(st7789_dc, machine.Pin.OUT),
    xstart=0, ystart=0, rotation=0)

# === Joystick ===
xAxis = ADC(Pin(29))   # joystick X-axis

# === Button A ===
btn_a = Pin(6, Pin.IN, Pin.PULL_UP)

# === Game constants ===
PLAYER_W, PLAYER_H = 20, 10
PLAYER_Y = 220
PLAYER_SPEED = 4

BULLET_W, BULLET_H = 3, 5
BULLET_SPEED = 6
bullet_active = False
bullet_x = 0
bullet_y = 0
bullet_cooldown = 200
last_shot_time = 0

ENEMY_ROWS = 4
ENEMY_COLS = 6
ENEMY_W, ENEMY_H = 20, 10
ENEMY_X_SPACING = 25
ENEMY_Y_SPACING = 20
ENEMY_START_Y = 30
ENEMY_SPEED = 1

# === Draw functions ===
def draw_player(x, y):
    display.fill_rect(x, y, PLAYER_W, PLAYER_H, st7789.WHITE)

def erase_player(x, y):
    display.fill_rect(x, y, PLAYER_W, PLAYER_H, st7789.BLACK)

def draw_bullet(x, y):
    display.fill_rect(x, y, BULLET_W, BULLET_H, st7789.RED)

def erase_bullet(x, y):
    display.fill_rect(x, y, BULLET_W, BULLET_H, st7789.BLACK)

def draw_enemy(ex, ey):
    display.fill_rect(ex, ey, ENEMY_W, ENEMY_H, st7789.GREEN)
    display.pixel(ex + ENEMY_W//2, ey - 2, st7789.GREEN)

def erase_enemy(ex, ey):
    display.fill_rect(ex, ey - 2, ENEMY_W, ENEMY_H + 2, st7789.BLACK)

def draw_score(score):
    display.fill_rect(0, 0, 100, 16, st7789.BLACK)
    display.text(font2, "Score:" + str(score), 0, 0, st7789.WHITE)

# === Game initialization ===
def init_game():
    global player_x, bullet_active, bullet_x, bullet_y, last_shot_time, enemies, enemy_dx, score
    player_x = disp_width//2 - PLAYER_W//2
    bullet_active = False
    bullet_x = 0
    bullet_y = 0
    last_shot_time = 0
    score = 0
    enemies = []
    for r in range(ENEMY_ROWS):
        for c in range(ENEMY_COLS):
            ex = c * ENEMY_X_SPACING + 20
            ey = r * ENEMY_Y_SPACING + ENEMY_START_Y
            enemies.append([ex, ey, True, ex, ey])
    enemy_dx = ENEMY_SPEED
    display.fill(st7789.BLACK)
    draw_player(player_x, PLAYER_Y)
    for ex, ey, alive, _, _ in enemies:
        draw_enemy(ex, ey)
    draw_score(score)

init_game()

# === Main game loop ===
while True:
    now = time.ticks_ms()
    
    # --- Player input ---
    x_val = xAxis.read_u16()
    joystick_dx = int((x_val - 32768)/32768 * PLAYER_SPEED)
    old_player_x = player_x
    player_x += joystick_dx
    player_x = max(0, min(disp_width - PLAYER_W, player_x))
    if player_x != old_player_x:
        erase_player(old_player_x, PLAYER_Y)
        draw_player(player_x, PLAYER_Y)
    
    # --- Shooting (constant) ---
    if not bullet_active and time.ticks_diff(now, last_shot_time) > bullet_cooldown:
        bullet_active = True
        bullet_x = player_x + PLAYER_W//2 - BULLET_W//2
        bullet_y = PLAYER_Y - BULLET_H
        last_shot_time = now
    
    # --- Bullet movement ---
    if bullet_active:
        erase_bullet(bullet_x, bullet_y)
        bullet_y -= BULLET_SPEED
        if bullet_y < 0:
            bullet_active = False
        else:
            for e in enemies:
                ex, ey, alive, _, _ = e
                if alive and ex <= bullet_x <= ex + ENEMY_W and ey <= bullet_y <= ey + ENEMY_H:
                    e[2] = False
                    erase_enemy(ex, ey)
                    bullet_active = False
                    score += 10
                    draw_score(score)
                    break
            if bullet_active:
                draw_bullet(bullet_x, bullet_y)
    
    # --- Enemy movement ---
    move_down = False
    for e in enemies:
        ex, ey, alive, prev_x, prev_y = e
        if alive:
            erase_enemy(prev_x, prev_y)
            e[0] += enemy_dx
            e[3] = e[0]
            e[4] = e[1]
            if e[0] <= 0 or e[0] >= disp_width - ENEMY_W:
                move_down = True
    
    if move_down:
        enemy_dx *= -1
        for e in enemies:
            if e[2]:
                e[1] += ENEMY_Y_SPACING // 2
                e[4] = e[1]
    
    for ex, ey, alive, _, _ in enemies:
        if alive:
            draw_enemy(ex, ey)
    
    # --- Check for game over ---
    game_over = False
    for ex, ey, alive, _, _ in enemies:
        if alive and ey + ENEMY_H >= PLAYER_Y:
            game_over = True
            break
    if game_over:
        display.fill(st7789.BLACK)
        display.text(font2, "GAME OVER", 40, 100, st7789.RED)
        display.text(font2, "Press A to restart", 10, 140, st7789.WHITE)
        while btn_a.value() != 0:
            pass
        init_game()
    
    # --- Check for win ---
    if all(not e[2] for e in enemies):
        display.fill(st7789.BLACK)
        display.text(font2, "YOU WIN!", 40, 100, st7789.GREEN)
        display.text(font1, "Press A to restart", 10, 140, st7789.WHITE)
        while btn_a.value() != 0:
            pass
        init_game()
    
    time.sleep(0.02)

Demos

Last updated

Was this helpful?