Space Invaders on RasPi with OLED and Capacitive Touch

This project implements Capacitive Touch to a Space Invaders game that runs on a Raspberry Pi.

BeginnerFull instructions provided1 hour2,777
Space Invaders on RasPi with OLED and Capacitive Touch

Things used in this project

Hardware components

Raspberry Pi 2 Model B
Raspberry Pi 2 Model B
With Power Supply and SD card loaded with Raspbian
Γ—1
OD01
XinaBox OD01
Γ—1
BR01
XinaBox BR01
Γ—1
XinaBox SH01
Γ—1
XC10
XinaBox XC10
Γ—1

Story

Read more

Code

invaders.py

Python
This is the code, originally from Richard Hull's LUMA examples:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (c) 2014-17 Richard Hull and contributors
# See LICENSE.rst for details.
# PYTHON_ARGCOMPLETE_OK
# Cap Touch by XinaBox 
"""
Space Invaders demo.

Ported from:
https://gist.github.com/TheRayTracer/dd12c498e3ecb9b8b47f#file-invaders-py
"""
import os.path
import time
import random
from demo_opts import get_device
from PIL import Image
from luma.core.render import canvas
from luma.core.sprite_system import framerate_regulator

# Cap Touch modification Start
import sys
import smbus2
bus = smbus2.SMBus(1)
DEVICE_ADDRESS = 0x28
CAP_TOUCH_GENERAL_STATUS = 0x02
CAP_TOUCH_SENSOR_INPUT_STATUS = 0x03
bus.write_byte_data(DEVICE_ADDRESS, 0x27, 0x0)
bus.write_byte_data(DEVICE_ADDRESS, 0x21, 0x39)
bus.write_byte_data(DEVICE_ADDRESS, 0x0, 0x0)
# Cap Touch modification End


arrow = [0x04, 0x02, 0x01, 0x02, 0x04]
alien1 = [0x4C, 0x1A, 0xB6, 0x5F, 0x5F, 0xB6, 0x1A, 0x4C]
alien2 = [0x18, 0xFD, 0xA6, 0x3C, 0x3C, 0xA6, 0xFD, 0x18]
alien3 = [0xFC, 0x98, 0x35, 0x7E, 0x7E, 0x35, 0x98, 0xFC]
ARMY_SIZE_ROWS = 2
ARMY_SIZE_COLS = 6


class bullet(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.alive = False

    def render(self, draw):
        if self.alive:
            draw.line((self.x, self.y, self.x, self.y + 2), fill="white")

    def reset(self, x, y):
        self.x = x
        self.y = y
        self.alive = True
        return

    def update(self, direction):
        if self.alive:
            self.y = self.y + (direction * 4)
            if self.y < 10:
                self.alive = False


class player(object):
    def __init__(self):
        self.x = 48
        self.y = 54
        self.bullets = [bullet(0, 0) for _ in range(4)]

    def render(self, draw):
        for i in range(len(arrow)):
            line = arrow[i]
            for j in range(3):
                if line & 0x1:
                    draw.point((self.x - 2 + i, self.y + j), fill="white")
                line >>= 1

        for bullet in self.bullets:
            bullet.render(draw)

    def update(self, direction):
        t = self.x + (direction * 2)
        if t > 4 and t < 92:
            self.x = t
        for bullet in self.bullets:
            bullet.update(-1)

    def shoot(self):
        for bullet in self.bullets:
            if not bullet.alive:
                bullet.reset(self.x, self.y)
                break


class invader(object):
    def __init__(self, minx, maxx, x, y):
        self.x = x
        self.y = y
        self._direction = 1
        self.alive = True
        self.score = 10
        self._minx = minx
        self._maxx = maxx
        return

    def render(self, draw):
        if self.alive:
            for i in range(len(alien2)):
                line = alien2[i]
                for j in range(8):
                    if line & 0x1:
                        draw.point((self.x - 4 + i, self.y - 4 + j), "green")
                    line >>= 1

    def update(self):
        invaded = False
        if self.alive:
            t = self.x + self._direction
            if t > self._minx and t < self._maxx:
                self.x = self.x + self._direction
            else:
                self._direction = self._direction * -1
                self.y = self.y + 2
                if self.y > 44:
                    invaded = True
        return invaded


class army(object):
    def __init__(self):
        self.invaded = False
        self.invaders = []
        for i in range(ARMY_SIZE_ROWS):
            for j in range(ARMY_SIZE_COLS):
                minx = 4 + (j * 12)
                maxx = 30 + (j * 12)
                x = 4 + (j * 12)
                y = 14 + (i * 12)
                self.invaders.append(invader(minx, maxx, x, y))

    def render(self, draw):
        for invader in self.invaders:
            invader.render(draw)

    def update(self, bullets):
        for invader in self.invaders:
            if invader.update():
                self.invaded = True

        for invader in self.invaders:
            if invader.alive:
                for bullet in bullets:
                    if bullet.alive:
                        t = (invader.x - bullet.x) * (invader.x - bullet.x) + (invader.y - bullet.y) * (invader.y - bullet.y)
                        # if point is in circle
                        if t < 25:  # 5 * 5 = r * r
                            invader.alive = False
                            bullet.alive = False

    def size(self):
        size = 0
        for invader in self.invaders:
            if invader.alive:
                size += 1
        return size

    def score(self):
        score = 0
        for invader in self.invaders:
            if not invader.alive:
                score += invader.score
        return score


def ai_logic_shoot(army, plyr):
    for invader in army.invaders:
        if invader.alive:
            if plyr.x > (invader.x - 2) and plyr.x < (invader.x + 2):
                if random.random() < 0.75:
                    plyr.shoot()
                    return


def ai_logic_move(army, plyr, rows):
    for i in rows:
        invader = army.invaders[i]
        if invader.alive:
            if plyr.x < invader.x:
                plyr.update(1)
                return
            elif plyr.x > invader.x:
                plyr.update(-1)
                return
        i += 1


if __name__ == '__main__':

    device = get_device()

    if device.width < 96 or device.height < 64:
        raise ValueError("Unsupported mode: {0}x{1}".format(device.width, device.height))

    regulator = framerate_regulator()
    plyr = player()
    army = army()
    rows = random.sample(range(12), 12)

    img_path = os.path.abspath(os.path.join(os.path.dirname(__file__), 'images', 'splash.bmp'))
    splash = Image.open(img_path) \
        .transform((device.width, device.height), Image.AFFINE, (1, 0, 0, 0, 1, 0), Image.BILINEAR) \
        .convert(device.mode)

    try:
        # Double buffering in pygame?
        device.display(splash)
        device.display(splash)

        time.sleep(3)
        device.clear()

        while not army.invaded and army.size() > 0:
            with regulator:
                with canvas(device) as draw:
                    draw.line((0, 61, 95, 61), fill="white")
                    draw.line((0, 63, 95, 63), fill="white")

                    # Cap Touch Code Start
                    touch_type = bus.read_byte_data(DEVICE_ADDRESS,CAP_TOUCH_GENERAL_STATUS)
                    if touch_type == 0x01:
                        button = bus.read_byte_data(DEVICE_ADDRESS,CAP_TOUCH_SENSOR_INPUT_STATUS)
                        if button == 0x01:
                            sys.exit("User exit!")
                        if button == 0x20:
                            plyr.update(1)
                        if button == 0x08:
                            plyr.shoot()
                            plyr.update(0)
                        if button == 0x10:
                            plyr.update(-1)
                        bus.write_byte_data(DEVICE_ADDRESS, 0x0, 0x0)
                    # ai_logic_shoot(army, plyr)
                    # ai_logic_move(army, plyr, rows)
                    # Cap Touch Code End

                    army.update(plyr.bullets)

                    army.render(draw)
                    plyr.render(draw)

                    draw.text((8, 0), text="Score: {0}".format(army.score()), fill="blue")

        # Double buffering in pygame?
        for i in range(2):
            with canvas(device) as draw:
                if army.size() == 0:
                    draw.text((27, 28), text="Victory", fill="blue")
                else:
                    draw.text((30, 28), text="Defeat", fill="red")

        time.sleep(5)
    except KeyboardInterrupt:
        pass

Credits

Comments

Please log in or sign up to comment.