pyera快速教程: 從貪吃蛇開始

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)出:

image.png

右鍵刪除時(shí)如果彈出窗口,記得勾掉sage delete 和search in comments and strings. 否則可能不讓刪除那些文件彼棍。

image.png

清理過后的窗口如圖

image.png

雙擊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)行窗口的膳算。如圖:

image.png

不妨在open_func函數(shù)中寫下一句:

def open_func():
    game.pl('pyera貪吃蛇游戲加載中')

再次運(yùn)行后可以看到

image.png

你所寫下的內(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

image.png

輸入:map.json“娱俺,點(diǎn)擊確定。

image.png

雙擊打開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ì)改變。

image.png

這里解釋一下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)行后如圖:


image.png

點(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è)換行逸尖。完成后如圖:


image.png

至此,開始界面就算搭建完成了。代碼如下:

# -*- 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)需要注意:

  1. 循環(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

  2. continue跨扮,因?yàn)槊總€(gè)位置只用繪制一種元素序无,因此繪制之后直接用continue跳過后面的循環(huán)部分。

  3. 每次內(nèi)循環(huán)完成的時(shí)候衡创,都調(diào)用game.pl()另起一行帝嗡,開始下一行打印。

  4. 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é)果:


image.png

其中黃色表示蛇动看,大白點(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()
  1. 全局變量direction用'shang'你踩,‘xia’,‘zuo’讳苦,‘you’表示前進(jìn)方向带膜,初始值為‘xia’。
  2. Next_step()函數(shù)中第一步依然是準(zhǔn)備變量鸳谜。
  3. 第二步是判斷mapdata中是否右蘋果膝藕,如果沒有就創(chuàng)造一個(gè)蘋果。這里mapdata.values()是返回dict中所有的值
  4. 第三部更新蛇的身體卿堂,這里身體不包含頭部(snake_list[0]). 更新的思路很簡(jiǎn)單蛇的每一塊身體都位置都為前一塊身體的位置束莫。比如 (2,0)(1,0)(0,0)->(,)(2,0)(1,0)
  5. 第四步是更新蛇頭的位置。首先取出蛇頭位置草描,然后根據(jù)direction的不同使x/y加減1览绿,最后再賦值給蛇頭snake_list[0]
  6. 最后返回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))
  1. game.clr_cmd() 的作用是清除之前所有的命令按鈕,以防誤操作逛绵。通常建立新的界面的時(shí)候使用
  2. draw_map() 根據(jù)地圖數(shù)據(jù)和蛇身數(shù)據(jù)繪制地圖
  3. 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提供的各種功能能夠有效減少我們的工作量硕并,是我們更加集中于游戲本身的邏輯上法焰。
  4. 狀態(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è)半角字符毙玻。
  5. 繪制命令(上下左右):這里create_func()就顯出其作用了豌蟋,create_func 會(huì)給出一個(gè)新的函數(shù)。對(duì)于‘左‘按鈕桑滩,func()會(huì)使direction為’zuo‘梧疲。以此類推。
  6. Game.pl('\n')的目的是換兩行运准。因?yàn)間ame.pl()函數(shù)只會(huì)在當(dāng)前行不為空的時(shí)候另起一行幌氮。因此只能換行一次。要換兩行的話胁澳,需要再輸出一個(gè)換行符, 即game.pl('\n')
  7. 存儲(chǔ)游戲按鈕该互。參數(shù)依然是點(diǎn)擊‘退出’按鈕的時(shí)候,會(huì)返回哪個(gè)函數(shù)韭畸。這里說明一下存儲(chǔ)讀取的原理宇智。木人狀態(tài)下,存儲(chǔ)和讀取實(shí)際上就是對(duì)game.data的存儲(chǔ)和讀取胰丁。因此任何存放在game.data中的數(shù)據(jù)都會(huì)被存儲(chǔ)和讀取随橘。
  8. 讀取游戲按鈕。和之前一樣锦庸,有所區(qū)別的是第一參數(shù)為main_func机蔗,意思是點(diǎn)退出按鈕的時(shí)候會(huì)退出到main_func

運(yùn)行一下如下:


image.png

蛇已經(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()
  1. 新添加代碼有兩部分。首先是記錄蛇身體最后一節(jié)的位置汇恤。目的是一旦吃蘋果需要添加蛇身,則直接添加在這個(gè)位置上拔恰。因此需要提前記錄一下這個(gè)位置因谎。
  2. 獲得蘋果位置。首先定義一個(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)作。
  3. 判斷是否吃到蘋果很簡(jiǎn)單夷恍,就是蛇頭的位置是否與蘋果位置相等魔眨。
  4. 如果相等的話,則先讓蘋果消失酿雪,即把所有地圖數(shù)據(jù)都值為0.
  5. 然后延長(zhǎng)折身體遏暴,用append函數(shù)將last_snake_position再附加到snake_list中去。

此時(shí)運(yùn)行指黎,可以發(fā)現(xiàn)當(dāng)蛇頭吃的到蘋果的時(shí)候朋凉,蛇身體延長(zhǎng)。


image.png

不過會(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()
  1. 身體碰撞檢測(cè)徙垫,這里注意是snake_list[1::]表示從snake_list[1]到最后所有的元素。這句話的意思放棒,蛇頭是否碰到身體的其他部位姻报。
  2. 超出邊界檢測(cè),即檢測(cè)蛇頭的x和y是否小于0或者大于等于map_size. 如前幾節(jié)所說地圖的范圍是0<=x,y<=map_size-1
  3. 當(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)行后死亡情況如圖:


image.png

自此摩泪,所有的貪吃蛇開發(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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末项棠,一起剝皮案震驚了整個(gè)濱河市悲雳,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌香追,老刑警劉巖合瓢,帶你破解...
    沈念sama閱讀 218,122評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異透典,居然都是意外死亡晴楔,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門峭咒,熙熙樓的掌柜王于貴愁眉苦臉地迎上來税弃,“玉大人,你說我怎么就攤上這事凑队≡蚬” “怎么了?”我有些...
    開封第一講書人閱讀 164,491評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵漩氨,是天一觀的道長(zhǎng)西壮。 經(jīng)常有香客問我,道長(zhǎng)叫惊,這世上最難降的妖魔是什么款青? 我笑而不...
    開封第一講書人閱讀 58,636評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮霍狰,結(jié)果婚禮上抡草,老公的妹妹穿的比我還像新娘。我一直安慰自己蔗坯,他們只是感情好康震,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,676評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著宾濒,像睡著了一般腿短。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,541評(píng)論 1 305
  • 那天答姥,我揣著相機(jī)與錄音,去河邊找鬼谚咬。 笑死鹦付,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的择卦。 我是一名探鬼主播敲长,決...
    沈念sama閱讀 40,292評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼秉继!你這毒婦竟也來了祈噪?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,211評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤尚辑,失蹤者是張志新(化名)和其女友劉穎辑鲤,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體杠茬,經(jīng)...
    沈念sama閱讀 45,655評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡月褥,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,846評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了瓢喉。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片宁赤。...
    茶點(diǎn)故事閱讀 39,965評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖栓票,靈堂內(nèi)的尸體忽然破棺而出决左,到底是詐尸還是另有隱情,我是刑警寧澤走贪,帶...
    沈念sama閱讀 35,684評(píng)論 5 347
  • 正文 年R本政府宣布佛猛,位于F島的核電站,受9級(jí)特大地震影響厉斟,放射性物質(zhì)發(fā)生泄漏挚躯。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,295評(píng)論 3 329
  • 文/蒙蒙 一擦秽、第九天 我趴在偏房一處隱蔽的房頂上張望码荔。 院中可真熱鬧,春花似錦感挥、人聲如沸缩搅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽硼瓣。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間堂鲤,已是汗流浹背亿傅。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留瘟栖,地道東北人葵擎。 一個(gè)月前我還...
    沈念sama閱讀 48,126評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像半哟,于是被迫代替她去往敵國(guó)和親酬滤。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,914評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容