pygame開發(fā):馬賽邏輯小游戲的代碼實(shí)現(xiàn)
一、游戲簡(jiǎn)介
馬賽邏輯,是一個(gè)類似數(shù)獨(dú)和掃雷的邏輯小游戲,根據(jù)棋盤周圍的數(shù)據(jù)提示點(diǎn)亮方格,因外形像馬賽克而得名。在手機(jī)游戲中有多款 APP 可以體驗(yàn)該游戲,如 Peak、Nonogram、Crossme 等。但在 PC 端,筆者暫時(shí)還未發(fā)現(xiàn)復(fù)刻版,于是打算自己動(dòng)手實(shí)現(xiàn)一番。
馬賽邏輯的基本玩法如下圖所示,上側(cè)橫向的各組數(shù)字為:對(duì)每一列中存在的目標(biāo)方格的標(biāo)注,如 2
表示該列有 2 個(gè)連續(xù)的目標(biāo),1 2
表示該列有 1 個(gè)獨(dú)立的目標(biāo) + 2 個(gè)連續(xù)的目標(biāo)。左側(cè)縱向的各組數(shù)據(jù)為對(duì)每一行的標(biāo)注。通過(guò)上、左兩側(cè)的提示,將所有目標(biāo)方格點(diǎn)亮即為通關(guān)。
二、核心代碼解析
在正式開始游戲開發(fā)之前,我們可以先想想實(shí)現(xiàn)這個(gè)項(xiàng)目的關(guān)鍵點(diǎn)在哪。首先,方格有選中和未選中兩種狀態(tài),那可以用 1 表示選中、0 表示未選中。要判斷玩家點(diǎn)亮的方格是否正確,只需將方塊矩陣映射成 01 矩陣,再與答案矩陣對(duì)比即可。如此一來(lái),出題也很容易,隨機(jī)生成一串 01 組合即可。
而最重要的地方在于,如何生成提示數(shù)值?我們需要分別對(duì)每行每列進(jìn)行遍歷,找出單獨(dú)的 1 和連續(xù)的 1。下面以棋盤的一行為例進(jìn)行說(shuō)明。
首先,準(zhǔn)備一個(gè)列表類型的變量 remind
用于儲(chǔ)存多個(gè)提示數(shù)值,并準(zhǔn)備一個(gè)位移標(biāo)記 flag
用于記錄當(dāng)前是在答案陣列的哪一位進(jìn)行判斷,以及一個(gè)數(shù)值記錄 num
,再將答案陣列 [0, 1, 1, 0, 1, 0, 0, 1]
傳入計(jì)數(shù)器。
當(dāng)傳入陣列長(zhǎng)度大于 1 時(shí)有四種情況,分別是:①當(dāng)前位 0 ,次位 1;②當(dāng)前位 1,次位 0;③連續(xù)多位 1;④連續(xù)多位 0。根據(jù)不同情況進(jìn)行位移,將新的陣列傳入計(jì)數(shù)器,并在 1 換 0 的時(shí)候記錄數(shù)值。
當(dāng)傳入陣列等于 1 時(shí)有兩種情況,分別是:①上位 0;②上位 1。根據(jù)不同的情況記錄數(shù)值。
按照這個(gè)思路,我們可以用一個(gè)簡(jiǎn)單的遞歸來(lái)實(shí)現(xiàn)這個(gè)提示算法,代碼如下:
def get_line_remind(_line): # 輸出一行或一列的提示 remind = [] # 一行或一列的提示記錄 num = 0 # 提示值 def fun(line): nonlocal remind, num flag = 0 # 位移 if len(line) > 1: if line[0] == 0 and line[1] == 1: flag += 1 elif line[0] == line[1] == 0: flag += 2 elif line[0] == 1 and line[1] == 0: num += 1 remind.append(num) num = 0 flag += 2 elif line[0] == line[1] == 1: num += 1 flag += 1 fun(line[flag:]) elif len(line) and line[0]: if num: remind.append(num + 1) else: remind.append(1) fun(_line) return remind
三、pygame開發(fā)流程
1、從創(chuàng)建窗口到棋盤繪制
棋盤的設(shè)計(jì)及玩法已經(jīng)初具雛形了,可以正式開始制作游戲了啦!~筆者采用了有超過(guò) 20 年歷史的游戲制作庫(kù) pygame,該游戲庫(kù)包含了用于制作簡(jiǎn)單 2D 游戲的基本套件,python 及游戲愛好者們已經(jīng)用它制作了成千上萬(wàn)的小游戲,使用 pip 安裝即可使用。
第一步,對(duì)各類游戲元素的顏色、位置、尺寸等必要參數(shù)做一些設(shè)置。接著,初始化 pygame,繪制一個(gè)指定大小的窗口,使用 pygame.font.Font()
加載指定的字體文件,以防游戲打包后運(yùn)行出錯(cuò)。
需要注意的是,pygame 的所有視覺元素都建立在不斷地重新繪制上,利用 pygame.display.flip()
進(jìn)行整體更新。因?yàn)楹笃谛枰诎咨尘爸刑砑觿?dòng)態(tài)元素,所以將背景繪制放入主循環(huán)的首位。
在主循環(huán)中,通過(guò)遍歷事件來(lái)獲取玩家的操作,當(dāng)前僅追蹤了一個(gè)退出事件。
import pygame import sys # 參數(shù)設(shè)置 ---------------------------------- blue = (159, 197, 232) # 被選中方格的顏色 gray = (217, 217, 217) # 棋盤網(wǎng)格線顏色 gold = (255, 215, 0) # 游戲記錄文字顏色 black = (0, 0, 0) white = (255, 255, 255) start_x = 240 # 棋盤左上角位置 start_y = 150 size = 2 # 一行/列的方塊個(gè)數(shù) square = 320 # 棋盤邊長(zhǎng) length = int(square / size) # 每個(gè)方塊的邊長(zhǎng) # 游戲初始化 ---------------------------------- pygame.init() screen = pygame.display.set_mode((780, 520)) # 創(chuàng)建窗口 font = pygame.font.Font(r'./data/msyh.ttf', 20) # 提示字體 # 主循環(huán) ---------------------------------- while True: screen.fill(white) # 背景填充 for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() # 退出pygame sys.exit() # 安全退出系統(tǒng) pygame.display.flip() # 更新全部顯示
運(yùn)行以上代碼可以得到一片空白(霧)。
下一步,我們來(lái)想想怎么繪制棋盤。首先,棋盤本身的尺寸是固定的,我們只需修改棋盤中的方格數(shù)量和大小,來(lái)改變棋局。因此,在第一步的參數(shù)設(shè)置中,使用 start_x
、start_y
來(lái)確定棋盤的位置,并設(shè)置棋盤的邊長(zhǎng) square = 320
,以及一行中方塊的個(gè)數(shù) size
和方塊邊長(zhǎng) length
。
因?yàn)榉綁K是可以被點(diǎn)擊而改變顏色的,所以我們要先自定義一個(gè)方塊類。機(jī)制比較簡(jiǎn)單,初始化即傳入坐標(biāo)和邊長(zhǎng),調(diào)用 pygame.draw.rect()
來(lái)繪制矩形。
class Item: # 自定義方塊類 def __init__(self, pos_x, pos_y, leng): self.rect = pygame.draw.rect(screen, gray, [pos_x, pos_y, leng, leng], 0) self.state = False
再定義一個(gè)繪制棋盤的方法,從棋盤左上角開始,橫豎各畫 size
個(gè)方塊,返回方塊對(duì)象列表。
def create_chessboard(): # 創(chuàng)建棋盤 item_lst = [] for v in range(size): for h in range(size): rect = Item(start_x + h*length, start_y + v*length, length) item_lst.append(rect) return item_lst
由于方塊初始顏色和背景色一樣是白色,還需要加上網(wǎng)格線,橫豎各畫 size+1
條線,調(diào)用 pygame.draw.line()
繪制線條。
def draw_line(): # 繪制網(wǎng)格線 for n in range(size+1): start = (start_x, start_y + n * length) end = (start_x + square, start_y + n * length) pygame.draw.line(screen, gray, start, end, 2) for n in range(size+1): start = (start_x + n * length, start_y) end = (start_x + n * length, start_y + square) pygame.draw.line(screen, gray, start, end, 2)
將主循環(huán)代碼修改如下,注意:網(wǎng)格線是繪制在整個(gè)圖層組的最上層,才不會(huì)被方格和背景覆蓋掉。運(yùn)行即可繪制出初始棋盤,如圖為 4X4 的規(guī)格。
## 前文參數(shù)、初始化略 ...... items = create_chessboard() # 創(chuàng)建棋盤 while True: screen.fill(white) # 背景填充 for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() # 退出pygame sys.exit() # 安全退出系統(tǒng) draw_line() # 繪制棋盤網(wǎng)格線 pygame.display.flip() # 更新全部顯示
2、點(diǎn)擊方格改變顏色
2.1、點(diǎn)擊事件
在事件遍歷中添加對(duì)鼠標(biāo)點(diǎn)擊事件的追蹤,并獲取點(diǎn)擊坐標(biāo),之后通過(guò)判斷點(diǎn)擊的位置是否在某個(gè)方格中,即可得知是哪個(gè)方格被點(diǎn)擊了,并作出顏色修改。
if event.type == pygame.MOUSEBUTTONDOWN: # 鼠標(biāo)點(diǎn)擊事件 x, y = event.pos
2.2、碰撞檢測(cè)
那么來(lái)寫一個(gè)判斷方法,將之前創(chuàng)建棋盤時(shí)得到的方塊對(duì)象列表,和鼠標(biāo)坐標(biāo)傳入,遍歷方塊并通過(guò)矩形的 collidepoint()
方法進(jìn)行碰撞檢測(cè),若鼠標(biāo)碰撞到了矩形區(qū)域,就對(duì)方塊的狀態(tài)取反。
def check_click(item_lst, pos_x, pos_y): # 更新每個(gè)方塊的點(diǎn)擊狀態(tài) for i in item_lst: if i.rect.collidepoint(pos_x, pos_y): i.state = bool(1 - i.state)
2.3、方格變色
def change_color(item_lst): # 根據(jù)狀態(tài)改變方塊顏色 for i in item_lst: if i.state: pygame.draw.rect(screen, blue, i.rect, 0) else: pygame.draw.rect(screen, white, i.rect, 0)
2.4、陣列轉(zhuǎn)換
再來(lái)寫一個(gè)獲取玩家操作陣列的方法,利用列表生成式將方塊狀態(tài)轉(zhuǎn)換為 01 列表。
def create_answer_array(): # 創(chuàng)建答案矩陣 lst = [1 if random() > 0.5 else 0 for _ in range(size*size)] if list(set(lst))[0] == 0: lst[0] = 1 return lst
并通過(guò)隨機(jī)生成的方式來(lái)創(chuàng)建答案,之后通過(guò)比較兩個(gè)列表即可判斷游戲是否通關(guān)。別忘了,答案陣列中不能全都是 0。
def create_answer_array(): # 創(chuàng)建答案矩陣 lst = [1 if random() > 0.5 else 0 for _ in range(size*size)] if list(set(lst))[0] == 0: lst[0] = 1 return lst
2.5、效果初見
修改主循環(huán)代碼如下,運(yùn)行后嘗試點(diǎn)擊可見效果。
## 前文參數(shù)、初始化略 ...... answer = create_answer_array() # 創(chuàng)建答案矩陣 # 主循環(huán) ---------------------------------- while True: screen.fill(white) # 背景填充 for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() # 退出pygame sys.exit() # 安全退出系統(tǒng) if event.type == pygame.MOUSEBUTTONDOWN: # 鼠標(biāo)點(diǎn)擊事件 x, y = event.pos check_click(items, x, y) # 檢查選中的方格,修改狀態(tài) result = get_player_array(items) # 獲取方格操作矩陣 print(result) if result == answer: print("YOU WIN!") change_color(items) # 根據(jù)方格狀態(tài)修改顏色 draw_line() # 繪制棋盤網(wǎng)格線 pygame.display.flip() # 更新全部顯示
3、顯示提示信息
沒有提示只能盲點(diǎn)怎么玩呀!趕緊把提示信息搞出來(lái) (ε(#)☆╰╮o(皿///)
前文我們已經(jīng)了解了提示算法,接下來(lái)就根據(jù)答案矩陣來(lái)整理兩側(cè)的提示信息。
def get_w_remind(answer_lst): # 根據(jù)答案矩陣輸出提示列表 h_remind = [] v_remind = [] h_array = [answer_lst[i: i+size] for i in range(0, len(answer_lst), size)] # 橫向矩陣 for h in h_array: h_remind.append(get_line_remind(h)) v_array = list(map(list, zip(*h_array))) # 縱向矩陣 for v in v_array: v_remind.append(get_line_remind(v)) return h_remind, v_remind
由于筆者是直接使用一維列表來(lái)代替矩陣的,因此如果要獲取每一行的提示,則需要按照 size
將將答案陣列分割成多份。而要獲取每一列的提示時(shí),則需要對(duì)分割好的橫向矩陣進(jìn)行行列轉(zhuǎn)置。
之后,通過(guò)億點(diǎn)點(diǎn)數(shù)學(xué)計(jì)算得到兩側(cè)信息的顯示坐標(biāo),利用窗口對(duì)象的 blit()
方法將渲染好的文本對(duì)象貼上去。對(duì)橫/縱陣列逆序的目的是,將多個(gè)提示數(shù)值從外到內(nèi)顯示,以符合閱讀習(xí)慣。
def show_remind(answer_lst): # 在棋盤兩側(cè)對(duì)應(yīng)位置顯示每行/列的提示 h_remind, v_remind = get_w_remind(answer_lst) for i, h in enumerate(h_remind): for j, num in enumerate(h[::-1]): text = font.render(f"{num}", True, black) screen.blit(text, (start_x - 20 * (j + 1), start_y + i * length + length / 2 - 10)) for i, v in enumerate(v_remind): for j, num in enumerate(v[::-1]): text = font.render(f"{num}", True, black) screen.blit(text, (start_x + i * length + length / 2 - 5, start_y - 30 * (j + 1)))
終于可以玩啦,來(lái)瞅瞅。
至此,馬賽邏輯的核心玩法已經(jīng)實(shí)現(xiàn),之后再完善一下游戲機(jī)制和體驗(yàn)效果,例如:修改難度、添加音效等,就可以打造一個(gè)相對(duì)完備的小游戲啦?。?s>為了偷懶 )限于篇幅,在此不作贅述,可通過(guò)文末的方式獲取帶注釋的最終版源碼參考學(xué)習(xí)。
四、游戲演示視頻
最終打包的游戲演示見下方視頻,完整源碼及打包后的游戲文件可通過(guò)以下方式獲?。╡xe 文件可能會(huì)被誤殺,需添加信任),感謝各位的閱讀。
源碼及游戲戳:http://xiazai.jb51.net/202109/yuanma/mosailogic_jb51.rar
以上就是pygame開發(fā):馬賽邏輯小游戲的代碼實(shí)現(xiàn)的詳細(xì)內(nèi)容,更多關(guān)于pygame開發(fā):馬賽邏輯小游戲的資料請(qǐng)關(guān)注本站其它相關(guān)文章!
版權(quán)聲明:本站文章來(lái)源標(biāo)注為YINGSOO的內(nèi)容版權(quán)均為本站所有,歡迎引用、轉(zhuǎn)載,請(qǐng)保持原文完整并注明來(lái)源及原文鏈接。禁止復(fù)制或仿造本網(wǎng)站,禁止在非www.sddonglingsh.com所屬的服務(wù)器上建立鏡像,否則將依法追究法律責(zé)任。本站部分內(nèi)容來(lái)源于網(wǎng)友推薦、互聯(lián)網(wǎng)收集整理而來(lái),僅供學(xué)習(xí)參考,不代表本站立場(chǎng),如有內(nèi)容涉嫌侵權(quán),請(qǐng)聯(lián)系alex-e#qq.com處理。