維基少年:樹莓派/樹莓派構建樹莓派太空侵略者控制器
Stuart Fox 的教程
感謝 Scott Bowman & Lee Robinson
公有領域
2017 年 1 月 28 日 - www.cotswoldjam.org
在本教程中,我們將構建一個迷你遊戲控制器,將其連線到樹莓派的 GPIO 引腳,並用 Python 編寫一個程式來識別按鈕按下。最後,我們將檢查並執行一個《太空侵略者》遊戲,用我們剛學到的方法修改它以與我們的控制器一起使用。

你的包中將包含以下元件
- ×6 根 M-F 跳線(針腳到插座)
- ×1 個迷你麵包板
- ×3 個瞬時按鈕


如圖所示,用按鈕的聯結器抓住它,小心地彎曲按鈕,使其與針腳成 90 度角。
慢慢地、輕輕地做這件事,以免斷裂聯結器。
將麵包板放在你的面前,如圖左所示。使用影像中顯示的數字和字母作為參考。
現在,拿起你的第一個按鈕,將它的聯結器推入 E1 和 E3 孔中,按鈕朝向你,如圖右所示。
重複此過程,將第二個按鈕的聯結器放在 E5 和 E7 孔中,將第三個按鈕的聯結器放在 E15 和 E17 孔中。
| 首先,確保你的 Pi 已關閉,並且電源線已斷開。 |

你的套件中跳線顏色並不重要 - 你放置跳線的位置很重要!
確保你連線到正確的孔和針腳。參考上面的圖示,按照以下步驟連線你的控制器

| 麵包板 | Pi GPIO |
|---|---|
| A1 線連線到 | 引腳 29(GPIO 5) |
| A3 線連線到 | 引腳 39(GND) |
| A5 線連線到 | 引腳 31(GPIO 6) |
| A7 線連線到 | 引腳 25(GND) |
| A15 線連線到 | 引腳 33(GPIO 13) |
| A17 線連線到 | 引腳 34(GND) |
現在你已經構建了一個控制器並將其連線到樹莓派的 GPIO 引腳,是時候看看它是否能工作了。在你給 Pi 通電之前,確保你的線已經由導師檢查過。
給你的樹莓派通電。從桌面選單中,選擇程式設計 - Thonny Python IDE。
現在嘗試按下每個按鈕。如果一切正常,程式將在按下每個按鈕時顯示與每個按鈕相對應的文字。
from gpiozero import Button
from signal import pause
button_1=Button(5)
button_2=Button(6)
button_3=Button(13)
def buttonone ():
print("Button 1")
def buttontwo ():
print("Button 2")
def buttonthree ():
print("Button 3")
while (True):
button_1.when_pressed = buttonone
button_2.when_pressed = buttontwo
button_3.when_pressed = buttonthree
pause ()
現在使用檔案,另存為將程式儲存為你選擇的名稱(不要忘記在末尾加上 .py),儲存在 ~/python/buttoninvaders 資料夾中。
這個程式向我們展示瞭如何使用 GPIOZero 庫在 Python 中觸發函式,它還將用於測試你新構建的控制器。
from gpiozero import Button
from signal import pause
from 行告訴計算機學習新的東西。計算機可以從其他人編寫的程式中學習;我們稱這些程式為“庫”。我們的程式需要名為 button 的函式,該函式來自 gpiozero 庫,它將用於檢測你控制器上的按鈕按下。來自 signal 庫的 pause 函式,以便我們插入暫停。
button_1=Button(5)
button_2=Button(6)
button_3=Button(13)
這些行設定了我們按鈕的名稱,並告訴程式它們連線到哪個 GPIO 引腳。所以現在程式將看到我們的三個按鈕連線到這三個 GPIO 引腳:引腳 5(I2C1 SCL)、引腳 6(GND)和 引腳 7(GPIO 4)
def buttonone ():
print("Button 1")
def buttontwo ():
print("Button 2")
def buttonthree ():
print("Button 3")
在這裡,我們正在設定名為 buttonone、buttontwo 和 buttonthree 的函式。函式包含多個指令,準備由事件或輸入觸發。在這種情況下,每個函式只執行一項任務,那就是在螢幕上列印一些文字。當我們檢視《太空侵略者》遊戲的程式碼時,我們將看到像這樣的函式在觸發時同時執行多項任務。
while True:
這個 while True: 告訴程式在一個迴圈中永遠執行。
button_1.when_pressed = buttonone
button_2.when_pressed = buttontwo
button_3.when_pressed = buttonthree
pause ()
現在我們告訴程式在按下按鈕時觸發一個函式。

現在我們的控制面板已經構建並測試完畢,是時候保衛地球免受外星人的攻擊了!在 Thonny Python IDE 中,使用檔案,開啟開啟 ~/python/buttoninvaders 資料夾中的 buttoninvaders.py 程式。
現在啟用行號:從工具選單中選擇選項,選擇編輯器選項卡,然後選中“顯示行號”旁邊的框。
檢視以下程式碼行,看看你是否認得這個遊戲是如何被修改以與我們的控制器一起工作的。
第 12 到 20 行、第 55 到 63 行和 第 451 到 484 行。這些行被註釋以解釋修改。
現在,點選
執行按鈕,我們修改後的《太空侵略者》遊戲將啟動。
- 按鈕一為左
- 按鈕二為右
- 按鈕三為射擊
您可以按下開火鍵或空格鍵 開始遊戲。
本教程中使用的太空侵略者遊戲是由李·羅賓遜使用Pygame 模組為 Python 開發的。他寫了一篇部落格文章解釋了他是如何製作的,並連結到該專案的 GitHub 儲存庫:https://leerob.io/blog/space-invaders-with-python
如果您對 1978 年由 Taito 發行的最初的太空侵略者街機遊戲感興趣,有一個專門的太空侵略者華夏公益教科書。
在華夏公益教科書上還有一個PyGame 指南,教你如何建立自己的影片遊戲。
本太空侵略者遊戲的內容在維基共享資源上的Raspberry Pi 教程按鈕入侵者資源 類別中。
本教程的原始 PDF 在維基共享資源上提供:Space Invaders Controler 教程 2.pdf
#!/usr/bin/env python
# Space Invaders
# Created by Lee Robinson
# Modified for the Cotswold Pi Jam 'Build A Space Invaders Controller' tutorial.
from pygame import *
import sys
from os.path import abspath, dirname
from random import randint, choice
# These next two lines have been added to import the libraries we need
# for the program to recognise our buttons
from gpiozero import Button
#from signal import pause
# These lines set up and assign the GPIO Pins that our buttons are connected to
button_1=Button(5)
button_2=Button(6)
button_3=Button(13)
BASE_PATH = abspath(dirname(__file__))
FONT_PATH = BASE_PATH + '/fonts/'
IMAGE_PATH = BASE_PATH + '/images/'
SOUND_PATH = BASE_PATH + '/sounds/'
# Colors (R, G, B)
WHITE = (255, 255, 255)
GREEN = (78, 255, 87)
YELLOW = (241, 255, 0)
BLUE = (80, 255, 239)
PURPLE = (203, 0, 255)
RED = (237, 28, 36)
SCREEN = display.set_mode((800, 600))
FONT = FONT_PATH + 'space_invaders.ttf'
IMG_NAMES = ['ship', 'mystery',
'enemy1_1', 'enemy1_2',
'enemy2_1', 'enemy2_2',
'enemy3_1', 'enemy3_2',
'explosionblue', 'explosiongreen', 'explosionpurple',
'laser', 'enemylaser']
IMAGES = {name: image.load(IMAGE_PATH + '{}.png'.format(name)).convert_alpha()
for name in IMG_NAMES}
class Ship(sprite.Sprite):
def __init__(self):
sprite.Sprite.__init__(self)
self.image = IMAGES['ship']
self.rect = self.image.get_rect(topleft=(375, 540))
self.speed = 5
# Added 'if button_1.is_pressed' to this function so that we can use our controler
# to move the spaceship left and right. self.rect.x sets boundries
# so that the spaceship stops at the edges of the window.
def update(self, keys, *args):
if button_1.is_pressed and self.rect.x > 10 or keys[K_LEFT] and self.rect.x > 10:
self.rect.x -= self.speed
if button_2.is_pressed and self.rect.x < 740 or keys[K_RIGHT] and self.rect.x < 740:
self.rect.x += self.speed
game.screen.blit(self.image, self.rect)
class Bullet(sprite.Sprite):
def __init__(self, xpos, ypos, direction, speed, filename, side):
sprite.Sprite.__init__(self)
self.image = IMAGES[filename]
self.rect = self.image.get_rect(topleft=(xpos, ypos))
self.speed = speed
self.direction = direction
self.side = side
self.filename = filename
def update(self, keys, *args):
game.screen.blit(self.image, self.rect)
self.rect.y += self.speed * self.direction
if self.rect.y < 15 or self.rect.y > 600:
self.kill()
class Enemy(sprite.Sprite):
def __init__(self, row, column):
sprite.Sprite.__init__(self)
self.row = row
self.column = column
self.images = []
self.load_images()
self.index = 0
self.image = self.images[self.index]
self.rect = self.image.get_rect()
self.direction = 1
self.rightMoves = 30
self.leftMoves = 30
self.moveNumber = 15
self.moveTime = 600
self.timer = time.get_ticks()
def update(self, keys, currentTime, enemies):
if currentTime - self.timer > self.moveTime:
if self.direction == 1:
maxMove = self.rightMoves + enemies.rightAddMove
else:
maxMove = self.leftMoves + enemies.leftAddMove
if self.moveNumber >= maxMove:
if self.direction == 1:
self.leftMoves = 30 + enemies.rightAddMove
elif self.direction == -1:
self.rightMoves = 30 + enemies.leftAddMove
self.direction *= -1
self.moveNumber = 0
self.rect.y += 35
elif self.direction == 1:
self.rect.x += 10
self.moveNumber += 1
elif self.direction == -1:
self.rect.x -= 10
self.moveNumber += 1
self.index += 1
if self.index >= len(self.images):
self.index = 0
self.image = self.images[self.index]
self.timer += self.moveTime
game.screen.blit(self.image, self.rect)
def load_images(self):
images = {0: ['1_2', '1_1'],
1: ['2_2', '2_1'],
2: ['2_2', '2_1'],
3: ['3_1', '3_2'],
4: ['3_1', '3_2'],
}
img1, img2 = (IMAGES['enemy{}'.format(img_num)] for img_num in
images[self.row])
self.images.append(transform.scale(img1, (40, 35)))
self.images.append(transform.scale(img2, (40, 35)))
class EnemiesGroup(sprite.Group):
def __init__(self, columns, rows):
sprite.Group.__init__(self)
self.enemies = [[0] * columns for _ in range(rows)]
self.columns = columns
self.rows = rows
self.leftAddMove = 0
self.rightAddMove = 0
self._aliveColumns = list(range(columns))
self._leftAliveColumn = 0
self._rightAliveColumn = columns - 1
self._leftKilledColumns = 0
self._rightKilledColumns = 0
def add(self, *sprites):
super(sprite.Group, self).add(*sprites)
for s in sprites:
self.enemies[s.row][s.column] = s
def is_column_dead(self, column):
for row in range(self.rows):
if self.enemies[row][column]:
return False
return True
def random_bottom(self):
random_index = randint(0, len(self._aliveColumns) - 1)
col = self._aliveColumns[random_index]
for row in range(self.rows, 0, -1):
enemy = self.enemies[row - 1][col]
if enemy:
return enemy
return None
def kill(self, enemy):
# on double hit calls twice for same enemy, so check before
if not self.enemies[enemy.row][enemy.column]:
return # nothing to kill
self.enemies[enemy.row][enemy.column] = None
isColumnDead = self.is_column_dead(enemy.column)
if isColumnDead:
self._aliveColumns.remove(enemy.column)
if enemy.column == self._rightAliveColumn:
while self._rightAliveColumn > 0 and isColumnDead:
self._rightAliveColumn -= 1
self._rightKilledColumns += 1
self.rightAddMove = self._rightKilledColumns * 5
isColumnDead = self.is_column_dead(self._rightAliveColumn)
elif enemy.column == self._leftAliveColumn:
while self._leftAliveColumn < self.columns and isColumnDead:
self._leftAliveColumn += 1
self._leftKilledColumns += 1
self.leftAddMove = self._leftKilledColumns * 5
isColumnDead = self.is_column_dead(self._leftAliveColumn)
class Blocker(sprite.Sprite):
def __init__(self, size, color, row, column):
sprite.Sprite.__init__(self)
self.height = size
self.width = size
self.color = color
self.image = Surface((self.width, self.height))
self.image.fill(self.color)
self.rect = self.image.get_rect()
self.row = row
self.column = column
def update(self, keys, *args):
game.screen.blit(self.image, self.rect)
class Mystery(sprite.Sprite):
def __init__(self):
sprite.Sprite.__init__(self)
self.image = IMAGES['mystery']
self.image = transform.scale(self.image, (75, 35))
self.rect = self.image.get_rect(topleft=(-80, 45))
self.row = 5
self.moveTime = 25000
self.direction = 1
self.timer = time.get_ticks()
self.mysteryEntered = mixer.Sound(SOUND_PATH + 'mysteryentered.wav')
self.mysteryEntered.set_volume(0.3)
self.playSound = True
def update(self, keys, currentTime, *args):
resetTimer = False
passed = currentTime - self.timer
if passed > self.moveTime:
if (self.rect.x < 0 or self.rect.x > 800) and self.playSound:
self.mysteryEntered.play()
self.playSound = False
if self.rect.x < 840 and self.direction == 1:
self.mysteryEntered.fadeout(4000)
self.rect.x += 2
game.screen.blit(self.image, self.rect)
if self.rect.x > -100 and self.direction == -1:
self.mysteryEntered.fadeout(4000)
self.rect.x -= 2
game.screen.blit(self.image, self.rect)
if self.rect.x > 830:
self.playSound = True
self.direction = -1
resetTimer = True
if self.rect.x < -90:
self.playSound = True
self.direction = 1
resetTimer = True
if passed > self.moveTime and resetTimer:
self.timer = currentTime
class Explosion(sprite.Sprite):
def __init__(self, xpos, ypos, row, ship, mystery, score):
sprite.Sprite.__init__(self)
self.isMystery = mystery
self.isShip = ship
if mystery:
self.text = Text(FONT, 20, str(score), WHITE, xpos + 20, ypos + 6)
elif ship:
self.image = IMAGES['ship']
self.rect = self.image.get_rect(topleft=(xpos, ypos))
else:
self.row = row
self.load_image()
self.image = transform.scale(self.image, (40, 35))
self.rect = self.image.get_rect(topleft=(xpos, ypos))
game.screen.blit(self.image, self.rect)
self.timer = time.get_ticks()
def update(self, keys, currentTime):
passed = currentTime - self.timer
if self.isMystery:
if passed <= 200:
self.text.draw(game.screen)
elif 400 < passed <= 600:
self.text.draw(game.screen)
elif passed > 600:
self.kill()
elif self.isShip:
if 300 < passed <= 600:
game.screen.blit(self.image, self.rect)
elif passed > 900:
self.kill()
else:
if passed <= 100:
game.screen.blit(self.image, self.rect)
elif 100 < passed <= 200:
self.image = transform.scale(self.image, (50, 45))
game.screen.blit(self.image,
(self.rect.x - 6, self.rect.y - 6))
elif passed > 400:
self.kill()
def load_image(self):
imgColors = ['purple', 'blue', 'blue', 'green', 'green']
self.image = IMAGES['explosion{}'.format(imgColors[self.row])]
class Life(sprite.Sprite):
def __init__(self, xpos, ypos):
sprite.Sprite.__init__(self)
self.image = IMAGES['ship']
self.image = transform.scale(self.image, (23, 23))
self.rect = self.image.get_rect(topleft=(xpos, ypos))
def update(self, keys, *args):
game.screen.blit(self.image, self.rect)
class Text(object):
def __init__(self, textFont, size, message, color, xpos, ypos):
self.font = font.Font(textFont, size)
self.surface = self.font.render(message, True, color)
self.rect = self.surface.get_rect(topleft=(xpos, ypos))
def draw(self, surface):
surface.blit(self.surface, self.rect)
class SpaceInvaders(object):
def __init__(self):
# It seems, in Linux buffersize=512 is not enough, use 4096 to prevent:
# ALSA lib pcm.c:7963:(snd_pcm_recover) underrun occurred
mixer.pre_init(44100, -16, 1, 4096)
init()
self.caption = display.set_caption('Space Invaders')
self.screen = SCREEN
self.background = image.load(IMAGE_PATH + 'background.jpg').convert()
self.startGame = False
self.mainScreen = True
self.gameOver = False
# Initial value for a new game
self.enemyPositionDefault = 65
# Counter for enemy starting position (increased each new round)
self.enemyPositionStart = self.enemyPositionDefault
# Current enemy starting position
self.enemyPosition = self.enemyPositionStart
def reset(self, score, lives, newGame=False):
self.player = Ship()
self.playerGroup = sprite.Group(self.player)
self.explosionsGroup = sprite.Group()
self.bullets = sprite.Group()
self.mysteryShip = Mystery()
self.mysteryGroup = sprite.Group(self.mysteryShip)
self.enemyBullets = sprite.Group()
self.reset_lives(lives)
self.enemyPosition = self.enemyPositionStart
self.make_enemies()
# Only create blockers on a new game, not a new round
if newGame:
self.allBlockers = sprite.Group(self.make_blockers(0),
self.make_blockers(1),
self.make_blockers(2),
self.make_blockers(3))
self.keys = key.get_pressed()
self.clock = time.Clock()
self.timer = time.get_ticks()
self.noteTimer = time.get_ticks()
self.shipTimer = time.get_ticks()
self.score = score
self.lives = lives
self.create_audio()
self.create_text()
self.makeNewShip = False
self.shipAlive = True
def make_blockers(self, number):
blockerGroup = sprite.Group()
for row in range(4):
for column in range(9):
blocker = Blocker(10, GREEN, row, column)
blocker.rect.x = 50 + (200 * number) + (column * blocker.width)
blocker.rect.y = 450 + (row * blocker.height)
blockerGroup.add(blocker)
return blockerGroup
def reset_lives_sprites(self):
self.life1 = Life(715, 3)
self.life2 = Life(742, 3)
self.life3 = Life(769, 3)
if self.lives == 3:
self.livesGroup = sprite.Group(self.life1, self.life2, self.life3)
elif self.lives == 2:
self.livesGroup = sprite.Group(self.life1, self.life2)
elif self.lives == 1:
self.livesGroup = sprite.Group(self.life1)
def reset_lives(self, lives):
self.lives = lives
self.reset_lives_sprites()
def create_audio(self):
self.sounds = {}
for sound_name in ['shoot', 'shoot2', 'invaderkilled', 'mysterykilled',
'shipexplosion']:
self.sounds[sound_name] = mixer.Sound(
SOUND_PATH + '{}.wav'.format(sound_name))
self.sounds[sound_name].set_volume(0.2)
self.musicNotes = [mixer.Sound(SOUND_PATH + '{}.wav'.format(i)) for i
in range(4)]
for sound in self.musicNotes:
sound.set_volume(0.5)
self.noteIndex = 0
def play_main_music(self, currentTime):
moveTime = self.enemies.sprites()[0].moveTime
if currentTime - self.noteTimer > moveTime:
self.note = self.musicNotes[self.noteIndex]
if self.noteIndex < 3:
self.noteIndex += 1
else:
self.noteIndex = 0
self.note.play()
self.noteTimer += moveTime
def create_text(self):
self.titleText = Text(FONT, 50, 'Space Invaders', WHITE, 164, 155)
self.titleText2 = Text(FONT, 25, 'Press any key to continue', WHITE,
201, 225)
self.gameOverText = Text(FONT, 50, 'Game Over', WHITE, 250, 270)
self.nextRoundText = Text(FONT, 50, 'Next Round', WHITE, 240, 270)
self.enemy1Text = Text(FONT, 25, ' = 10 pts', GREEN, 368, 270)
self.enemy2Text = Text(FONT, 25, ' = 20 pts', BLUE, 368, 320)
self.enemy3Text = Text(FONT, 25, ' = 30 pts', PURPLE, 368, 370)
self.enemy4Text = Text(FONT, 25, ' = ?????', RED, 368, 420)
self.scoreText = Text(FONT, 20, 'Score', WHITE, 5, 5)
self.livesText = Text(FONT, 20, 'Lives ', WHITE, 640, 5)
@staticmethod
def should_exit(evt):
# type: (pygame.event.EventType) -> bool
return evt.type == QUIT or (evt.type == KEYUP and evt.key == K_ESCAPE)
# New function created to contain all the things the game must do when the player presses the
# fire button.
def fire(self):
if len(self.bullets) == 0 and self.shipAlive:
if self.score < 1000:
bullet = Bullet(self.player.rect.x + 23,
self.player.rect.y + 5, -1,
15, 'laser', 'center')
self.bullets.add(bullet)
self.allSprites.add(self.bullets)
self.sounds['shoot'].play()
else:
leftbullet = Bullet(self.player.rect.x + 8,
self.player.rect.y + 5, -1,
15, 'laser', 'left')
rightbullet = Bullet(self.player.rect.x + 38,
self.player.rect.y + 5, -1,
15, 'laser', 'right')
self.bullets.add(leftbullet)
self.bullets.add(rightbullet)
self.allSprites.add(self.bullets)
self.sounds['shoot2'].play()
# This new function waits for ether our button, or the space bar to be pressed. When
# ether of those things happens it will tell thefinction 'fire' above.
def check_input(self):
if button_3.is_pressed:
self.fire()
self.keys = key.get_pressed()
for e in event.get():
if self.should_exit(e):
sys.exit()
if e.type == KEYDOWN and e.key == K_SPACE:
self.fire()
def make_enemies(self):
enemies = EnemiesGroup(10, 5)
for row in range(5):
for column in range(10):
enemy = Enemy(row, column)
enemy.rect.x = 157 + (column * 50)
enemy.rect.y = self.enemyPosition + (row * 45)
enemies.add(enemy)
self.enemies = enemies
self.allSprites = sprite.Group(self.player, self.enemies,
self.livesGroup, self.mysteryShip)
def make_enemies_shoot(self):
if (time.get_ticks() - self.timer) > 700:
enemy = self.enemies.random_bottom()
if enemy:
self.enemyBullets.add(
Bullet(enemy.rect.x + 14, enemy.rect.y + 20, 1, 5,
'enemylaser', 'center'))
self.allSprites.add(self.enemyBullets)
self.timer = time.get_ticks()
def calculate_score(self, row):
scores = {0: 30,
1: 20,
2: 20,
3: 10,
4: 10,
5: choice([50, 100, 150, 300])
}
score = scores[row]
self.score += score
return score
def create_main_menu(self):
self.enemy1 = IMAGES['enemy3_1']
self.enemy1 = transform.scale(self.enemy1, (40, 40))
self.enemy2 = IMAGES['enemy2_2']
self.enemy2 = transform.scale(self.enemy2, (40, 40))
self.enemy3 = IMAGES['enemy1_2']
self.enemy3 = transform.scale(self.enemy3, (40, 40))
self.enemy4 = IMAGES['mystery']
self.enemy4 = transform.scale(self.enemy4, (80, 40))
self.screen.blit(self.enemy1, (318, 270))
self.screen.blit(self.enemy2, (318, 320))
self.screen.blit(self.enemy3, (318, 370))
self.screen.blit(self.enemy4, (299, 420))
for e in event.get():
if self.should_exit(e):
sys.exit()
if e.type == KEYUP:
self.startGame = True
self.mainScreen = False
def update_enemy_speed(self):
if len(self.enemies) <= 10:
for enemy in self.enemies:
enemy.moveTime = 400
if len(self.enemies) == 1:
for enemy in self.enemies:
enemy.moveTime = 200
def check_collisions(self):
collidedict = sprite.groupcollide(self.bullets, self.enemyBullets,
True, False)
if collidedict:
for value in collidedict.values():
for currentSprite in value:
self.enemyBullets.remove(currentSprite)
self.allSprites.remove(currentSprite)
enemiesdict = sprite.groupcollide(self.bullets, self.enemies,
True, False)
if enemiesdict:
for value in enemiesdict.values():
for currentSprite in value:
self.enemies.kill(currentSprite)
self.sounds['invaderkilled'].play()
score = self.calculate_score(currentSprite.row)
explosion = Explosion(currentSprite.rect.x,
currentSprite.rect.y,
currentSprite.row, False, False,
score)
self.explosionsGroup.add(explosion)
self.allSprites.remove(currentSprite)
self.enemies.remove(currentSprite)
self.gameTimer = time.get_ticks()
break
mysterydict = sprite.groupcollide(self.bullets, self.mysteryGroup,
True, True)
if mysterydict:
for value in mysterydict.values():
for currentSprite in value:
currentSprite.mysteryEntered.stop()
self.sounds['mysterykilled'].play()
score = self.calculate_score(currentSprite.row)
explosion = Explosion(currentSprite.rect.x,
currentSprite.rect.y,
currentSprite.row, False, True,
score)
self.explosionsGroup.add(explosion)
self.allSprites.remove(currentSprite)
self.mysteryGroup.remove(currentSprite)
newShip = Mystery()
self.allSprites.add(newShip)
self.mysteryGroup.add(newShip)
break
bulletsdict = sprite.groupcollide(self.enemyBullets, self.playerGroup,
True, False)
if bulletsdict:
for value in bulletsdict.values():
for playerShip in value:
if self.lives == 3:
self.lives -= 1
self.livesGroup.remove(self.life3)
self.allSprites.remove(self.life3)
elif self.lives == 2:
self.lives -= 1
self.livesGroup.remove(self.life2)
self.allSprites.remove(self.life2)
elif self.lives == 1:
self.lives -= 1
self.livesGroup.remove(self.life1)
self.allSprites.remove(self.life1)
elif self.lives == 0:
self.gameOver = True
self.startGame = False
self.sounds['shipexplosion'].play()
explosion = Explosion(playerShip.rect.x, playerShip.rect.y,
0, True, False, 0)
self.explosionsGroup.add(explosion)
self.allSprites.remove(playerShip)
self.playerGroup.remove(playerShip)
self.makeNewShip = True
self.shipTimer = time.get_ticks()
self.shipAlive = False
if sprite.groupcollide(self.enemies, self.playerGroup, True, True):
self.gameOver = True
self.startGame = False
sprite.groupcollide(self.bullets, self.allBlockers, True, True)
sprite.groupcollide(self.enemyBullets, self.allBlockers, True, True)
sprite.groupcollide(self.enemies, self.allBlockers, False, True)
def create_new_ship(self, createShip, currentTime):
if createShip and (currentTime - self.shipTimer > 900):
self.player = Ship()
self.allSprites.add(self.player)
self.playerGroup.add(self.player)
self.makeNewShip = False
self.shipAlive = True
def create_game_over(self, currentTime):
self.screen.blit(self.background, (0, 0))
passed = currentTime - self.timer
if passed < 750:
self.gameOverText.draw(self.screen)
elif 750 < passed < 1500:
self.screen.blit(self.background, (0, 0))
elif 1500 < passed < 2250:
self.gameOverText.draw(self.screen)
elif 2250 < passed < 2750:
self.screen.blit(self.background, (0, 0))
elif passed > 3000:
self.mainScreen = True
for e in event.get():
if self.should_exit(e):
sys.exit()
def main(self):
while True:
if self.mainScreen:
self.reset(0, 3, True)
self.screen.blit(self.background, (0, 0))
self.titleText.draw(self.screen)
self.titleText2.draw(self.screen)
self.enemy1Text.draw(self.screen)
self.enemy2Text.draw(self.screen)
self.enemy3Text.draw(self.screen)
self.enemy4Text.draw(self.screen)
self.create_main_menu()
elif self.startGame:
if len(self.enemies) == 0:
currentTime = time.get_ticks()
if currentTime - self.gameTimer < 3000:
self.screen.blit(self.background, (0, 0))
self.scoreText2 = Text(FONT, 20, str(self.score),
GREEN, 85, 5)
self.scoreText.draw(self.screen)
self.scoreText2.draw(self.screen)
self.nextRoundText.draw(self.screen)
self.livesText.draw(self.screen)
self.livesGroup.update(self.keys)
self.check_input()
if currentTime - self.gameTimer > 3000:
# Move enemies closer to bottom
self.enemyPositionStart += 35
self.reset(self.score, self.lives)
self.gameTimer += 3000
else:
currentTime = time.get_ticks()
self.play_main_music(currentTime)
self.screen.blit(self.background, (0, 0))
self.allBlockers.update(self.screen)
self.scoreText2 = Text(FONT, 20, str(self.score), GREEN,
85, 5)
self.scoreText.draw(self.screen)
self.scoreText2.draw(self.screen)
self.livesText.draw(self.screen)
self.check_input()
self.allSprites.update(self.keys, currentTime,
self.enemies)
self.explosionsGroup.update(self.keys, currentTime)
self.check_collisions()
self.create_new_ship(self.makeNewShip, currentTime)
self.update_enemy_speed()
if len(self.enemies) > 0:
self.make_enemies_shoot()
elif self.gameOver:
currentTime = time.get_ticks()
# Reset enemy starting position
self.enemyPositionStart = self.enemyPositionDefault
self.create_game_over(currentTime)
display.update()
self.clock.tick(60)
if not self.startGame and button_3.is_pressed:
self.startGame = True
self.mainScreen = False
if __name__ == '__main__':
game = SpaceInvaders()
game.main()
注意: 原始碼根據 GitHub 儲存庫中的w:MIT 許可證 提供:https://github.com/leerob/space-invaders/blob/master/LICENSE
The MIT License (MIT)
Copyright (c) 2014-2019 Lee Robinson
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
