-
Notifications
You must be signed in to change notification settings - Fork 49
控制蛇的行走
上一個章節初次執行遊戲,可以發現蛇不受控制地往下走。所以在這個章節介紹如何制定指令,並控制蛇的移動方向。
首先定義蛇的移動指令有:上、下、左、右,還有一個無動作(就是甚麼鍵都沒有按),分別代表的字串是 "UP"
、"DOWN"
、"LEFT"
、"RIGHT"
、"NONE"
,在遊戲中都會以這些字串代表指令。
回到 gameobject.py
。一開始,預設蛇的移動方向是向下的。在 Snake
類別中的 __init__()
最後面加入 _action
屬性:
class Snake:
def __init__(self):
...
self._action = "DOWN"
在前面蛇的設計中,蛇身就是依照蛇頭上一動的位置在移動,所以控制蛇的移動,就是決定蛇頭要往哪裡動。流程如下:
- 檢查指令是否有效,如果無效,則使用上一影格的指令;
- 蛇身移動到蛇頭上一動的位置;
- 蛇頭依指令移動到正確的位置。
先在遊戲物件檔中的 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()
套用前面提到的流程:
- 首先讓
move()
多一個引數action
,可以傳入移動指令:
def move(self, action):
- 檢查指令是否有效,下面兩種情況都是無效的指令:
- 如果玩家沒有指定指令;
- 如果蛇會直接走回自己的身體(例如:蛇本來往下走,就不能直接往上走)。
# 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
要注意的是,如果玩家沒有指定指令,就必須先給予上一次有效的指令,才去做蛇頭位置判定。因為在貪食蛇遊戲中,蛇會一直移動。而遊戲一開始就指定往下的指令,所以蛇一開始就會一直往下走。
- 蛇尾移動到蛇頭的位置,藉此讓蛇移動:
# Move the body 1 step ahead
tail = self.body.pop()
tail.pos = self.head.pos
self.body.appendleft(tail)
- 以有效的移動指令去更新蛇頭的位置,並更新
_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