python貪吃蛇


代碼地址如下:
http://www.demodashi.com/demo/13335.html

一、先展示python貪吃蛇效果

python snake

二、操作說明

按鍵 功能
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個方格組成的方陣

'''
用背景色填充屏幕(清屏)
'''
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,刷新屏幕

'''
刷新屏幕
'''
def update(self):
    pygame.display.update()

rect

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

'''
在屏幕指定位置畫一個正方形(相對坐標)

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的封裝,使用的是屏幕坐標 )

'''
在屏幕指定位置畫一個圓形(相對坐標)

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的封裝,把按鍵按下的狀態封裝成事件

'''
屏幕事件
'''
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 ,以及其他要用到的變量.

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) # 游戲得分,吃一個食物得一分

drawshow

draw用於把所有的游戲元素:食物,蛇頭,蛇身,牆按照其坐標填充到地圖中, 地圖是一個二維的 list, 形如 s_map[x][y]
show用於把貪吃蛇地圖繪制到屏幕上,並刷新屏幕,這樣貪吃蛇就顯示出來了.show需要使用一個window對象來進行屏幕操作

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

: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放到一個單獨的線程中不斷運行.

'''
移動蛇

: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

用於在子線程中運行的線程函數

'''
貪吃蛇運行線程
'''
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

貪吃蛇游戲主函數

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

五、程序運行截圖



六、項目內文件截圖

項目內文件截圖

python貪吃蛇

代碼地址如下:
http://www.demodashi.com/demo/13335.html

注:本文著作權歸作者,由demo大師代發,拒絕轉載,轉載需要作者授權


免責聲明!

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



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