环境 python3+pygame

本文示例源码只用于学习研究,不包含第三方模块,如果你想运行的话需要安装pygame模块。

  • 安装pygame模块

python -m pip install --user pygame-1.9.4-cp37-cp37m-win_amd64.whl

  • 项目结构

可以自行替换资源文件,文件夹和文件名保持不变就行了


主循环:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# -*- encoding: utf-8 -*-
'''
@file :alienInvasion.py
@Time :2021-01-04 02:04:45
@Author :Karcyril
@Software :Visual Studio Code
'''

import pygame
import sys
import os
import gameFunctions
import gameStatistics
import button
import scoreBoard
from setting import Setting
from ship import Ship
from pygame.sprite import Group
from alien import Alien


def runGame():
pygame.init()
aiSet = Setting()
# 创建一个统计实例
gameStatis = gameStatistics.gameStatistics(aiSet)
screen = pygame.display.set_mode((aiSet.screenWidth, aiSet.screenHeight))
ship = Ship(aiSet, screen)
pygame.display.set_caption('Alien Invasion')
playButton = button.Button(aiSet, screen, 'Play')
sb = scoreBoard.ScoreBoard(aiSet, gameStatis, screen)
bullets = Group()
aliens = Group()
gameFunctions.createFleet(aiSet, screen, ship, aliens)
while True:
gameFunctions.checkEvent(
aiSet, gameStatis, sb, screen, ship, bullets, aliens, playButton)
# 当gameActive为真时更新飞船,子弹和外星人
if gameStatis.gameActive:
ship.update()
gameFunctions.updateBullets(
aiSet, gameStatis, sb, screen, ship, bullets, aliens)
gameFunctions.updateAliens(
aiSet, gameStatis, screen, ship, bullets, aliens)
gameFunctions.updateScreen(
aiSet, gameStatis, sb, screen, ship, bullets, aliens, playButton)


runGame()


子弹类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# -*- encoding: utf-8 -*-
'''
@file :bullet.py
@Time :2021-01-04 02:25:41
@Author :Karcyril
@Software :Visual Studio Code
'''

if __name__ != '__main__':
import pygame
from pygame.sprite import Sprite

class Bullet(Sprite):
def __init__(self, aiSet, screen, ship):
# 继承Sprite的构造函数
super().__init__()
self.screen = screen
# 在屏幕0,0处创建一个矩形(子弹),下x,y坐标为矩形的长宽
self.rect = pygame.Rect(
0, 0, aiSet.bulletWidth, aiSet.bulletHeight)
# 设置子弹的centerx 和top 位置
self.rect.centerx = ship.rect.centerx
self.rect.top = ship.rect.top
# 取得子弹矩形的y坐标,转换为float储存,改变子弹的位置只需要2个y坐标
self.y = float(self.rect.y)
self.color = aiSet.bulletColor
self.speedFactor = aiSet.bulletSpeedFactor
# Group的实例用于操作sprite编组,每个sprite派生类必须有同名的update方法

def update(self):
self.y -= self.speedFactor
self.rect.y = self.y
#

def drawBullet(self):
pygame.draw.rect(self.screen, self.color, self.rect)


外星人类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# -*- encoding: utf-8 -*-
'''
@file :alien.py
@Time :2021-01-04 02:24:01
@Author :Karcyril
@Software :Visual Studio Code
'''


if __name__ != '__main__':
import pygame
from pygame.sprite import Sprite

class Alien(Sprite):
def __init__(self, aiSet, screen):
super().__init__()
self.screen = screen
self.aiSet = aiSet
self.image = pygame.image.load(r'.\images\bird.jpg')
self.rect = self.image.get_rect()
self.rect.x = self.rect.width
self.rect.y = self.rect.height
self.x = float(self.rect.x)

def blitMe(self):
self.screen.blit(self.image, self.rect)

# 检查是否达到屏幕边缘
def checkEdge(self):
screenRect = self.screen.get_rect()
if self.rect.right >= screenRect.right:
return True
elif self.rect.left <= screenRect.left:
return True
return False

def update(self):
self.x += self.aiSet.alienSpeedFactor*self.aiSet.fleetDirection
self.rect.x = self.x


按键类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# -*- encoding: utf-8 -*-
'''
@file :button.py
@Time :2021-01-04 02:25:33
@Author :Karcyril
@Software :Visual Studio Code
'''


if __name__ != '__main__':
import pygame.font

class Button():
def __init__(self, aiSet, screen, msg):
self.screen = screen
self.screenRect = screen.get_rect()
self.width, self.height = 200, 50
self.buttonColor = (0, 255, 0)
self.textColor = (255, 255, 255)
self.font = pygame.font.SysFont(None, 48)
self.rect = pygame.Rect(0, 0, self.width, self.height)
self.rect.center = self.screenRect.center
self.preMsg(msg)

def preMsg(self, msg):
self.msgImage = self.font.render(
msg, True, self.textColor, self.buttonColor)
self.msgImageRect = self.msgImage.get_rect()
self.msgImageRect.center = self.rect.center

def drawButton(self):
self.screen.fill(self.buttonColor, self.rect)
self.screen.blit(self.msgImage, self.msgImageRect)


函数模块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
# -*- encoding: utf-8 -*-
'''
@file :gameFunctions.py
@Time :2021-01-04 02:24:53
@Author :Karcyril
@Software :Visual Studio Code
'''


if __name__ != '__main__':
import sys
import pygame
import bullet
from alien import Alien
from time import sleep

def checkKeyDownEvent(event, aiSet, gameStatis, sb, screen, ship, bullets, aliens, playButton):
if event.key == pygame.K_RIGHT:
ship.moveRight = True
elif event.key == pygame.K_LEFT:
ship.moveLeft = True
elif event.key == pygame.K_SPACE and gameStatis.gameActive:
fireBullet(aiSet, screen, ship, bullets)
elif event.key == pygame.K_q:
sys.exit(0)
elif event.key == pygame.K_p:
startGame(aiSet, gameStatis, sb, screen,
ship, bullets, aliens, playButton)

def checkKeyUpEvent(event, ship):
if event.key == pygame.K_RIGHT:
ship.moveRight = False
elif event.key == pygame.K_LEFT:
ship.moveLeft = False

# 检测事件
def checkEvent(aiSet, gameStatis, sb, screen, ship, bullets, aliens, playButton):
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit(0)
elif event.type == pygame.MOUSEBUTTONDOWN:
mouseX, mouseY = pygame.mouse.get_pos()
checkPlayButton(aiSet, gameStatis, sb, screen, ship, bullets, aliens,
playButton, mouseX, mouseY)
elif event.type == pygame.KEYDOWN:
checkKeyDownEvent(event, aiSet, gameStatis, sb, screen, ship, bullets,
aliens, playButton)
elif event.type == pygame.KEYUP:
checkKeyUpEvent(event, ship)

# 更新屏幕
def updateScreen(aiSet, gameStatis, sb, screen, ship, bullets, aliens, playButton):
screen.fill(aiSet.mainBgColor)
# bullets储存了所有子弹精灵实例,重绘所有子弹
for bulletElem in bullets.sprites():
bulletElem.drawBullet()
ship.blitMe()
aliens.draw(screen)
if not gameStatis.gameActive:
playButton.drawButton()
sb.showScore()
pygame.display.flip()

def updateBullets(aiSet, gameStatis, sb, screen, ship, bullets, aliens):
# 对编组调用update() 时, 编组将自动对其中的每个精灵调用update()
bullets.update()
# 如果子弹矩形的bottom小于等于0则删除此条目,因为会改变group,需要用group的副本循环
for bullet in bullets.copy():
if bullet.rect.bottom <= 0:
bullets.remove(bullet)
checkBulletAlienCollision(
aiSet, gameStatis, sb, screen, ship, bullets, aliens)

# 检测子弹和外星人碰撞
def checkBulletAlienCollision(aiSet, gameStatis, sb, screen, ship, bullets, aliens):
collisions = pygame.sprite.groupcollide(bullets, aliens, False, True)
# sprite.groupcollide返回一个字典,字典每个元素都是一个列表
if collisions:
for aliens in collisions.values():
gameStatis.score += aiSet.alienPoint*len(aliens)
sb.preScore()
# 检测当前分数是否高于最高分
checkHighScore(gameStatis, sb)
# 如果aliens为空则创建新的fleet
if len(aliens) == 0:
# 创建fleet之前清空所有bullet精灵
bullets.empty()
aiSet.increaseSpeed()
gameStatis.level += 1
sb.preLevel()
createFleet(aiSet, screen, ship, aliens)

def fireBullet(aiSet, screen, ship, bullets):
# 限制子弹数量
if len(bullets) < aiSet.bulletAllowed:
newBullet = bullet.Bullet(aiSet, screen, ship)
bullets.add(newBullet)

# 计算一行可以容纳多少外星人
def getNumberAlienX(aiSet, alienWidth):
availableSpace = aiSet.screenWidth-alienWidth*2
quantityAlienX = int(availableSpace/(alienWidth*2))
return quantityAlienX

# 计算屏幕能容纳多少行外星人
def getNumberAlienY(aiSet, shipHeight, alienHeight):
availableSpaceY = aiSet.screenHeight-alienHeight*3-shipHeight
quantityAlienY = int(availableSpaceY/(alienHeight*2))
return quantityAlienY

# 创建外星人实例并添加到group
def createAlien(aiSet, screen, aliens, alienNumberX, alienNumberY):
alien = Alien(aiSet, screen)
alien.x = alien.rect.width+2*alien.rect.width*alienNumberX
alien.rect.x = alien.x
alien.rect.y = alien.rect.height+2*alien.rect.height*alienNumberY
aliens.add(alien)

# 通过统计的row,column创建多行alien
def createFleet(aiSet, screen, ship, aliens):
alien = Alien(aiSet, screen)
quantityAlienX = getNumberAlienX(aiSet, alien.rect.width)
quantityAlienY = getNumberAlienY(
aiSet, ship.rect.height, alien.rect.height)
for alienNumberY in range(quantityAlienY):
for alienNumberX in range(quantityAlienX):
createAlien(aiSet, screen, aliens, alienNumberX, alienNumberY)

# 改变飞船群的方向,并向下移动
def changeFleetDirection(aiSet, aliens):
for alien in aliens.sprites():
alien.rect.y += aiSet.fleetDropSpeed
# 将fleetDirection取反,改变x移动方向
aiSet.fleetDirection *= -1

def checkFleetEdge(aiSet, aliens):
for alien in aliens.sprites():
if alien.checkEdge():
changeFleetDirection(aiSet, aliens)
break

# 更新飞船
def updateAliens(aiSet, gameStatis, screen, ship, bullets, aliens):
aliens.update()
checkFleetEdge(aiSet, aliens)
if pygame.sprite.spritecollideany(ship, aliens):
shipHit(aiSet, gameStatis, screen, ship, bullets, aliens)
checkAliensBottom(aiSet, gameStatis, screen, ship, bullets, aliens)

# 处理外星人和飞船碰撞
def shipHit(aiSet, gameStatis, screen, ship, bullets, aliens):
if gameStatis.shipLeft > 0:
gameStatis.shipLeft -= 1
aliens.empty()
bullets.empty()
createFleet(aiSet, screen, ship, aliens)
ship.shipCenter()
sleep(0.5)
else: # 如果飞船储备用光将gameActive设置为False
gameStatis.gameActive = False
pygame.mouse.set_visible(True)

# 检测外星人是否到达底部
def checkAliensBottom(aiSet, gameStatis, screen, ship, bullets, aliens):
screenRect = screen.get_rect()
for alien in aliens.sprites():
if alien.rect.bottom >= screenRect.bottom:
shipHit(aiSet, gameStatis, screen, ship, bullets, aliens)
break

# 检查鼠标是否在play按钮的rect内按下
def checkPlayButton(aiSet, gameStatis, sb, screen, ship, bullets, aliens,
playButton, mouseX, mouseY):
if not gameStatis.gameActive and playButton.rect.collidepoint(mouseX, mouseY):
startGame(aiSet, gameStatis, sb, screen,
ship, bullets, aliens, playButton)

# 开始游戏时初始化所有数据
def startGame(aiSet, gameStatis, sb, screen, ship, bullets, aliens, playButton):
pygame.mouse.set_visible(False)
gameStatis.resetStats()
gameStatis.gameActive = True
aiSet.initializeDynamicSetting()
bullets.empty()
aliens.empty()
ship.shipCenter()
createFleet(aiSet, screen, ship, aliens)
sb.preScore()
sb.preHighScore()
sb.preLevel()

def checkHighScore(gameStatis, sb):
if gameStatis.score > gameStatis.highScore:
gameStatis.highScore = gameStatis.score
sb.preHighScore()


检测游戏状态:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# -*- encoding: utf-8 -*-
'''
@file :gameStatistics.py
@Time :2021-01-04 02:24:46
@Author :Karcyril
@Software :Visual Studio Code
'''


if __name__ != '__main__':
class gameStatistics():
def __init__(self, aiSet):
self.aiSet = aiSet
self.resetStats()
self.gameActive = False
self.highScore = 0

def resetStats(self):
# 初始化飞船限制
self.shipLeft = self.aiSet.shipLimit
self.score = 0
self.level = 1


记分牌类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# -*- encoding: utf-8 -*-
'''
@file :scoreBoard.py
@Time :2021-01-04 02:24:37
@Author :Karcyril
@Software :Visual Studio Code
'''


if __name__ != '__main__':
import pygame.font

class ScoreBoard():
def __init__(self, aiSet, gameStatis, screen):
self.screen = screen
self.screenRect = self.screen.get_rect()
self.aiSet = aiSet
self.gameStatis = gameStatis
self.textColor = (30, 30, 30)
self.font = pygame.font.Font(r'C:\Windows\Fonts\simhei.ttf', 48)
self.levelFont = pygame.font.Font(
r'C:\Windows\Fonts\simhei.ttf', 20)
self.preScore()
self.preHighScore()
self.preLevel()

def preScore(self):
# 将score圆整到小数点左边第一位
roundScore = int(round(self.gameStatis.score, -1))
self.scoreImage = self.font.render('{:,}'.format(
roundScore), True, self.textColor, self.aiSet.mainBgColor)
self.scoreImageRect = self.scoreImage.get_rect()
self.scoreImageRect.right = self.screenRect.right-20
self.scoreImageRect.top = 20

def preHighScore(self):
roundHighScore = int(round(self.gameStatis.highScore, -1))
self.highScoreImage = self.font.render('{:,}'.format(
roundHighScore), True, self.textColor, self.aiSet.mainBgColor)
self.highScoreImageRect = self.highScoreImage.get_rect()
self.highScoreImageRect.centerx = self.screenRect.centerx
self.highScoreImageRect.top = self.screenRect.top

def preLevel(self):
levelStr = '第%2d关' % (self.gameStatis.level)
self.levelImage = self.levelFont.render(
levelStr, True, self.textColor, self.aiSet.mainBgColor)
self.levelImageRect = self.levelImage.get_rect()
self.levelImageRect.right = self.screenRect.right
# 高度设置为分数底部+20
self.levelImageRect.top = self.scoreImageRect.bottom+20

def showScore(self):
self.screen.blit(self.scoreImage, self.scoreImageRect)
self.screen.blit(self.highScoreImage, self.highScoreImageRect)
self.screen.blit(self.levelImage, self.levelImageRect)


游戏设置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# -*- encoding: utf-8 -*-
'''
@file :setting.py
@Time :2021-01-04 02:24:30
@Author :Karcyril
@Software :Visual Studio Code
'''


if __name__ != '__main__':
class Setting:
def __init__(self):
self.screenWidth = 1200
self.screenHeight = 800
self.mainBgColor = (230, 230, 230)
self.shipLimit = 2
self.bulletWidth = 300
self.bulletHeight = 150
self.bulletColor = (60, 60, 60)
self.bulletAllowed = 3
self.fleetDropSpeed = 15
# fleet移动的方向,1表示右移,-1表示左移
self.fleetDirection = 1
self.speedUpScale = 1.2
self.initializeDynamicSetting()
self.scoreScale = 1.5

def initializeDynamicSetting(self):
self.shipSpeedFactor = 1.5
self.bulletSpeedFactor = 3
self.alienSpeedFactor = 1
self.alienPoint = 50

def increaseSpeed(self):
self.shipSpeedFactor *= self.speedUpScale
self.bulletSpeedFactor *= self.speedUpScale
self.alienSpeedFactor *= self.speedUpScale
self.alienPoint = int(self.alienPoint*self.scoreScale)

玩家的飞机:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# -*- encoding: utf-8 -*-
'''
@file :ship.py
@Time :2021-01-04 02:24:16
@Author :Karcyril
@Software :Visual Studio Code
'''


if __name__ != '__main__':
import pygame

class Ship():
def __init__(self, aiSet, screen):
self.screen = screen
self.image = pygame.image.load(r'.\images\ship.bmp')
self.rect = self.image.get_rect()
self.screenRect = screen.get_rect()
self.rect.centerx = self.screenRect.centerx
self.rect.bottom = self.screenRect.bottom
self.moveRight = False
self.moveLeft = False
self.aiSet = aiSet
self.center = float(self.rect.centerx)

def update(self):
if self.moveRight and self.rect.right < self.screenRect.right:
self.center += self.aiSet.shipSpeedFactor
# screen坐标是独立于桌面的的,screen的最左边就是0
if self.moveLeft and self.rect.left > 0:
self.center -= self.aiSet.shipSpeedFactor
self.rect.centerx = self.center

def blitMe(self):
self.screen.blit(self.image, self.rect)

def shipCenter(self):
self.center = self.screenRect.centerx