Utility Bar

Space Dodger - Survival Game for RP2350 / Raspberry Pi Pico

Ethan Zaitchik |

Table of Contents

    At Zaitronics, we've recently built an upgraded version of a Raspberry Pi Pico 2, called Nexus RP2350 LiPo, with the intension of being a drop in replacement for any existing Pico projects. To demonstrate the board's capabilities, I've designed a simple Space Dodger game which requires very few parts most makers should have. For this version, we used a 1.3 inch OLED from Waveshare, but any similar small display should work with slight adjustments in the code.

    Parts List:

    Wiring (if you are not using the Nexus + OLED stack)

    OLED Pin RP2350 / Pico Pin
    VCC 3.3 V
    GND GND
    SCK GP10
    MOSI GP11
    DC GP8
    RST GP12
    CS GP9
    Button RP2350 / Pico Pin
    Button A (UP GP15 + GND (internal pull-up used)
    Button B DOWN GP17 + GND (internal pull-up used)

    How to Play

    • Button A (GP15) → move ship UP
    • Button B (GP17) → move ship DOWN
    • Avoid the white squares coming from the right
    • Your score is survival time + 10 points per dodged ball
    • The game gets faster the longer you survive

    Tweaking Difficulty

    Variable Effect Suggested range
    ball_speed = 1.8 Starting speed 1.2 – 2.5
    speed_increment How quickly it accelerates 0.0005 – 0.002
    Spawn chance line How often new balls appear 0.02 – 0.06
    Ball size in loop 6×6 is good visibility 4–8

    Complete Ready-to-Flash Code (MicroPython)

    Copy the entire code below into Thonny (or any MicroPython IDE) and save as main.py on your board.

    from machine import Pin, SPI
    import framebuf, time, random
    
    # Display pins
    DC = 8
    RST = 12
    MOSI = 11
    SCK = 10
    CS = 9
    
    # Button pins
    keyA = Pin(15, Pin.IN, Pin.PULL_UP)
    keyB = Pin(17, Pin.IN, Pin.PULL_UP)
    
    # OLED driver class (same as yours)
    class OLED_1inch3(framebuf.FrameBuffer):
        def __init__(self):
            self.width = 128
            self.height = 64
            self.rotate = 0
            self.cs = Pin(CS, Pin.OUT)
            self.rst = Pin(RST, Pin.OUT)
            self.cs(1)
            self.spi = SPI(1, 20000000, polarity=0, phase=0, sck=Pin(SCK), mosi=Pin(MOSI), miso=None)
            self.dc = Pin(DC, Pin.OUT)
            self.dc(1)
            self.buffer = bytearray(self.height * self.width // 8)
            super().__init__(self.buffer, self.width, self.height, framebuf.MONO_HMSB)
            self.init_display()
            self.white = 0xffff
            self.balck = 0x0000
    
        def write_cmd(self, cmd):
            self.cs(1)
            self.dc(0)
            self.cs(0)
            self.spi.write(bytearray([cmd]))
            self.cs(1)
    
        def write_data(self, buf):
            self.cs(1)
            self.dc(1)
            self.cs(0)
            self.spi.write(bytearray([buf]))
            self.cs(1)
    
        def init_display(self):
            self.rst(1)
            time.sleep(0.001)
            self.rst(0)
            time.sleep(0.01)
            self.rst(1)
            for cmd in (
                0xAE, 0x00, 0x10, 0xB0, 0xDC, 0x00, 0x81, 0x6F, 0x21,
                0xA1 if self.rotate == 180 else 0xA0,
                0xC0, 0xA4, 0xA6, 0xA8, 0x3F, 0xD3, 0x60, 0xD5, 0x41,
                0xD9, 0x22, 0xDB, 0x35, 0xAD, 0x8A, 0xAF
            ):
                self.write_cmd(cmd)
    
        def show(self):
            self.write_cmd(0xB0)
            for page in range(0, 64):
                self.column = page if self.rotate == 180 else 63 - page
                self.write_cmd(0x00 + (self.column & 0x0F))
                self.write_cmd(0x10 + (self.column >> 4))
                for num in range(0, 16):
                    self.write_data(self.buffer[page * 16 + num])
    
    # Initialize display
    OLED = OLED_1inch3()
    
    # Ball list: each ball is [x, y]
    balls = []
    
    # Spaceship position
    ship_x = 0
    ship_y = 28
    ship_w = 6
    ship_h = 6
    
    # Draw spaceship
    def draw_ship(x, y, scale=2):
        """
        Draw the spaceship pattern at (x, y)
        scale: how many screen pixels per pattern pixel
        """
        pattern = [
            "00000000000",
            "000xxx00000",
            "0000xxxx000",
            "xxxxxxxxx00",
            "00xxx00xxx0",
            "0xxxx000xxx",
            "00xxx00xxx0",
            "xxxxxxxxx00",
            "0000xxxx000",
            "000xxx00000",
            "00000000000",
        ]
    
        for row_idx, row in enumerate(pattern):
            for col_idx, pixel in enumerate(row):
                if pixel == "x":
                    OLED.fill_rect(x + col_idx*scale, y + row_idx*scale, scale, scale, OLED.white)
    
    import time
    
    # Game variables
    score = 0
    ball_speed = 2      # initial speed of balls
    speed_increment = 0.05  # increase speed over time
    
    start_time = time.ticks_ms()  # start timer
    
    # Main loop
    while True:
        OLED.fill(0)  # clear screen
    
        # Update score based on elapsed time
        elapsed_ms = time.ticks_diff(time.ticks_ms(), start_time)
        score = elapsed_ms // 100  # roughly increments every 0.1 second
    
        # Display score
        OLED.text("Score: {}".format(score), 0, 0)
    
        # Move spaceship up/down
        if keyA.value() == 0:  # up
            ship_y = max(0, ship_y - 2)
        if keyB.value() == 0:  # down
            ship_y = min(OLED.height - ship_h*2, ship_y + 2)  # scale=2
    
        # Randomly spawn a new ball on the right
        if random.random() < 0.05:  # reduce spawn rate for balance
            y = random.randint(0, OLED.height - 4)
            balls.append([127, y])
    
        # Draw and update balls
        new_balls = []
        for ball in balls:
            x, y = ball
            OLED.fill_rect(x, y, 4, 4, OLED.white)  # 4x4 ball
            x -= int(ball_speed)  # move left
            if x > 0:
                new_balls.append([x, y])
        balls = new_balls
    
        # Gradually increase speed
        ball_speed += speed_increment / 50  # slow acceleration
    
        # Draw spaceship
        draw_ship(ship_x, ship_y)
    
        OLED.show()
        time.sleep(0.05)

    Additional Resources

    • Full Driver & Examples: Check the Waveshare wiki for the original MicroPython library: https://www.waveshare.com/wiki/Pico-OLED-1.3 (includes C/C++ too).
    • Customization Ideas: Modify the ship pattern, add a buzzer on GP16 for collision sounds, or implement high-score saving to onboard flash.

    Leave a comment

    // Table of contents