维基少年:树莓派/树莓派构建树莓派太空侵略者控制器
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.