Skip to content

控制蛇的行走

LanKuDot edited this page Jun 15, 2020 · 2 revisions

上一個章節初次執行遊戲,可以發現蛇不受控制地往下走。所以在這個章節介紹如何制定指令,並控制蛇的移動方向。

定義移動指令

首先定義蛇的移動指令有:上、下、左、右,還有一個無動作(就是甚麼鍵都沒有按),分別代表的字串是 "UP""DOWN""LEFT""RIGHT""NONE",在遊戲中都會以這些字串代表指令。

回到 gameobject.py。一開始,預設蛇的移動方向是向下的。在 Snake 類別中的 __init__() 最後面加入 _action 屬性:

class Snake:
    def __init__(self):
        ...
        self._action = "DOWN"

讓指令移動蛇

在前面蛇的設計中,蛇身就是依照蛇頭上一動的位置在移動,所以控制蛇的移動,就是決定蛇頭要往哪裡動。流程如下:

  1. 檢查指令是否有效,如果無效,則使用上一影格的指令;
  2. 蛇身移動到蛇頭上一動的位置;
  3. 蛇頭依指令移動到正確的位置。

輔助函式

先在遊戲物件檔中的 Snake 新增一個函式 _get_possible_head_pos() 來計算蛇頭可能的位置:

    def _get_possible_head_pos(self, action):
        """
        Get the possible head position accroding to the given action
        """
        if action == "UP":
            move_delta = (0, -10)
        elif action == "DOWN":
            move_delta = (0, 10)
        elif action == "LEFT":
            move_delta = (-10, 0)
        elif action == "RIGHT":
            move_delta = (10, 0)

        return self.head.rect.move(move_delta).topleft

_get_possible_head_pos() 會回傳蛇頭可能的位置。使用 pygame.Rect.move() 來計算移動後的位置,並不會影響原本 rect 的位置,而是另外回傳移動後的 rect,如果是使用 move_ip() 才會直接更改原本 rect 的位置。

這裡的回傳值也可以使用 SnakeBody.pos 來計算,但是要注意的是 topleft 屬性回傳的是 tuple,把兩個 tuple 相加是「相接」,(1, 2) + (3, 4) 會得到 (1, 2, 3, 4)。所以必需要把 tuple 裡的元素個別相加後再回傳:

        return (self.head.pos[0] + move_delta[0], self.head.pos[1] + move_delta[1])

套用流程

接著讓 Snake.move() 套用前面提到的流程:

  1. 首先讓 move() 多一個引數 action,可以傳入移動指令:
    def move(self, action):
  1. 檢查指令是否有效,下面兩種情況都是無效的指令:
  • 如果玩家沒有指定指令;
  • 如果蛇會直接走回自己的身體(例如:蛇本來往下走,就不能直接往上走)。
        # If there is no action, take the same action as the last frame.
        if action == "NONE":
            action = self._action

        # If the head will go back to the body,
        # take the same action as the last frame.
        possible_head_pos = self._get_possible_head_pos(action)
        if action == "NONE" or possible_head_pos = self.body[0].pos:
            action = self._action

要注意的是,如果玩家沒有指定指令,就必須先給予上一次有效的指令,才去做蛇頭位置判定。因為在貪食蛇遊戲中,蛇會一直移動。而遊戲一開始就指定往下的指令,所以蛇一開始就會一直往下走。

  1. 蛇尾移動到蛇頭的位置,藉此讓蛇移動:
        # Move the body 1 step ahead
        tail = self.body.pop()
        tail.pos = self.head.pos
        self.body.appendleft(tail)
  1. 以有效的移動指令去更新蛇頭的位置,並更新 _action 供下次更新使用。
        # Get the next head position according to the valid action
        next_head_pos = self._get_possible_head_pos(action)
        self.head.pos = next_head_pos

        # Store the action
        self._action = action

到這邊就是完整的 Snake.move() 了。

最後,在 gamecore.py 中的 Scene.update() 中也多一個引數 action,藉此接收玩家的指令:

class Scene:
    ...

    def update(self, action):
        self._snake.move(action)

設置鍵盤指令

將鍵盤指令配對到控制蛇的指令上。在 snake.py 中,在場景更新之前,要先偵測玩家按下的按鍵,轉成對應的指令,並傳給 Scene 去更新蛇的移動方向:

if __name__ == "__main__":
    ...
    while running:
        # Get key command
        key_pressed_list = pygame.key.get_pressed()

        if   key_pressed_list[pygame.K_UP]:    action = "UP"
        elif key_pressed_list[pygame.K_DOWN]:  action = "DOWN"
        elif key_pressed_list[pygame.K_LEFT]:  action = "LEFT"
        elif key_pressed_list[pygame.K_RIGHT]: action = "RIGHT"
        else: action = "NONE"

        # Update the scene
        scene.update(action)
        ...

使用 pygame.key.get_pressed() 取得所有按鍵的狀態,再使用 pygame 中定義的按鍵值 來檢查對應的按鍵是否被按下,如果為 True,就代表被按下。這邊就將方向鍵分別對應四個移動指令,另外如果沒有指定的按鍵被按下時,就指定為 NONE

執行遊戲

執行遊戲看看成果。

python snake.py

Imgur