当前位置:
首页 > Python基础教程 >
-
python 游戏(滑动拼图Slide_Puzzle)
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游戏开发>>
游戏源码下载 https://inventwithpython.com/slidepuzzle.py
友情推荐: 猿人学Python【https://www.yuanrenxue.com/】 由一群工作十余年的老程序员结合实际工作经验所写的Python教程。
栏目列表
最新更新
nodejs爬虫
Python正则表达式完全指南
爬取豆瓣Top250图书数据
shp 地图文件批量添加字段
爬虫小试牛刀(爬取学校通知公告)
【python基础】函数-初识函数
【python基础】函数-返回值
HTTP请求:requests模块基础使用必知必会
Python初学者友好丨详解参数传递类型
如何有效管理爬虫流量?
SQL SERVER中递归
2个场景实例讲解GaussDB(DWS)基表统计信息估
常用的 SQL Server 关键字及其含义
动手分析SQL Server中的事务中使用的锁
openGauss内核分析:SQL by pass & 经典执行
一招教你如何高效批量导入与更新数据
天天写SQL,这些神奇的特性你知道吗?
openGauss内核分析:执行计划生成
[IM002]Navicat ODBC驱动器管理器 未发现数据
初入Sql Server 之 存储过程的简单使用
这是目前我见过最好的跨域解决方案!
减少回流与重绘
减少回流与重绘
如何使用KrpanoToolJS在浏览器切图
performance.now() 与 Date.now() 对比
一款纯 JS 实现的轻量化图片编辑器
关于开发 VS Code 插件遇到的 workbench.scm.
前端设计模式——观察者模式
前端设计模式——中介者模式
创建型-原型模式