1. 游戲設(shè)計(jì)與數(shù)據(jù)設(shè)計(jì)
感謝您選擇pyera,如果有任何問題想反饋和討論可以加群:
py/era 技術(shù)吹水群 543014634
完整貪吃蛇代碼參考:https://github.com/qsjl11/pyera_snake
貪吃蛇是一款家喻戶曉的游戲爵政,因?yàn)橛螒蛳到y(tǒng)非常簡(jiǎn)單涵卵,因此合適作為各種引擎的練習(xí)用融求,這里我們也用這個(gè)作為一個(gè)練習(xí)的起點(diǎn)蓉冈。
在正式碼代碼之前明未,我們要做好設(shè)計(jì)工作瞳别。好的準(zhǔn)備才能保證后期的順利征候。
作為教程本游戲的設(shè)計(jì)比較粗淺,只要大家能夠明白意思就好祟敛。
游戲設(shè)計(jì):
1. 游戲本身采用n*n的正方形矩陣疤坝。n是可以自定義的
2. 每個(gè)矩陣包含3種狀態(tài),分別是蘋果馆铁、蛇身子和空地跑揉。
3. 每一回合,蛇頭會(huì)向指定方向挪動(dòng)一格。后面的蛇身會(huì)跟隨前一蛇身移動(dòng)历谍。
4. 蘋果隨機(jī)出現(xiàn)在空地上现拒,直到被蛇吃掉。
5. 每吃掉一格蘋果望侈,蛇身會(huì)加長(zhǎng)一格
6. 當(dāng)蛇頭前進(jìn)方向受到邊界或者蛇身阻礙的時(shí)候印蔬,游戲結(jié)束
7. 地圖左上角為(0,0)脱衙,向右為x軸侥猬,向下為y軸
8. 游戲可以保存和讀取
數(shù)據(jù)設(shè)計(jì):
1. 設(shè)置地圖大小的變量map_size
2. 地圖保存在dict中,dict的鍵由元胞(x,y)構(gòu)成岂丘,值為整數(shù)【0陵究,1】,分別代表空地奥帘,蘋果
3. 蛇身體由一個(gè)list保存,每個(gè)元素是一個(gè)含有兩個(gè)元素的list仪召,表示x寨蹋,y坐標(biāo)
4. 一個(gè)list [x,y] 表示蘋果的位置。
流程設(shè)計(jì):
1. 開始游戲/讀取游戲
2. 游戲循環(huán)
* 根據(jù)地圖矩陣來繪制地圖
* 繪制命令扔茅,【上下左右】【保存讀取】
* 等待輸入
* 移動(dòng)蛇已旧,更新地圖信息
* 判斷蛇能夠吃到蘋果,能吃到的話召娜,長(zhǎng)度+1
2. 配置開發(fā)環(huán)境
工欲善其事必先利其器运褪。開發(fā)環(huán)境是非常重要的。這里不贅述基本的配置方法玖瘸,只討論涉及本游戲的配置秸讹。基本配置方法請(qǐng)參考文檔【開發(fā)環(huán)境配置】雅倒,選擇‘完全配置環(huán)境”進(jìn)行配置璃诀。
文檔 http://www.reibang.com/p/79096b775d40
當(dāng)基本配置完成之后,我們需要先刪除一些多余的東西蔑匣。因?yàn)樵创a包中含有很多展示性的東西劣欢,這些東西對(duì)于我們來說是沒有必要的。
這里要說明裁良,data文件夾保存配置數(shù)據(jù)凿将,script保存運(yùn)行的游戲腳本,因此只有這兩個(gè)文件中有內(nèi)容需要?jiǎng)h除价脾。Pyera_engine和build.py文件請(qǐng)勿刪除牧抵。
要保留的內(nèi)容有由紅線標(biāo)出:
右鍵刪除時(shí)如果彈出窗口,記得勾掉sage delete 和search in comments and strings. 否則可能不讓刪除那些文件彼棍。
清理過后的窗口如圖
雙擊mainflow.py, 將里面的代碼全部刪除后改為并保存:
# -*- coding: UTF-8 -*-
import core.game as game
import script.base_lib as lib
def open_func():
pass
def main_func():
pass
此時(shí)右鍵運(yùn)行pyera_engine/pyera.py盡管沒有內(nèi)容灭忠,但是還是可以打開運(yùn)行窗口的膳算。如圖:
不妨在open_func函數(shù)中寫下一句:
def open_func():
game.pl('pyera貪吃蛇游戲加載中')
再次運(yùn)行后可以看到
你所寫下的內(nèi)容已經(jīng)出現(xiàn)在了游戲窗口當(dāng)中。
至此弛作,配置環(huán)境的工作完成了涕蜂。
3. 簡(jiǎn)單的數(shù)據(jù)配置
根據(jù)之前的游戲設(shè)計(jì),需要有一個(gè)數(shù)據(jù)來保存map_size映琳。本章中机隙,則展現(xiàn)pyera的數(shù)據(jù)配置功能。
pyera中所有的預(yù)先設(shè)定的數(shù)據(jù)都在data文件夾中萨西。
“core_cfg.json”包含系統(tǒng)的基本配置有鹿。
“core_event_sort.json"則與事件順序系統(tǒng)有關(guān),暫時(shí)不用管這個(gè)功能谎脯。
本章節(jié)中將建立一個(gè)名為“map”的json數(shù)據(jù)文件葱跋,里面保存一個(gè)“map_size"的變量。
新建map文件源梭。右鍵點(diǎn)擊data文件夾->new->file
輸入:map.json“娱俺,點(diǎn)擊確定。
雙擊打開map.json文件废麻,并輸入一下代碼荠卷,注意大括號(hào)和雙引號(hào)為英文半角字符:
{
"map_size": 10
}
Ctrl+s 保存后數(shù)據(jù)配置就算結(jié)束了,但是怎么調(diào)用呢烛愧。這里演示一下油宜。打開mainflow.py, 將open_func函數(shù)改為:
def open_func():
game.pl('pyera貪吃蛇游戲加載中')
map_size=game.data['map']['map_size']
game.pl('地圖大小設(shè)置為 '+str(map_size))
運(yùn)行pyera.py,可以看到結(jié)果怜姿。如果更改map.json文件中的數(shù)值的話慎冤,游戲內(nèi)的輸出也會(huì)改變。
這里解釋一下open_func中的各句意思
a. 第一句單純的輸出社牲。game.pl() 函數(shù)輸出一句話并自動(dòng)換行.
b. 第二句為讀取數(shù)據(jù)粪薛。game.data是一個(gè)dict,第一個(gè)鍵值為文件名搏恤,第二鍵值為保存的內(nèi)容违寿。
c. 第三句輸出map_size, str()函數(shù)用來使map_size變?yōu)樽址炜眨M(jìn)而可以與其他字符串做+運(yùn)算藤巢。
4. 開始寫代碼,“開始游戲”與”讀取游戲“
本節(jié)將構(gòu)建開始游戲界面息罗,并利用現(xiàn)成的存儲(chǔ)/讀取游戲界面掂咒。
如前所述,mianflow.py/open_func()是程序的入口。現(xiàn)在我們將利用open_func()中添加一條分割線和兩個(gè)按鈕:“開始游戲”和“讀取游戲”
添加分割線
在open_func()添加“game.pline()“即可绍刮,可以運(yùn)行測(cè)試一下温圆,會(huì)多出一條線。
添加讀取游戲按鈕
open_func()中接如下代碼
game.pcmd('[001] 讀取游戲', 1, lib.load_func, arg=(open_func,))
這里詳細(xì)說一下pcmd命令的使用方法孩革。pcmd會(huì)打印一個(gè)字符串岁歉。當(dāng)點(diǎn)擊這個(gè)字符串或者輸入對(duì)應(yīng)數(shù)字命令的時(shí)候,程序會(huì)跳轉(zhuǎn)到對(duì)應(yīng)命令指定的函數(shù)膝蜈,并可以傳遞參數(shù)锅移。這里,
第一個(gè)參數(shù)是顯示的字符串饱搏;
第二參數(shù)是對(duì)應(yīng)的蘇子命令非剃,這里是1;
第三個(gè)參數(shù)是跳轉(zhuǎn)函數(shù) 為 lib.load_func推沸,
第四個(gè)為傳遞的參數(shù)备绽,為open_func。
Savelaod.load_func就是讀取列表函數(shù)鬓催,該函數(shù)的參數(shù)表示當(dāng)執(zhí)行“退出”命令時(shí)執(zhí)行哪個(gè)函數(shù)疯坤。這里,參數(shù)open_func的意思是如果不去讀檔直接退出的話深浮,則執(zhí)行open_func。
注意:這里open_func后面不加括號(hào)眠冈!
至此飞苇,mainflow文件應(yīng)當(dāng)如下:
# -*- coding: UTF-8 -*-
import core.game as game
import script.base_lib as lib
def open_func():
game.pl('pyera貪吃蛇游戲加載中')
map_size=game.data['map']['map_size']
game.pl('地圖大小設(shè)置為 '+str(map_size))
game.pline()
game.pcmd('[001] 讀取游戲', 1, lib.load_func, arg=(open_func,))
def main_func():
pass
運(yùn)行后如圖:
點(diǎn)擊讀取游戲或者在輸入欄輸入‘1’可以進(jìn)入讀取界面,退出則返回蜗顽,大家可以自己嘗試一下布卡。
添加新的游戲按鈕
盡管還有其他的輸入命令的方式,pyera建議使用關(guān)聯(lián)函數(shù)的辦法繪制按鈕雇盖。其他方式可以參考詳細(xì)文檔忿等。
添加開始游戲按鈕,需要如下幾步:
a. 添加一個(gè)新函數(shù)崔挖,用來做處理開始游戲的各種變量設(shè)置贸街。添加函數(shù) def newgame_func()
b. 在open_func中追加輸出命令,將開始游戲關(guān)聯(lián)到 newgame_func()上
game.pcmd('[002] 開始游戲', 2, newgame_func)
相關(guān)部分代碼文件如下:
def open_func():
game.pl('pyera貪吃蛇游戲加載中')
map_size=game.data['map']['map_size']
game.pl('地圖大小設(shè)置為 '+str(map_size))
game.pline()
game.pcmd('[001] 讀取游戲', 1, lib.load_func, arg=(open_func,))
game.pcmd('[002] 開始游戲', 2, newgame_func)
def newgame_func():
pass
版面修整狸相。
運(yùn)行后我們會(huì)發(fā)現(xiàn)薛匪,此時(shí)兩個(gè)命令緊緊的挨在一起不好看。最好是分成兩行脓鹃。
因此在兩個(gè)pcmd命令之間加入一句話 game.pl() 這樣可以引入一個(gè)換行逸尖。完成后如圖:
至此,開始界面就算搭建完成了。代碼如下:
# -*- coding: UTF-8 -*-
import core.game as game
import script.base_lib as lib
def open_func():
game.pl('pyera貪吃蛇游戲加載中')
map_size=game.data['map']['map_size']
game.pl('地圖大小設(shè)置為 '+str(map_size))
game.pline()
game.pcmd('[001] 讀取游戲', 1, lib.load_func, arg=(open_func,))
game.pl()
game.pcmd('[002] 開始游戲', 2, newgame_func)
def newgame_func():
pass
def main_func():
pass
def open_menu():
pass
newgame_func的具體工作在下一節(jié)中講解娇跟。
5. newgame_func與相關(guān)函數(shù)
在點(diǎn)擊開始游戲后岩齿,會(huì)執(zhí)行newgame_func。這個(gè)函數(shù)將用來做一些初始化工作, newgame_func執(zhí)行后會(huì)執(zhí)行main_func苞俘。這里先把整段函數(shù)寫出來盹沈。再一一解釋
def newgame_func():
# 初始化地圖數(shù)據(jù)dic,0是空地苗胀,1是蘋果
map_size = game.data['map']['map_size']
game.data['mapdata'] = {}
for y in range(0, map_size):
for x in range(0, map_size):
game.data['mapdata'][(x, y)] = 0
# 初始化蛇list, 三格長(zhǎng)度襟诸,靠左下角放置
snake_list = []
snake_list.append((0, 2))
snake_list.append((0, 1))
snake_list.append((0, 0))
game.data['snake_list'] = snake_list
main_func()
def main_func():
# 添加一個(gè)蘋果
create_apple()
#繪制地圖
draw_map()
初始化地圖數(shù)據(jù)
初始化過程的第一步就是獲得map_size。然后建立一個(gè)空dict來存儲(chǔ)地圖數(shù)據(jù)基协。地圖數(shù)據(jù)以鍵值方式存儲(chǔ)歌亲。鍵是一個(gè)(x,y)的元胞,而值是‘0’空地或者‘1’蘋果澜驮。不過初始化的過程中全部用0.
需要注意的是range函數(shù)陷揪。Range( 1,3)表示[1,2]杂穷,而不是[1,2,3]悍缠,因此這里的x和y的取值范圍為0<=x,y<map_size.
初始化蛇list。
首先建立一個(gè)空list耐量,然后用append函數(shù)來附加位置飞蚓。分別是(0,2),(0,1),(0,0).這里list[0]元素為蛇頭。
進(jìn)入main_func()
Main_func()是游戲的主體函數(shù)廊蜒,不過這里只完成制作蘋果和顯示兩個(gè)步驟趴拧,以示效果。
添加蘋果
添加蘋果是一個(gè)單獨(dú)的函數(shù)create_apple(), 函數(shù)如下:
def create_apple():
map_size = game.data['map']['map_size']
# 隨機(jī)蘋果的位置山叮,當(dāng)位置與蛇重合的時(shí)候著榴,重新隨機(jī)
import random
apple_position = (random.randint(0, map_size - 1), random.randint(0, map_size - 1))
while apple_position in game.data['snake_list']:
apple_position = (random.randint(0, map_size - 1), random.randint(0, map_size - 1))
# 將mapdata中的對(duì)應(yīng)蘋果位置改為1
game.data['mapdata'][apple_position] = 1
首先依然是獲得map_size, 然后使用random模塊中的randint()函數(shù)獲得一個(gè)隨機(jī)整數(shù)。這里需要注意的是屁倔,因?yàn)閞andint(i,j)的到整數(shù)為 i<=value<=j脑又,因此其參數(shù)應(yīng)當(dāng)為(0,map_size-1)锐借。否則隨機(jī)的值可能正好為map_size问麸,但實(shí)際坐標(biāo)中最大值為map_size-1,會(huì)照成溢出瞎饲,引起程序錯(cuò)誤口叙。
while循環(huán)的目的是,當(dāng)蘋果隨機(jī)的位置恰好落于蛇身體上的時(shí)候嗅战,則重新隨機(jī)妄田,直到蘋果不產(chǎn)生再蛇身上為止俺亮。這樣最后apple_position 就是一個(gè)有效的位置。
最后將mapdata中對(duì)應(yīng)位置的值改為1疟呐,表示該位置是蘋果脚曾。
繪制地圖draw_map(), 其內(nèi)容如下:
def draw_map():
map_size = game.data['map']['map_size']
snake_list=game.data['snake_list']
mapdata=game.data['mapdata']
for y in range(0, map_size):
for x in range(0, map_size):
pos=(x,y)
if pos in snake_list:
game.p('?', style='special')
continue
if mapdata[pos]==1:
game.p('?')
continue
if mapdata[pos]==0:
game.p('﹒')
continue
game.pl()
為了方便調(diào)用,map_size, snake_list 和mapdata都用單獨(dú)的變量引用启具。通過雙重循環(huán)本讥,判斷如果是蛇身部分,則繪制'?'鲁冯, 蘋果繪制('?')拷沸, 空地用'﹒'
這里有幾點(diǎn)需要注意:
循環(huán)的時(shí)候是外循環(huán)為y,內(nèi)循環(huán)為x薯演,目的是按照(0,0)->(1,0)->(2,0)->…->(0,1)->(1,1)->…的方式進(jìn)行循環(huán)撞芍。因此繪制的時(shí)候是一行一行輸出的,只能先變x后變y
continue跨扮,因?yàn)槊總€(gè)位置只用繪制一種元素序无,因此繪制之后直接用continue跳過后面的循環(huán)部分。
每次內(nèi)循環(huán)完成的時(shí)候衡创,都調(diào)用game.pl()另起一行帝嗡,開始下一行打印。
game.p('?', style='special')中的style是可以自行定義的璃氢,具體可以參考詳細(xì)文檔哟玷。
目前完整的mainflow.py 如下:
# -*- coding: UTF-8 -*-
import core.game as game
import script.base_lib as lib
def open_func():
game.pl('pyera貪吃蛇游戲加載中')
map_size = game.data['map']['map_size']
game.pl('地圖大小設(shè)置為 ' + str(map_size))
game.pline()
game.pcmd('[001] 讀取游戲', 1, lib.load_func, arg=(open_func,))
game.pl()
game.pcmd('[002] 開始游戲', 2, newgame_func)
def newgame_func():
# 初始化地圖數(shù)據(jù)dic,0是空地一也,1是蘋果
map_size = game.data['map']['map_size']
game.data['mapdata'] = {}
for y in range(0, map_size):
for x in range(0, map_size):
game.data['mapdata'][(x, y)] = 0
# 初始化蛇list, 三格長(zhǎng)度碗降,靠左下角放置
snake_list = []
snake_list.append((0, 2))
snake_list.append((0, 1))
snake_list.append((0, 0))
game.data['snake_list'] = snake_list
main_func()
def create_apple():
map_size = game.data['map']['map_size']
# 隨機(jī)蘋果的位置,當(dāng)位置與蛇重合的時(shí)候塘秦,重新隨機(jī)
import random
apple_position = (random.randint(0, map_size - 1), random.randint(0, map_size - 1))
while apple_position in game.data['snake_list']:
apple_position = (random.randint(0, map_size - 1), random.randint(0, map_size - 1))
# 將mapdata中的對(duì)應(yīng)蘋果位置改為1
game.data['mapdata'][apple_position] = 1
def draw_map():
map_size = game.data['map']['map_size']
snake_list=game.data['snake_list']
mapdata=game.data['mapdata']
for y in range(0, map_size):
for x in range(0, map_size):
pos=(x,y)
if pos in snake_list:
game.p('?', style='special')
continue
if mapdata[pos]==1:
game.p('?')
continue
if mapdata[pos]==0:
game.p('﹒')
continue
game.pl()
def main_func():
# 添加一個(gè)蘋果
create_apple()
# 繪制地圖
draw_map()
當(dāng)運(yùn)行并點(diǎn)擊開始游戲的時(shí)候,應(yīng)該有如下結(jié)果:
其中黃色表示蛇动看,大白點(diǎn)是蘋果尊剔,小白點(diǎn)是空地。
下一節(jié)將講述main_func()的構(gòu)建菱皆,來實(shí)現(xiàn)完整顯示與菜單须误。
6. Main_func的編寫 移動(dòng)與菜單
本節(jié)中將完善main_func, 并創(chuàng)建菜單,使蛇可以真正的移動(dòng)仇轻。同時(shí)演示保存和讀取功能京痢。注意,本章中將使用python中的閉包特性篷店,如果感到難以理解的話祭椰,可以多多百度也可加群討論臭家。
一個(gè)關(guān)于閉包的參考資料:http://blog.csdn.net/ChangerJJLee/article/details/52598629
本節(jié)中修改的函數(shù)為main_func,并添加一個(gè)函數(shù)next_step()和一個(gè)全局變量direction方淤。
Next_step()的功能是生成蘋果并更新蛇的位置钉赁,direction指示蛇前進(jìn)的方向。
direction = 'xia'
def next_step():
# 方便調(diào)用
global direction
snake_list = game.data['snake_list']
mapdata = game.data['mapdata']
# 判斷是否有蘋果携茂,沒有就創(chuàng)造一個(gè)
if 1 not in mapdata.values():
create_apple()
# 更新蛇身
for i in range(len(snake_list) - 1, 0, -1):
snake_list[i] = snake_list[i - 1]
# 更新蛇頭位置
x_head = snake_list[0][0]
y_head = snake_list[0][1]
if direction == 'zuo':
x_head = x_head - 1
if direction == 'you':
x_head = x_head + 1
if direction == 'shang':
y_head = y_head - 1
if direction == 'xia':
y_head = y_head + 1
snake_list[0] = (x_head, y_head)
# 返回主函數(shù)
main_func()
- 全局變量direction用'shang'你踩,‘xia’,‘zuo’讳苦,‘you’表示前進(jìn)方向带膜,初始值為‘xia’。
- Next_step()函數(shù)中第一步依然是準(zhǔn)備變量鸳谜。
- 第二步是判斷mapdata中是否右蘋果膝藕,如果沒有就創(chuàng)造一個(gè)蘋果。這里mapdata.values()是返回dict中所有的值
- 第三部更新蛇的身體卿堂,這里身體不包含頭部(snake_list[0]). 更新的思路很簡(jiǎn)單蛇的每一塊身體都位置都為前一塊身體的位置束莫。比如 (2,0)(1,0)(0,0)->(,)(2,0)(1,0)
- 第四步是更新蛇頭的位置。首先取出蛇頭位置草描,然后根據(jù)direction的不同使x/y加減1览绿,最后再賦值給蛇頭snake_list[0]
- 最后返回main_func()
目前next_step()函數(shù)還沒有全部完成,剩下的部分將在下一節(jié)中完成穗慕。
下面將寫main_func()有什么變化饿敲,并對(duì)其中較為關(guān)鍵的語句進(jìn)行解釋:
def main_func():
# 新界面準(zhǔn)備
game.clr_cmd()
game.pline()
# 畫圖
draw_map()
def create_func(direction_name):
def func():
global direction
direction = direction_name
next_step()
return func
# 狀態(tài)顯示
game.pline('--')
game.pl('分?jǐn)?shù):')
# 繪制命令按鈕
game.pline('--')
game.pcmd('[1] 左 ', 1, create_func('zuo'))
game.pcmd('[2] 上 ', 2, create_func('shang'))
game.pcmd('[3] 右 ', 3, create_func('you'))
game.pl()
game.p(' ')
game.pcmd('[4] 下 ', 4, create_func('xia'))
game.pl('\n')
game.pcmd('[5] 存儲(chǔ)游戲 ', 5, lib.save_func, arg=main_func)
game.pcmd('[6] 讀取游戲 ', 6, lib.load_func, arg=(main_func, main_func))
- game.clr_cmd() 的作用是清除之前所有的命令按鈕,以防誤操作逛绵。通常建立新的界面的時(shí)候使用
- draw_map() 根據(jù)地圖數(shù)據(jù)和蛇身數(shù)據(jù)繪制地圖
- Create_func()是一個(gè)較難理解點(diǎn)怀各,這個(gè)函數(shù)的作用是制作另一個(gè)函數(shù),即期內(nèi)部的函數(shù)func().func()的內(nèi)容有一下幾點(diǎn)术浪,聲明使用全局變量direction瓢对,設(shè)置direction為dirction_name, 最后調(diào)用next_setp(). 當(dāng)給定不同的direction_name的時(shí)候,產(chǎn)生的func()會(huì)有所區(qū)別胰苏。之所以這么寫的目的是簡(jiǎn)化程序硕蛹。實(shí)際上,python提供的各種功能能夠有效減少我們的工作量硕并,是我們更加集中于游戲本身的邏輯上法焰。
- 狀態(tài)顯示,這里預(yù)留分?jǐn)?shù)的記錄部分倔毙。下一節(jié)再擴(kuò)展埃仪。需要注意的是game.pline('--')這個(gè)函數(shù)。當(dāng)該函數(shù)沒有參數(shù)的時(shí)候會(huì)繪制一條默認(rèn)的粗直線陕赃,但是也可以給一個(gè)參數(shù)卵蛉,這樣會(huì)根據(jù)所給的字符繪制分割線颁股。需要注意的是這個(gè)位置應(yīng)當(dāng)是一個(gè)雙字符快讀的內(nèi)容,即一個(gè)全角字符或兩個(gè)半角字符毙玻。
- 繪制命令(上下左右):這里create_func()就顯出其作用了豌蟋,create_func 會(huì)給出一個(gè)新的函數(shù)。對(duì)于‘左‘按鈕桑滩,func()會(huì)使direction為’zuo‘梧疲。以此類推。
- Game.pl('\n')的目的是換兩行运准。因?yàn)間ame.pl()函數(shù)只會(huì)在當(dāng)前行不為空的時(shí)候另起一行幌氮。因此只能換行一次。要換兩行的話胁澳,需要再輸出一個(gè)換行符, 即game.pl('\n')
- 存儲(chǔ)游戲按鈕该互。參數(shù)依然是點(diǎn)擊‘退出’按鈕的時(shí)候,會(huì)返回哪個(gè)函數(shù)韭畸。這里說明一下存儲(chǔ)讀取的原理宇智。木人狀態(tài)下,存儲(chǔ)和讀取實(shí)際上就是對(duì)game.data的存儲(chǔ)和讀取胰丁。因此任何存放在game.data中的數(shù)據(jù)都會(huì)被存儲(chǔ)和讀取随橘。
- 讀取游戲按鈕。和之前一樣锦庸,有所區(qū)別的是第一參數(shù)為main_func机蔗,意思是點(diǎn)退出按鈕的時(shí)候會(huì)退出到main_func
運(yùn)行一下如下:
蛇已經(jīng)可以根據(jù)上下左右移動(dòng)了。
后續(xù)工作:盡管可以移動(dòng)甘萧,并且存儲(chǔ)和讀取功能都已完成萝嘁。但是目前蛇不會(huì)和蘋果交互也不會(huì)碰到墻壁死亡。下一節(jié)中我們將對(duì)next_step 更改以實(shí)現(xiàn)更多的功能
7. 擴(kuò)展next_step(), 吃蘋果與死亡
本節(jié)將擴(kuò)展next_step的功能扬卷,首先我們擴(kuò)展吃蘋果的功能牙言,然后擴(kuò)展碰撞死亡的功能。
def next_step():
# 方便調(diào)用
global direction
snake_list = game.data['snake_list']
mapdata = game.data['mapdata']
# 判斷是否有蘋果怪得,沒有就創(chuàng)造一個(gè)
if 1 not in mapdata.values():
create_apple()
# 新添加**************************
# 記錄蛇身體的最后一節(jié)的位置
last_snake_position = snake_list[-1]
# 更新蛇身
for i in range(len(snake_list) - 1, 0, -1):
snake_list[i] = snake_list[i - 1]
# 更新蛇頭位置
x_head = snake_list[0][0]
y_head = snake_list[0][1]
if direction == 'zuo':
x_head = x_head - 1
if direction == 'you':
x_head = x_head + 1
if direction == 'shang':
y_head = y_head - 1
if direction == 'xia':
y_head = y_head + 1
snake_list[0] = (x_head, y_head)
# 新添加**************************
apple_position=(-1,-1)
# 獲得蘋果位置
for k,v in mapdata.items():
if v==1:
apple_position=k
break
# 判斷是否吃到蘋果嬉挡,如果吃到蘋果則加一節(jié)身體
if snake_list[0] == apple_position:
# 蘋果消失,所有地格變?yōu)?
for k in mapdata:
mapdata[k]=0
#延長(zhǎng)蛇身,把動(dòng)前最后的一個(gè)位置添加到蛇身體中
snake_list.append(last_snake_position)
# 返回主函數(shù)
main_func()
- 新添加代碼有兩部分。首先是記錄蛇身體最后一節(jié)的位置汇恤。目的是一旦吃蘋果需要添加蛇身,則直接添加在這個(gè)位置上拔恰。因此需要提前記錄一下這個(gè)位置因谎。
- 獲得蘋果位置。首先定義一個(gè)變量apple_position默認(rèn)值為(-1,-1).因?yàn)樵撐恢贸龅貓D顯示范圍颜懊,所以是安全的默認(rèn)值财岔。然后通過for循環(huán)风皿,當(dāng)值為1的時(shí)候,將apple_position等于鍵k匠璧。之所以直接break是因?yàn)槲覀儜?yīng)當(dāng)只有一個(gè)蘋果桐款,因此無需多余的循環(huán)動(dòng)作。
- 判斷是否吃到蘋果很簡(jiǎn)單夷恍,就是蛇頭的位置是否與蘋果位置相等魔眨。
- 如果相等的話,則先讓蘋果消失酿雪,即把所有地圖數(shù)據(jù)都值為0.
- 然后延長(zhǎng)折身體遏暴,用append函數(shù)將last_snake_position再附加到snake_list中去。
此時(shí)運(yùn)行指黎,可以發(fā)現(xiàn)當(dāng)蛇頭吃的到蘋果的時(shí)候朋凉,蛇身體延長(zhǎng)。
不過會(huì)發(fā)現(xiàn)一個(gè)問題醋安,當(dāng)吃完蘋果后只有下一回合會(huì)才回產(chǎn)生新蘋果杂彭。因?yàn)椴挥绊懹螒蜻M(jìn)行,所以解決方法就不多說了吓揪,大家可以自己開動(dòng)腦筋解決這個(gè)問題亲怠。
然后是第二部,碰撞死亡檢測(cè)磺芭,所有的檢測(cè)都發(fā)生在最后赁炎,因此next_step 函數(shù)應(yīng)當(dāng)追加以下內(nèi)容:
# 身體碰撞檢測(cè)
if snake_list[0] in snake_list[1::]:
game.pl('蛇碰到了它的身體,你失敗了', style='special')
open_func()
return
# 超出邊界檢測(cè)
if snake_list[0][0]<0 or snake_list[0][0]>=map_size or snake_list[0][1]<0 or snake_list[0][1]>=map_size:
game.pl('蛇碰到了墻壁钾腺,你失敗了', style='special')
open_func()
return
# 返回主函數(shù)
main_func()
- 身體碰撞檢測(cè)徙垫,這里注意是snake_list[1::]表示從snake_list[1]到最后所有的元素。這句話的意思放棒,蛇頭是否碰到身體的其他部位姻报。
- 超出邊界檢測(cè),即檢測(cè)蛇頭的x和y是否小于0或者大于等于map_size. 如前幾節(jié)所說地圖的范圍是0<=x,y<=map_size-1
- 當(dāng)檢測(cè)為真后间螟。做了兩間事吴旋,第一件事是打印失敗信息。然后轉(zhuǎn)到open_func()轉(zhuǎn)到新的一句厢破,最后是return退出next_step()函數(shù)荣瑟。如果不加return的話會(huì)導(dǎo)致執(zhí)行玩open_func()函數(shù)又會(huì)繼續(xù)執(zhí)行main_func()函數(shù)。
運(yùn)行后死亡情況如圖:
自此摩泪,所有的貪吃蛇開發(fā)已經(jīng)完成了笆焰。但是依然有很多可以做的事和可以優(yōu)化的地方。這里列出幾項(xiàng):
1. 一個(gè)蘋果被吃掉见坑,立刻產(chǎn)生新的蘋果嚷掠。
2. 蛇頭采用不同的圖形繪制
3. 添加內(nèi)部墻壁
4. 即時(shí)化
5. 分?jǐn)?shù)系統(tǒng)
總之捏检,這個(gè)只是pyera的入門教程,還有很多功能沒有介紹不皆,依然列出幾項(xiàng)比較有價(jià)值的:
1. 事件系統(tǒng)
2. 設(shè)定循環(huán)主界面
3. 非綁定的輸入處理部分贯城,類似于era腳本中的$input部分
4. align排版函數(shù)
5. 文字輸入功能等。
6. 構(gòu)建exe運(yùn)行文件的功能
這些功能可能在以后擴(kuò)展貪吃蛇游戲的時(shí)候慢慢介紹到霹娄。大家也可以參考詳細(xì)文檔的部分來了解使用方法能犯。
最后感謝您選擇pyera,如果有任何問題想反饋和討論可以加群:
py/era 技術(shù)吹水群 543014634
完整貪吃蛇代碼參考:https://github.com/qsjl11/pyera_snake