天天看點

python貪吃蛇_python貪吃蛇

# 一、先展示python貪吃蛇效果

![python snake](https://raw.githubusercontent.com/WHJWNAVY/myImage/master/py_snake/py_snake.gif)

## 二、操作說明

|按鍵|功能|

|:---:|:---:|

|UP|向上移動|

|DOWN|向下移動|

|LEFT|向左移動|

|RIGHT|向右移動|

|空格|暫停/繼續|

|F1|加速|

|F2|減速|

|F3|開啟/關閉無敵模式|

|ESC|退出遊戲|

## 三、遊戲說明

本教程使用python實作了一個簡易的貪吃蛇遊戲,為了讓更多人能體會到python給我們帶來的友善和樂趣,本教程源代碼包含了詳細的注釋,同時也采用了更簡單和易于了解的方式來實作貪吃蛇遊戲.

遊戲開始時,會生成 一個 ***位置随機長度為5的蛇*** (蛇頭紅色,蛇身綠色),一個 ***位置随機的食物*** (紅色),和一堵 ***位置随機的長度最大為5的牆*** (黑色).

遊戲運作過程中,可以通過 ***方向鍵*** 控制蛇移動來吃掉食物,每吃掉一個食物蛇身長度加1,每吃掉 ***10*** 個食物遊戲速度加快一個等級,并且增加一堵位置随機長度最大為5的牆,以增加遊戲難度.

蛇移動過程中咬到自身或撞到牆就會死亡,遊戲自動退出.當然,也可以開啟 ***無敵模式*** ,讓小蛇盡情的暢遊.

## 四、源碼詳解

本遊戲的源碼共分為三個子產品: ***game子產品*** , ***window子產品*** , ***snake子產品***.

### 1、window子產品

本子產品用于實作遊戲界面的繪制和視窗事件的檢測.

本子產品提供了 ***clear(清屏)*** , ***update(重新整理)*** , ***rect(畫矩形)*** , ***circle(畫圓)*** , ***event(事件檢測)*** 等接口.

本子產品的功能主要使用pygame子產品實作,是對pygame的進一步封裝.

#### **clear**

用指定顔色填充背景,并且繪制遊戲地圖方格,遊戲地圖是一個由橫向40個方格,縱向20個方格組成的方陣

```py

'''

用背景色填充螢幕(清屏)

'''

def clear(self):

color = self._color_sub(self.COLOR_WHITE, self.gw_bgcol)

self._game_window.fill(self.gw_bgcol)

for x in range(self.maxx()+1):

pygame.draw.line(self._game_window, color, (x*self.pnt_size, 0), (x*self.pnt_size, self.gw_height), 1)

for y in range(self.maxy()+1):

pygame.draw.line(self._game_window, color, (0, y * self.pnt_size), (self.gw_width, y*self.pnt_size), 1)

```

#### **update**

pygame的update,重新整理螢幕

```py

'''

重新整理螢幕

'''

def update(self):

pygame.display.update()

```

#### **rect**

往地圖上的指定位置的小方格中畫一個矩形,這裡使用的坐标不是螢幕坐标,而是小方格在地圖中的坐标( *_rect是對pygame的draw.rect的封裝,使用的是螢幕坐标* )

```py

'''

在螢幕指定位置畫一個正方形(相對坐标)

Parameters

:param x: 正方形左上角的x坐标

:param y: 正方形左上角的y坐标

:param color: 圓形填充顔色

'''

def rect(self, x, y, *color):

pntcol = self.pnt_col

if len(color) != 0:

pntcol = color[0]

if x < 0 or x > self.maxx() or y < 0 or y > self.maxy():

return

self._rect(x*self.pnt_size, y*self.pnt_size, pntcol)

```

#### **circle**

往地圖上的指定位置的小方格中畫一個圓形,這裡使用的坐标不是螢幕坐标,而是小方格在地圖中的坐标( *_circle是對pygame的draw.circle的封裝,使用的是螢幕坐标* )

```py

'''

在螢幕指定位置畫一個圓形(相對坐标)

Parameters

:param x: 圓形外接正方形左上角的x坐标

:param y: 圓形外接正方形左上角的y坐标

:param color: 圓形填充顔色

'''

def circle(self, x, y, *color):

pntcol = self.pnt_col

if len(color) != 0:

pntcol = color[0]

if x < 0 or x > self.maxx() or y < 0 or y > self.maxy():

return

x = x*self.pnt_size

y = y*self.pnt_size

self._circle(x, y, x+self.pnt_size, y+self.pnt_size, pntcol)

```

#### **event**

檢按鍵按下的事件,是對pygame的event的封裝,把按鍵按下的狀态封裝成事件

```py

'''

螢幕事件

'''

def event(self):

for event in pygame.event.get():

if event.type == pygame.QUIT:

return self.EVENT_QUIT

elif event.type == pygame.KEYDOWN: # KEYUP:

if event.key == pygame.K_LEFT or event.key == pygame.K_a:

return self.EVENT_KLEFT

elif event.key == pygame.K_RIGHT or event.key == pygame.K_d:

return self.EVENT_KRIGHT

elif event.key == pygame.K_UP or event.key == pygame.K_w:

return self.EVENT_KUP

elif event.key == pygame.K_DOWN or event.key == pygame.K_s:

return self.EVENT_KDOWN

elif event.key == pygame.K_SPACE:

return self.EVENT_STOP

elif event.key == pygame.K_F1:

return self.EVENT_ADD

elif event.key == pygame.K_F2:

return self.EVENT_SUB

elif event.key == pygame.K_ESCAPE:

return self.EVENT_QUIT

elif event.key == pygame.K_F3:

return self.EVENT_KING

return self.EVENT_NONE

```

### 2、snake子產品

本子產品使用最簡單的方法實作了貪吃蛇原理, 包含貪吃蛇的初始化,貪吃蛇的移動和碰撞檢測,食物的生成,強的生成,貪吃蛇地圖,貪吃蛇的繪制等.

#### **\_\_init\_\_**

貪吃蛇子產品的初始化,建立了用于儲存貪吃蛇蛇身,貪吃蛇地圖的 ***list*** ,以及其他要用到的變量.

```py

def __init__(self, s_len=5, s_width=40, s_height=20): # (640/20 - 1, 480/20 -1)

self.s_width = s_width

self.s_height = s_height

self.s_life = self.SNAKE_LIFE

self._dir = self.DIR_RIGHT

self.s_king = False # 無敵模式

self.s_list = [] # 儲存貪吃蛇蛇身坐标(s_list[0]儲存食物,s_list[1]儲存蛇頭,其他儲存蛇身)

self.s_wall = [] # 儲存牆的坐标

self._create_wall() # 建立一堵牆, 強的位置随機, 方向随機, 長度最大為5

self.s_map = self._map_create(self.BODY_NONE) # 儲存貪吃蛇地圖,所有的遊戲元素都要填充到地圖中,然後統一繪制到螢幕上

# create a food, food = list[0]

_s_food = self._create_body() # 建立一個随機的坐标,标記為食物

self.s_list.append(_s_food)

# creat a head, head = list[1]

self._s_head = self._create_body() # 建立一個随機的坐标,标記為蛇頭

self.s_list.append(self._s_head)

# create body and add body to list

for _ in range(s_len-1): # 蛇身的坐标通過蛇頭的坐标和蛇的方向計算得來

self._s_head = (self._s_head[0]-1, self._s_head[1])

self.s_list.append(self._s_head)

# print(self.s_list)

self.s_score = 0 # len(self.s_list) # 遊戲得分,吃一個食物得一分

```

#### **draw** 和 **show**

draw用于把所有的遊戲元素:食物,蛇頭,蛇身,牆按照其坐标填充到地圖中, 地圖是一個二維的 ***list***, 形如 ***s_map[x][y]***

show用于把貪吃蛇地圖繪制到螢幕上,并重新整理螢幕,這樣貪吃蛇就顯示出來了.show需要使用一個window對象來進行螢幕操作

```py

'''

繪制食物和蛇(把地圖繪制到螢幕上)

:param pen: window對象

'''

def show(self, pen):

pen.clear()

self.draw()

for x in range(self.s_width):

for y in range(self.s_height):

if self.s_map[x][y] != self.BODY_NONE:

if self.s_map[x][y] == self.BODY_FOOD:

pen.circle(x, y, pen.COLOR_BLUE) # draw food

if self.s_map[x][y] == self.BODY_HEAD:

pen.rect(x, y, pen.COLOR_RED) # draw head

if self.s_map[x][y] == self.BODY_SNAKE:

pen.rect(x, y, pen.COLOR_GREEN) # draw snake

if self.s_map[x][y] == self.BODY_WALL:

pen.rect(x, y, pen.COLOR_BLACK) # draw snake

pen.update()

'''

把蛇和食物放在地圖中

'''

def draw(self):

x = 0

y = 0

self._map_init(self.s_map, self.BODY_NONE)

if len(self.s_list) != 0:

x = self.s_list[0][0]

y = self.s_list[0][1]

if x >= 0 and x < self.s_width and y >= 0 and y < self.s_height:

self.s_map[x][y] = self.BODY_FOOD # draw food

x = self.s_list[1][0]

y = self.s_list[1][1]

if x >= 0 and x < self.s_width and y >= 0 and y < self.s_height:

self.s_map[x][y] = self.BODY_HEAD # draw head

for s in range(2, len(self.s_list)): # draw snake

x = self.s_list[s][0]

y = self.s_list[s][1]

if x >= 0 and x < self.s_width and y >= 0 and y < self.s_height:

self.s_map[x][y] = self.BODY_SNAKE

if len(self.s_wall) != 0:

for w in self.s_wall:

x = w[0]

y = w[1]

if x >= 0 and x < self.s_width and y >= 0 and y < self.s_height:

self.s_map[x][y] = self.BODY_WALL

```

#### **move**

用于貪吃蛇的移動,移動過程中會進行碰撞檢測,如果撞到食物,則吃掉食物并産生一個新的食物,同時蛇身長度增加,遊戲得分增加.如果撞到自己,或撞到牆,則蛇死亡. 沒調用一個move,貪吃蛇移動一步,一般把move和show放到一個單獨的線程中不斷運作.

```py

'''

移動蛇

:param dir: 蛇移動方向

'''

def move(self, dir=DIR_RIGHT):

if self._check_dir(self._dir, dir):

self._dir = dir

head = self.s_list[1] # save head

last = self.s_list[-1] # save tail

# move the snake body fowward(copy list[n-1] to list[n])

for idx in range(len(self.s_list)-1, 1, -1):

self.s_list[idx] = self.s_list[idx-1]

head_t = self._add_xy(head, self._dir) # new head

# check snake head(cross wall)

if head_t[0] < 0:

head_t[0] = self.s_width - 1

elif head_t[0] > self.s_width - 1:

head_t[0] = 0

if head_t[1] < 0:

head_t[1] = self.s_height - 1

elif head_t[1] > self.s_height - 1:

head_t[1] = 0

chk, bd = self._check_body(head_t) # check the head

# if bd != self.BODY_NONE:

# print(chk, bd)

if chk == True and bd != self.BODY_NONE:

if bd == self.BODY_HEAD or bd == self.BODY_SNAKE or bd == self.BODY_WALL: # eat yourself or wall

if self.s_king != True: # 無敵模式

self.s_life = self.SNAKE_DIE # die

return self.s_life

else: # eat food

self.s_list.append(last) # body growth

self.s_score = self.s_score + 1 # add score

if self.s_score % 10 == 0: # 每吃10個食物增加一面牆

self._create_wall()

food = self._create_body() # create food

if food == None: # no space to create food

self.s_life = self.SNAKE_WIN

return self.s_life

self.s_list[0] = food

self.s_list[1] = head_t # update head

if len(self.s_list) == ((self.s_width * self.s_height)):

self.s_life = self.SNAKE_WIN

return self.s_life

```

### 3、game子產品

該子產品建立一個window對象和一個snake對象, 然後在一個新線程中執行snake子產品的move和show函數,實作貪吃蛇的不斷移動和繪制.在主線程中,不斷檢測視窗事件,根據視窗事件類型改變蛇的狀态.

#### **game_run**

用于在子線程中運作的線程函數

```py

'''

貪吃蛇運作線程

'''

def game_run(snake):

global dir

global stop

global speed

delay = 1.5

while True:

if stop != True:

life = snake.move(dir)

if life != snake.SNAKE_LIFE:

break # die, exit

snake.show(window)

delay = 1 - speed * 0.05

if delay < 0.05:

delay = 0.05

time.sleep(delay)

```

#### **main**

貪吃蛇遊戲主函數

```py

if __name__ == "__main__":

snake, window = game_init()

# 建立新線程,在新線程中允許和繪制貪吃蛇

gt = threading.Thread(target=game_run, args=(snake,))

gt.start()

# 主線程用于檢測按鍵事件

while True:

event = window.event()

if event != window.EVENT_NONE:

if event == window.EVENT_QUIT: # ESC退出

window.quit()

elif event == window.EVENT_KUP or \

event == window.EVENT_KDOWN or \

event == window.EVENT_KLEFT or \

event == window.EVENT_KRIGHT: # 方向鍵控制貪吃蛇移動

dir = event

elif event == window.EVENT_STOP:#空格鍵暫停和繼續

if stop == False:

stop = True

else:

stop = False

#print(dir, snake.s_life)

elif event == window.EVENT_ADD: # F1速度加

speed = speed + 1

elif event == window.EVENT_SUB: # F2速度減

speed = speed - 1

elif event == window.EVENT_KING: # F3無敵模式

if snake.s_king == True:

snake.s_king = False

else:

snake.s_king = True

if snake.s_life != snake.SNAKE_LIFE: # 如果貪吃蛇死亡則退出遊戲

window.quit()

if score != snake.s_score: # 每得10分速度增加一個等級

score = snake.s_score

if(score % 10 == 0):

speed = speed + 1

```

# 五、程式運作截圖

![](https://raw.githubusercontent.com/WHJWNAVY/myImage/master/py_snake/20180524014149.jpg)

![](https://raw.githubusercontent.com/WHJWNAVY/myImage/master/py_snake/20180524014241.jpg)

![](https://raw.githubusercontent.com/WHJWNAVY/myImage/master/py_snake/py_snake.gif)

## 六、項目内檔案截圖

![項目内檔案截圖](https://raw.githubusercontent.com/WHJWNAVY/myImage/master/py_snake/20180524014350.jpg)

上一篇: Qt貪吃蛇