python 游戏(滑动拼图Slide_Puzzle)
程序员文章站
2022-07-02 20:44:47
1. 游戏功能和流程图 实现16宫格滑动拼图,实现3个按钮(重置用户操作,重新开始游戏,解密游戏),后续难度,额外添加重置一次的按钮,解密算法的植入,数字改变为图片植入 游戏流程图 2. 游戏配置 配置游戏目录 配置游戏(game_conf.py) 配置颜色(color.py) 配置动作(handl ......
1. 游戏功能和流程图
实现16宫格滑动拼图,实现3个按钮(重置用户操作,重新开始游戏,解密游戏),后续难度,额外添加重置一次的按钮,解密算法的植入,数字改变为图片植入
游戏流程图
2. 游戏配置
配置游戏目录
配置游戏(game_conf.py)
#游戏参数配置
board_width=4 #游戏板列数
board_height=4 #游戏板行数
tile_size=80 #游戏板块大小
window_width=1024 #窗口大小
window_height=768
fps=30 #游戏帧数
basicfont_size=20 #字体大小
x_margin=int((window_width-(tile_size*board_width+(board_width-1)))/2) #x边距
y_margin=int((window_height-(tile_size*board_height+(board_height-1)))/2) #y边距
配置颜色(color.py)
#游戏色彩配置
black=(0,0,0) #黑色
white=(255,255,255) #白色
bright_blue=(0,50,250) #宝石蓝色
darkturquois=(3,54,73) #深珠宝绿
green=(0,204,0) #绿色
配置动作(handle.py)
#动作配置
up = 'up'
down = 'down'
left = 'left'
right = 'right'
3. 游戏使用工具和函数(views.py)
3.1 工具和绘画类使用模块和常量
from conf.color import *
from conf.game_conf import *
from conf.handle import *
from pygame import *
import random,pygame,sys
tile_color=green
text_color=white
bg_color=darkturquois
message_color=white
border_color=bright_blue
3.2 生成游戏数据
def get_starting_board():
'''生成游戏板数据结构
:return 返回数据坐标'''
board=[]
for x in range(board_width):
counter = 1+x
column=[]
for y in range(board_height):
column.append(counter)#添加y轴数据
counter+=board_width
board.append(column) #添加x轴数据
board[board_width-1][board_height-1]=none #最后一个方块是空方块
return board
3.3 查找空白块坐标
def get_blank_position(board):
'''找到游戏空白块坐标
:param board 游戏板块坐标列表
:return 返回空板块坐标'''
for x in range(board_width):
for y in range(board_height):
if board[x][y]==none:
return (x,y)
3.4 移动方块坐标
def make_move(board,move):
'''数据移动方块
:param 移动板块列表
:param move 移动方向'''
blank_x,blank_y=get_blank_position(board)
if move==up:#上升空白块
#blank_y+1 下个格子的y坐标,下降空白格,向上移动格子
board[blank_x][blank_y],board[blank_x][blank_y+1]=board[blank_x][blank_y+1],board[blank_x][blank_y]
elif move==down:
# blank_y-1 上个格子的y坐标,上降空白格,向下移动格子
board[blank_x][blank_y], board[blank_x][blank_y - 1] = board[blank_x][blank_y - 1], board[blank_x][blank_y]
elif move==left:
#blank_x + 1 右边格子的x坐标,右移空白格,向左移动格子
board[blank_x][blank_y], board[blank_x+1][blank_y] = board[blank_x+1][blank_y], board[blank_x][blank_y]
elif move==right:
# blank_x - 1 左边格子的x坐标,左移空白格,向右移动格子
board[blank_x][blank_y], board[blank_x - 1][blank_y] = board[blank_x - 1][blank_y], board[blank_x][blank_y]
3.5 移动限制
def is_valid_move(board,move):
'''移动判断,移动断言
:param board 移动板块列表
:param move 移动方向
:return 返回true可移动,返回false不可移动'''
blank_x,blank_y=get_blank_position(board) #获取空白位置的坐标
#如果是上升的话,空白格的坐标不能等于y边界的坐标,如果返回flase
#下降,空白坐标不能等于y零界点左边,如果等于返回false
return (move==up and blank_y !=board_height-1)\
or (move==down and blank_y !=0) \
or (move==left and blank_x !=board_width-1) \
or (move==right and blank_x !=0)
3.6 随机移动方向
def get_random_move(board,last_move=none):
'''随机移动方向
:param board 游戏数据
:param last_move 上次移动记录
:return 返回随机移动方位'''
vaild_moves=[up,down,left,right]
if last_move ==up or not is_valid_move(board,down):#排除上下重复移动和向下不能移动选项
vaild_moves.remove(down) #删除向下移动
if last_move ==down or not is_valid_move(board,up):
vaild_moves.remove(up)
if last_move ==left or not is_valid_move(board,right):#排除左右重复移动和向右不能移动选项
vaild_moves.remove(right)
if last_move ==right or not is_valid_move(board,left):
vaild_moves.remove(left)
return random.choice(vaild_moves)
3.7 数据坐标转化像素坐标
def get_left_top_of_tile(tile_x,tile_y):
'''根据坐标返回像素坐标
:param tile_x 数据x坐标
:param tile_y 数据y坐标
:return 返回像素left,top坐标'''
left=x_margin+(tile_x*tile_size)+(tile_x-1) #获取左边的像素坐标(tile_x-1)格子间的间距
top=y_margin+(tile_y*tile_size)+(tile_y-1)#获取头部坐标
return (left,top)
3.8 矩形碰撞(根据像素坐标找到数据坐标)
def get_spot_clicked(board,x,y):
'''矩形碰撞
:param board 游戏数据列表
:param x 像素x坐标
:param y 像素y坐标
:return 返回数据坐标'''
for tile_x in range(len(board)):
for tile_y in range(len(board[0])):
left,top=get_left_top_of_tile(tile_x,tile_y)
tile_rect=pygame.rect(left,top,tile_size,tile_size) #创建坐标矩形
if tile_rect.collidepoint(x,y): #判断像素坐标点是否在矩形内部
return (tile_x,tile_y) #返回数据坐标
return (none,none)
3.9 退出游戏函数
def terminate():
'''退出游戏'''
pygame.quit()
sys.exit()
3.10 退出事件监控
def check_for_quit():
'''循环事件监控'''
for event in pygame.event.get(quit):
terminate()
for event in pygame.event.get(keyup):
if event.key==k_escape: #获取esc按键
terminate()
pygame.event.post(event) #将其他事件放回event
4 创建游戏绘画类(views.py)
4.1 创建类的初始化
def __init__(self,display_surf,fps_clock,basic_font):
self.display_surf=display_surf#surf对象
self.fps_clock=fps_clock#fps对象
self.basic_font=basic_font#文字对象
4.2 绘制单个方块
def draw_tile(self,tile_x,tile_y,number,adjx=0,adjy=0):
'''绘制游戏方块
:param tile_x 数据x坐标
:param tile_y 数据y坐标
:param number 方块显示信息
:param adjx 滑动动画x位移像素
:param adjy 滑动动画y位移像素'''
left,top=get_left_top_of_tile(tile_x,tile_y)
pygame.draw.rect(self.display_surf,tile_color,(left+adjx,top+adjy,tile_size,tile_size))
text_surf=self.basic_font.render(str(number),true,text_color)#创建文字图层
text_rect=text_surf.get_rect()#创建文字矩形块
text_rect.center=(left+int(tile_size/2)+adjx,top+int(tile_size/2)+adjy) #文字居中
self.display_surf.blit(text_surf,text_rect) #生成字体
4.3 创建字体通用对象
def make_text(self,text,color,bg_color,left,top):
'''创建字体对象
:param text 显示文本
:param color 文本颜色
:param bg_color 文本背景颜色
:param left 像素x坐标
:param top 像素y坐标
:return text_surf 文本对象 text_rect 位置对象'''
text_surf=self.basic_font.render(text,true,color,bg_color) #创建文本
#定位文字矩形块
text_rect=text_surf.get_rect()
text_rect.topleft=(left,top)
return (text_surf,text_rect)
4.4 生成游戏按钮文本对象
def make_button_obj(self):
'''生成按钮文本对象
:param 返回按钮文本对象'''
button_left = window_width-120
button_top = window_height-90
reset_surf,reset_rect=self.make_text('reset',text_color,tile_color,button_left,button_top)
new_surf, new_rect = self.make_text('new game', text_color, tile_color, button_left, button_top+30)
solve_surf, solve_rect = self.make_text('solve', text_color, tile_color, button_left, button_top + 60)
return (reset_surf, reset_rect,new_surf, new_rect,solve_surf, solve_rect)
4.5 绘制游戏面板
def draw_board(self,board,message):
'''绘制画板
:param board 游戏坐标数据
:param message 游戏左上角提示信息'''
self.display_surf.fill(bg_color)#添加背景
if message: #如果有提示信息显示提示信息
text_surf,text_rect=self.make_text(message,message_color,bg_color,5,5) #获取文本对象和定位对象
self.display_surf.blit(text_surf,text_rect) #显示消息
for tile_x in range(len(board)): #绘制方块
for tile_y in range(len(board[0])):
if board[tile_x][tile_y]:
self.draw_tile(tile_x,tile_y,board[tile_x][tile_y])
#绘制边框
left,top=get_left_top_of_tile(0,0)
width=board_width*tile_size
height=board_height*tile_size
pygame.draw.rect(self.display_surf,border_color,(left-5,top-5,width+11,height+11),4)
#绘制按钮
reset_surf, reset_rect,new_surf, new_rect,solve_surf, solve_rect=self.make_button_obj()
self.display_surf.blit(reset_surf,reset_rect)
self.display_surf.blit(new_surf, new_rect)
self.display_surf.blit(solve_surf, solve_rect)
4.6 绘制滑动动画
def slide_animation(self,board,direction,message,animation_speed):
'''绘制滑动动画
:param board 游戏坐标数据
:param direction 移动方位
:param message 游戏左上角提示信息
:param animation_speed 移动动画速度'''
blank_x,blank_y=get_blank_position(board)
#根据空白坐标和移动方位获取要移动方块的坐标
move_x,move_y=0,0
if direction == up:
move_x,move_y =blank_x,blank_y+1
if direction == down:
move_x,move_y=blank_x,blank_y-1
if direction == left:
move_x,move_y=blank_x+1,blank_y
if direction == right:
move_x,move_y=blank_x-1,blank_y
self.draw_board(board,message) #绘制画板
base_surf=self.display_surf.copy()#复制一个新的窗口对象
move_left,move_top=get_left_top_of_tile(move_x,move_y)
#绘制空白区(这时候有2块空白区域)
pygame.draw.rect(base_surf,bg_color,(move_left,move_top,tile_size,tile_size))
#绘制滑动效果
for i in range(0,tile_size,animation_speed): #animation_speed步长偏移速度,每次循环后方块的位置向指定方向移动
check_for_quit()
self.display_surf.blit(base_surf,(0,0))
if direction==up:
self.draw_tile(move_x,move_y,board[move_x][move_y],0,-i) #x不动,y轴向上偏移
if direction==down:
self.draw_tile(move_x, move_y, board[move_x][move_y], 0, i) # x不动,y轴向下偏移
if direction==left:
self.draw_tile(move_x, move_y, board[move_x][move_y], -i, 0) # x不动,y轴向左偏移
if direction==right:
self.draw_tile(move_x, move_y, board[move_x][move_y], i, 0) # x不动,y轴向右偏移
pygame.display.update()
self.fps_clock.tick(fps)
4.7 绘制重新开始游戏动画并重置游戏数据
def generate_new_puzzle(self,num_slides):
'''重新开始游戏
:param num_slides 移动次数
:return board 游戏坐标数据sequence 移动数据'''
sequence=[] #移动数据
board=get_starting_board() #重新生成游戏数据
self.draw_board(board,'') #显示开始画板
pygame.display.update()
pygame.time.wait(500) #等待500毫秒
last_move=none
for i in range(num_slides): #执行移动打乱游戏数据
move=get_random_move(board,last_move) #获取随机移动方向
self.slide_animation(board,move,'generating new puzzle...',animation_speed=int(tile_size/3)) #执行移动动画
make_move(board,move) #数据坐标移动
sequence.append(move) #记录移动信息
last_move=move
return (board,sequence)
4.8 绘制重置玩家步骤动画
def reset_animation(self,board,all_moves):
'''重置步骤
:param board 游戏坐标数据
:param all_moves 游戏移动信息列表'''
rev_all_moves=all_moves[:]
if rev_all_moves:
rev_all_moves.reverse() #翻转操作列表
opposite_move=down
for move in rev_all_moves: #反转移动方向,反向移动重置
if move==up:
opposite_move=down
if move==down:
opposite_move=up
if move == left:
opposite_move = right
if move == right:
opposite_move = left
self.slide_animation(board,opposite_move,'',animation_speed=int(tile_size/2)) #执行移动动画
make_move(board,opposite_move)#数据移动方块
5. 游戏逻辑判断(游戏核心slide_puzzle.py)
5.1 游戏使用模块和常量
import pygame,sys,random
from pygame.locals import *
import os,sys
base_path=os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0,base_path)
from conf.color import *
from conf.game_conf import *
from conf.handle import *
from core.views import *
blank=none
button_color = white #按钮颜色
buttontext_color = black #按钮文本颜色
message_color = white
5.2 游戏逻辑判断
def main():
pygame.init()
fps_clock = pygame.time.clock()
display_surf=pygame.display.set_mode((window_width,window_height))
pygame.display.set_caption('slide puzzle')
basic_font = pygame.font.font(base_path+'\conf\freesansbold.ttf', basicfont_size)
puzzle=game_draw(display_surf,fps_clock,basic_font)
reset_surf, reset_rect, new_surf, new_rect, solve_surf, solve_rect = puzzle.make_button_obj()
main_board,solution_seq=puzzle.generate_new_puzzle(80)
solved_board=get_starting_board() #游戏胜利对比数据
all_move=[] #游戏移动方向数据
while true:
slide_to=none #
msg='click tile or press arrow keys to slide.' #游戏开始提示
if main_board==solved_board: #游戏获胜判断
msg='solved!'
solution_seq = []
all_move = []
puzzle.draw_board(main_board,msg)
check_for_quit()
for event in pygame.event.get():
if event.type==mousebuttonup: #鼠标点击按钮
spot_x,spot_y=get_spot_clicked(main_board,event.pos[0],event.pos[1]) #鼠标点击碰撞
if (spot_x,spot_y)==(none,none): #如果没有在游戏方块上
if reset_rect.collidepoint(event.pos):
puzzle.reset_animation(main_board,all_move) #重置用户移动数据
all_move = []
elif new_rect.collidepoint(event.pos):
main_board,solution_seq=puzzle.generate_new_puzzle(80) #重置游戏数据和初始系统移动数据
all_move = []
elif solve_rect.collidepoint(event.pos):
puzzle.reset_animation(main_board,solution_seq+all_move) #重置用户移动列表,在重置系统移动列表
solution_seq=[]
all_move = []
else:
blank_x,blank_y=get_blank_position(main_board)
if spot_x-1==blank_x and spot_y == blank_y:
slide_to = left
if spot_x+1==blank_x and spot_y == blank_y:#如果方块的右边是空白
slide_to = right
if spot_x==blank_x and spot_y-1 == blank_y:
slide_to = up
if spot_x==blank_x and spot_y+1 == blank_y:
slide_to = down
elif event.type == keyup:
# 监控按键移动方式
if event.key in (k_left, k_a) and is_valid_move(main_board, left):
slide_to = left
# 如果按键触发在右按键和d按键并且可以右移动方块
elif event.key in (k_right, k_d) and is_valid_move(main_board, right):
slide_to = right
elif event.key in (k_up, k_w) and is_valid_move(main_board, up):
slide_to = up
elif event.key in (k_down, k_s) and is_valid_move(main_board, down):
slide_to = down
if slide_to:
puzzle.slide_animation(main_board,slide_to, 'click tile or press arrow keys to slide.', 8) #执行滑动动画
make_move(main_board,slide_to) #修改游戏坐标数据
all_move.append(slide_to) #添加移动记录
pygame.display.update()
fps_clock.tick(fps)
5.3 运行游戏
if __name__ == '__main__':
main()
python学习途径
本游戏参考书本 <<python和pygame游戏开发>>
游戏源码下载
友情推荐: 猿人学python【】 由一群工作十余年的老程序员结合实际工作经验所写的python教程。