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教程。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM