参考 pygame 实现 flappybird 并打包成 exe 运行文件
音乐媒体文件 | 作用 |
---|---|
victory.wav | 玩家胜利音乐 |
bg_music.mp3 | 游戏背景音乐 |
click.wav | 按钮点击声 |
forbid.wav | 禁止点击声 |
put_down.wav | 棋子落子声 |
图片媒体文件 | 作用 |
---|---|
chessboard.png | 棋盘 |
src.jpg | exe图标 |
logo.png | 任务栏图标 |
文件 | 作用 |
---|---|
black.py | 黑子 |
cursors.py | 鼠标格式 |
main.py | 主程序 |
white.py | 白子 |
创建三个按钮start(开始),back(悔棋),again(再来一次),点击“开始”才能落子,创建一个矩阵来对应棋盘上的落子,在矩阵中用零表示空,用1表示黑,10 表示白(黑 1 白 10 ),每落一子则用一个 5 * 5 大小的矩阵遍历棋盘,判断矩阵横竖斜的和是否为 5 或 10,每点击一次“悔棋”则抹除棋盘上的子,并在矩阵相应位置设置为 0,点击“再来”按钮则刷新游戏
逻辑注意:
1.黑白的对应数字尽量大避免有交集(如若白 1 黑 2,则 5 白与 2 黑 1 白等同,造误判)
2.游戏遵循白先黑后,标志位设定可以看出来
3.在悔棋处有一个逻辑缺陷,由于时间冲突(刚写完就被其他事占领)未更改,就是单悔棋一子会造成黑白交换,悔棋二子才不会,您可以自行更改
4.一定要分清并对应棋盘坐标与矩阵坐标
cursors.py
thickarrow_strings
text_no
text_arrow
主要包括三个鼠标形态,箭头,禁止,无柄箭头
由 pygame 所提供的源代码修改而来
E:\Anaconda3\envs\tensorflow\Lib\site-packages\pygame\examples\cursors.py
black.py 与 white.py
主要给出黑子与白子的参数
class Black(object):
def __init__(self):
self.color = 0, 0, 0
self.radius = 10
self.width = 10
main.py
导入必要的包与模块
import pygame
import numpy as np
import sys
from white import White
from black import Black
import cursors
落子的更新
def map_update(w_flag, s_flag, pos):
if w_flag and s_flag:
sound.play()
pygame.draw.circle(screen, White.color, pos, White.radius, White.width)
if not w_flag and s_flag:
sound.play()
pygame.draw.circle(screen, Black.color, pos, Black.radius, Black.width)
棋局结果的判断
def result(matrix, num):
over = False
bg_color = (72, 61, 139)
text_color = (255, 255, 255)
result_font = pygame.font.SysFont('Arial', 40)
white_rectangle = result_font.render("White is winner", True, text_color, bg_color)
white_rectangle.set_alpha(200)
black_rectangle = result_font.render("Black is winner", True, text_color, bg_color)
black_rectangle.set_alpha(200)
if num >= 9:
for row in range(0, 11):
for i in range(0, 11):
calculate_area = matrix[row:row + 5, i:i + 5]
if np.sum(calculate_area) >= 5:
if (np.trace(calculate_area) == 5 or np.trace(
np.fliplr(calculate_area)) == 5) or (
5 in sum(calculate_area[:, ]) or 5 in sum(calculate_area.T[:, ])):
matrix = np.zeros([15, 15], dtype=int)
over = True
white_win()
victory.play()
elif (np.trace(calculate_area) == 50 or np.trace(
np.fliplr(calculate_area)) == 50) or (
50 in sum(calculate_area[:, ]) or 50 in sum(calculate_area.T[:, ])):
matrix = np.zeros([15, 15], dtype=int)
over = True
black_win()
victory.play()
else:
over = False
pygame.display.flip()
return over
白色赢后的显示效果
def white_win():
button_color = (0, 0, 255)
text_color = (255, 255, 255)
start_font = pygame.font.SysFont('Arial', 30)
img_button = start_font.render("White is WINNER", True, text_color, button_color)
img_button.set_alpha(170)
screen.blit(img_button, [screen.get_width() / 2 - img_button.get_width() / 2,
screen.get_height()/2 - img_button.get_height() / 2 - 30])
pygame.display.flip()
黑色赢后的显示效果
def start_button():
button_color = (0, 0, 255)
text_color = (255, 255, 255)
start_font = pygame.font.SysFont('Arial', 30)
img_button = start_font.render("Start", True, text_color, button_color)
screen.blit(img_button, [screen.get_width() / 5 - img_button.get_width() / 2,
screen.get_height() - 40])
pygame.display.flip()
悔棋按钮
def back_button():
button_color = (0, 0, 255)
text_color = (255, 255, 255)
start_font = pygame.font.SysFont('Arial', 30)
img_button = start_font.render("Back", True, text_color, button_color)
screen.blit(img_button, [screen.get_width() / 2 - img_button.get_width() / 2,
screen.get_height() - 40])
pygame.display.flip()
再来一次按钮
def again_button():
button_color = (0, 0, 255)
text_color = (255, 255, 255)
start_font = pygame.font.SysFont('Arial', 30)
img_button = start_font.render("Again", True, text_color, button_color)
screen.blit(img_button, [4 * screen.get_width() / 5 - img_button.get_width() / 2,
screen.get_height() - 40])
pygame.display.flip()
提前开辟变量,用于存储落子的顺序,分别为实际棋盘与矩阵棋盘,方便悔棋时清理棋盘
chess_order = []
board_order = []
主程序入口与函数初始化,参数设定
if __name__ == '__main__':
pygame.init()
pygame.mixer.init()
pygame.font.init()
font = pygame.font.SysFont('Arial', 50)
size = width, height = 535, 586
screen = pygame.display.set_mode(size)
screen.fill((255, 0, 0))
background = pygame.image.load('picture/chessboard.png')
game_icon = pygame.image.load('picture/logo.png')
pygame.display.set_icon(game_icon)
White = White()
Black = Black()
初始标志位,分别为开始,悔棋,再来,白棋落子,游戏结束标志位
start_flag = False
back_flag = False
again_flag = False
white_flag = True
chess_over = False
背景显示,创建棋盘矩阵,数量与位置初始化
screen.blit(background, (0, 0))
chess_board = np.zeros([15, 15], dtype=int)
chess_num = 0
white_num = 0
position = (0, 0)
函数初始化,音效载入
clock = pygame.time.Clock()
pygame.mixer.music.load('music/bg_music.mp3')
pygame.mixer.music.play(-1, 0.0)
sound = pygame.mixer.Sound('music/put_down.wav')
click_sound = pygame.mixer.Sound('music/click.wav')
forbidden = pygame.mixer.Sound('music/forbid.wav')
victory = pygame.mixer.Sound('music/victory.wav')
循环开始,pygame标准开始格式
while True:
clock.tick(60)
again_button()
start_button()
back_button()
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
获取鼠标坐标,限定鼠标范围,通过标志位设置鼠标形态
x, y = pygame.mouse.get_pos()
click = pygame.mouse.get_pressed()
if x in range(0, 536) and y in range(0, 537) and not start_flag:
curs, mask = cursors.compile(cursors.text_no, 'X', '.')
pygame.mouse.set_cursor((24, 24), (0, 0), curs, mask)
elif start_flag and x in range(0, 536) and y in range(0, 537):
curs, mask = cursors.compile(cursors.thickarrow_strings, 'X', '.')
pygame.mouse.set_cursor((24, 24), (0, 0), curs, mask)
else:
curs, mask = cursors.compile(cursors.text_arrow, 'X', '.')
pygame.mouse.set_cursor((24, 24), (0, 0), curs, mask)
标志位判断与载入游戏音效
if not start_flag and event.type == pygame.MOUSEBUTTONDOWN and x in range(0, 536) and y in range(0, 537):
forbidden.play()
if chess_over and x in range(0, 536) and y in range(0, 537)and event.type == pygame.MOUSEBUTTONDOWN:
forbidden.play()
if chess_over and x in range(0, 536) and y in range(0, 537):
curs, mask = cursors.compile(cursors.text_no, 'X', '.')
pygame.mouse.set_cursor((24, 24), (0, 0), curs, mask)
if click[0] == 1 and (x in range(81, 133) and y in range(546, 582)):
click_sound.play()
start_flag = True
if click[0] == 1 and (x in range(240, 294) and y in range(546, 582)) and not start_flag:
click_sound.play()
if click[0] == 1 and (x in range(396, 459) and y in range(546, 582))and not start_flag:
click_sound.play()
if click[0] == 1 and (x in range(240, 294) and y in range(546, 582)) and start_flag and np.sum(chess_board) and not chess_over:
click_sound.play()
back_flag = True
if click[0] == 1 and (x in range(240, 294) and y in range(546, 582)):
click_sound.play()
if click[0] == 1 and (x in range(396, 459) and y in range(546, 582))and start_flag:
click_sound.play()
again_flag = True
标志位判断游戏状态
if (x in range(0, 536) and y in range(0, 537)) and start_flag and not back_flag and not again_flag:
x_chess = round((x - 23) / 35) * 35 + 23
y_chess = round((y - 23) / 35) * 35 + 23
x_board = round((x - 23) / 35)
y_board = round((y - 23) / 35)
position = (x_board, y_board)
pos_chess = (x_chess, y_chess)
if not chess_over:
if white_flag and position[0] in range(0,15) and position[1] in range(0,15):
if event.type == pygame.MOUSEBUTTONDOWN and not chess_board[position]:
map_update(white_flag, start_flag, pos_chess)
chess_order.append(pos_chess)
board_order.append(position)
chess_board[position] = 1
white_flag = bool(1 - white_flag)
chess_num += 1
chess_over = result(chess_board, chess_num)
else:
if position[0] in range(0,15) and position[1] in range(0,15):
if event.type == pygame.MOUSEBUTTONDOWN and not chess_board[position]:
map_update(white_flag, start_flag, pos_chess)
chess_order.append(pos_chess)
board_order.append(position)
chess_board[position] = 10
white_flag = bool(1 - white_flag)
chess_num += 1
chess_over = result(chess_board, chess_num)
if start_flag and not back_flag and again_flag:
chess_board = np.zeros([15, 15], dtype=int)
chess_order.clear()
board_order.clear()
screen.blit(background, (0, 0))
again_flag = False
chess_over = False
if start_flag and back_flag and not again_flag and not chess_over:
if chess_order:
chess_board[board_order[-1]] = 0
pos_point = chess_order[-1]
back_bg1 = pygame.transform.chop(background, (0, 0, pos_point[0] - 11, pos_point[1] - 11))
back_bg2 = pygame.transform.rotate(back_bg1, 180)
back_bg3 = pygame.transform.chop(back_bg2,
(0, 0, back_bg2.get_width() - 22, back_bg2.get_height() - 22))
final_bg = pygame.transform.rotate(back_bg3, 180)
screen.blit(final_bg, (pos_point[0] - 11, pos_point[1] - 11))
chess_order.pop()
board_order.pop()
back_flag = False
screen.blit(screen, (0, 0))
pygame.display.update()
程序标志位分析概要:
1.标志位包括白棋的标志位,通过反转形成黑白交替落子
2.通过开始标志位判断游戏是否开始,悔棋标志位判断是否悔棋,再来标志位判断是否刷新游戏
3.为了增强程序的鲁棒性,标志位赢联合使用而不是单一使用,减少 bug 的产生
(base) C:\Users\HaoHao>activate tensorflow
(tensorflow) C:\Users\HaoHao>cd F:\Gobang
(tensorflow) C:\Users\HaoHao>F:
(tensorflow) F:\Gobang>python main.py
由于Pygame具有窗口所以一般直接打包方法为
pyinstaller -F -w -i logo.ico main.py
但会报 failed to execute script 错误(请手动排除媒体文件不在同级目录的原因)
pyinstaller -F -w -c -i logo.ico main.py
打包完成后,打开cmd,将 exe 文件直接拖入,会看到报错信息
C:\Users\HaoHao>E:\Desktop\Gobang\dist\main.exe
pygame 1.9.6
Hello from the pygame community. https://www.pygame.org/contribute.html
Traceback (most recent call last):
File "main.py", line 2, in
File "e:\anaconda3\envs\tensorflow\lib\site-packages\PyInstaller\loader\pyimod03_importers.py", line 623, in exec_module
exec(bytecode, module.__dict__)
File "site-packages\numpy\__init__.py", line 150, in
File "e:\anaconda3\envs\tensorflow\lib\site-packages\PyInstaller\loader\pyimod03_importers.py", line 623, in exec_module
exec(bytecode, module.__dict__)
File "site-packages\numpy\random\__init__.py", line 180, in
File "mtrand.pyx", line 1, in init numpy.random.mtrand
ModuleNotFoundError: No module named 'numpy.random.common'
[5356] Failed to execute script main
这是缺少模块的原因
命令增加隐藏模块导入部分 --hidden-import
pyinstaller -F -w -c -i logo.ico main.py --hidden-import numpy.random.common
完成后再拖入 cmd,发现缺少模块,再输入 --hidden-import,如此反复,直到成功,最终命令如下
pyinstaller -F -w -c -i logo.ico main.py --hidden-import numpy.random.common --hidden-import numpy.random.bounded_integers --hidden-import numpy.random.entropy
只需要将上面的 -c 调试模式取消就行
pyinstaller -F -w -i logo.ico main.py --hidden-import numpy.random.common --hidden-import numpy.random.bounded_integers --hidden-import numpy.random.entropy
Pyinstaller 与编译器对程序的要求是具有差异的,有可能编译器能运行,但程序打包后出现各种各样的情况,甚至无法打包成功,本次程序有如下几点注意
1.font 的参数不能出现 None(编译器可以运行,Pyinstaller 打包无法正常运行)
2.需要的媒体文件需要拷贝至可执行文件处(Failed to execute script main)
3.ico 图标文件不能将图片直接修改后缀得到来使用,必须转换才能使用(报缓存错误)
4.包含所有的隐藏模块