• 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+'confFreeSansBold.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教程。
  • 相关阅读:
    Ajax服务端框架的综合示例
    《Microsoft Sql server 2008 Internals》读书笔记目录索引
    Pro ASP.NET MVC 3 Framework
    C#客户端的异步操作
    在.net中读写config文件的各种方法
    KMP算法分析
    asp.net 4.0 + 模板引擎(NVelocity)CMS
    mvc3
    开源 Asp.net mvc 用户中心开发计划
    JqueryCMS
  • 原文地址:https://www.cnblogs.com/gm332211/p/10886428.html
Copyright © 2020-2023  润新知