在這個系列的第一篇文章中,我解釋瞭如何使用 Python 建立一個簡單的基於文字的骰子游戲。在第二部分中,我向你們展示瞭如何從頭開始構建遊戲,即從 建立遊戲的環境 開始。然後在第三部分,我們建立了一個玩家妖精,並且使它在你的(而不是空的)遊戲世界內生成。你可能已經註意到,如果你不能移動你的角色,那麼遊戲不是那麼有趣。在本篇文章中,我們將使用 Pygame 來新增鍵盤控制,如此一來你就可以控制你的角色的移動。
在 Pygame 中有許多函式可以用來新增(除鍵盤外的)其他控制,但如果你正在敲擊 Python 程式碼,那麼你一定是有一個鍵盤的,這將成為我們接下來會使用的控制方式。一旦你理解了鍵盤控制,你可以自己去探索其他選項。
在本系列的第二篇文章中,你已經為退出遊戲建立了一個按鍵,移動角色的(按鍵)原則也是相同的。但是,使你的角色移動起來要稍微複雜一點。
讓我們從簡單的部分入手:設定控制器按鍵。
為控制你的玩家妖精設定按鍵
在 IDLE、Ninja-IDE 或文字編輯器中開啟你的 Python 遊戲指令碼。
因為遊戲需要時刻“監聽”鍵盤事件,所以你寫的程式碼需要連續執行。你知道應該把需要在遊戲週期中持續執行的程式碼放在哪裡嗎?
如果你回答“放在主迴圈中”,那麼你是正確的!記住除非程式碼在迴圈中,否則(大多數情況下)它只會執行僅一次。如果它被寫在一個從未被使用的類或函式中,它可能根本不會執行。
要使 Python 監聽傳入的按鍵,將如下程式碼新增到主迴圈。目前的程式碼還不能產生任何的效果,所以使用 print 陳述句來表示成功的訊號。這是一種常見的除錯技術。
-
while main == True: -
for event in pygame.event.get(): -
if event.type == pygame.QUIT: -
pygame.quit(); sys.exit() -
main = False -
-
if event.type == pygame.KEYDOWN: -
if event.key == pygame.K_LEFT or event.key == ord('a'): -
print('left') -
if event.key == pygame.K_RIGHT or event.key == ord('d'): -
print('right') -
if event.key == pygame.K_UP or event.key == ord('w'): -
print('jump') -
-
if event.type == pygame.KEYUP: -
if event.key == pygame.K_LEFT or event.key == ord('a'): -
print('left stop') -
if event.key == pygame.K_RIGHT or event.key == ord('d'): -
print('right stop') -
if event.key == ord('q'): -
pygame.quit() -
sys.exit() -
main = False
一些人偏好使用鍵盤字母 W、A、S 和 D 來控制玩家角色,而另一些偏好使用方向鍵。因此確保你包含了兩種選項。
註意:當你在程式設計時,同時考慮所有使用者是非常重要的。如果你寫程式碼只是為了自己執行,那麼很可能你會成為你寫的程式的唯一使用者。更重要的是,如果你想找一個透過寫程式碼賺錢的工作,你寫的程式碼就應該讓所有人都能執行。給你的使用者選擇權,比如提供使用方向鍵或 WASD 的選項,是一個優秀程式員的標誌。
使用 Python 啟動你的遊戲,併在你按下“上下左右”方向鍵或 A、D 和 W 鍵的時候檢視控制檯視窗的輸出。
-
$ python ./your-name_game.py -
left -
left stop -
right -
right stop -
jump
這驗證了 Pygame 可以正確地檢測按鍵。現在是時候來完成使妖精移動的艱巨任務了。
編寫玩家移動函式
為了使你的妖精移動起來,你必須為你的妖精建立一個屬性代表移動。當你的妖精沒有在移動時,這個變數被設為 0。
如果你正在為你的妖精設定動畫,或者你決定在將來為它設定動畫,你還必須跟蹤幀來使走路迴圈保持在軌跡上。
在 Player 類中建立如下變數。開頭兩行作為背景關係對照(如果你一直跟著做,你的程式碼中就已經有這兩行),因此只需要新增最後三行:
-
def __init__(self): -
pygame.sprite.Sprite.__init__(self) -
self.movex = 0 # 沿 X 方向移動 -
self.movey = 0 # 沿 Y 方向移動 -
self.frame = 0 # 幀計數
設定好了這些變數,是時候去為妖精移動編寫程式碼了。
玩家妖精不需要時刻響應控制,有時它並沒有在移動。控制妖精的程式碼,僅僅只是玩家妖精所有能做的事情中的一小部分。在 Python 中,當你想要使一個物件做某件事並獨立於剩餘其他程式碼時,你可以將你的新程式碼放入一個函式。Python 的函式以關鍵詞 def 開頭,(該關鍵詞)代表了定義函式。
在你的 Player 類中建立如下函式,來為你的妖精在螢幕上的位置增加幾個畫素。現在先不要擔心你增加幾個畫素,這將在後續的程式碼中確定。
-
def control(self,x,y): -
''' -
控制玩家移動 -
''' -
self.movex += x -
self.movey += y
為了在 Pygame 中移動妖精,你需要告訴 Python 在新的位置重繪妖精,以及這個新位置在哪裡。
因為玩家妖精並不總是在移動,所以更新只需要是 Player 類中的一個函式。將此函式新增前面建立的 control 函式之後。
要使妖精看起來像是在行走(或者飛行,或是你的妖精應該做的任何事),你需要在按下適當的鍵時改變它在螢幕上的位置。要讓它在螢幕上移動,你需要將它的位置(由 self.rect.x 和 self.rect.y 屬性指定)重新定義為當前位置加上已應用的任意 movex 或 movey。(移動的畫素數量將在後續進行設定。)
-
def update(self): -
''' -
更新妖精位置 -
''' -
self.rect.x = self.rect.x + self.movex
對 Y 方向做同樣的處理:
-
self.rect.y = self.rect.y + self.movey
對於動畫,在妖精移動時推進動畫幀,並使用相應的動畫幀作為玩家的影象:
-
# 向左移動 -
if self.movex < 0: -
self.frame += 1 -
if self.frame > 3*ani: -
self.frame = 0 -
self.image = self.images[self.frame//ani] -
-
# 向右移動 -
if self.movex > 0: -
self.frame += 1 -
if self.frame > 3*ani: -
self.frame = 0 -
self.image = self.images[(self.frame//ani)+4]
透過設定一個變數來告訴程式碼為你的妖精位置增加多少畫素,然後在觸發你的玩家妖精的函式時使用這個變數。
首先,在你的設定部分建立這個變數。在如下程式碼中,開頭兩行是背景關係對照,因此只需要在你的指令碼中增加第三行程式碼:
-
player_list = pygame.sprite.Group() -
player_list.add(player) -
steps = 10 # 移動多少個畫素
現在你已經有了適當的函式和變數,使用你的按鍵來觸發函式並將變數傳遞給你的妖精。
為此,將主迴圈中的 print 陳述句替換為玩家妖精的名字(player)、函式(.control)以及你希望玩家妖精在每個迴圈中沿 X 軸和 Y 軸移動的步數。
-
if event.type == pygame.KEYDOWN: -
if event.key == pygame.K_LEFT or event.key == ord('a'): -
player.control(-steps,0) -
if event.key == pygame.K_RIGHT or event.key == ord('d'): -
player.control(steps,0) -
if event.key == pygame.K_UP or event.key == ord('w'): -
print('jump') -
-
if event.type == pygame.KEYUP: -
if event.key == pygame.K_LEFT or event.key == ord('a'): -
player.control(steps,0) -
if event.key == pygame.K_RIGHT or event.key == ord('d'): -
player.control(-steps,0) -
if event.key == ord('q'): -
pygame.quit() -
sys.exit() -
main = False
記住,steps 變數代表了當一個按鍵被按下時,你的妖精會移動多少個畫素。如果當你按下 D 或右方向鍵時,你的妖精的位置增加了 10 個畫素。那麼當你停止按下這個鍵時,你必須(將 step)減 10(-steps)來使你的妖精的動量回到 0。
現在嘗試你的遊戲。註意:它不會像你預想的那樣執行。
為什麼你的妖精仍無法移動?因為主迴圈還沒有呼叫 update 函式。
將如下程式碼加入到你的主迴圈中來告訴 Python 更新你的玩家妖精的位置。增加帶註釋的那行:
-
player.update() # 更新玩家位置 -
player_list.draw(world) -
pygame.display.flip() -
clock.tick(fps)
再次啟動你的遊戲來見證你的玩家妖精在你的命令下在螢幕上來回移動。現在還沒有垂直方向的移動,因為這部分函式會被重力控制,不過這是另一篇文章中的課程了。
與此同時,如果你擁有一個搖桿,你可以嘗試閱讀 Pygame 中 joystick 模組相關的檔案,看看你是否能透過這種方式讓你的妖精移動起來。或者,看看你是否能透過滑鼠與你的妖精互動。
最重要的是,玩的開心!
本教程中用到的所有程式碼
為了方便查閱,以下是目前本系列文章用到的所有程式碼。
-
#!/usr/bin/env python3 -
# 繪製世界 -
# 新增玩家和玩家控制 -
# 新增玩家移動控制 -
-
# GNU All-Permissive License -
# Copying and distribution of this file, with or without modification, -
# are permitted in any medium without royalty provided the copyright -
# notice and this notice are preserved. This file is offered as-is, -
# without any warranty. -
-
import pygame -
import sys -
import os -
-
''' -
Objects -
''' -
-
class Player(pygame.sprite.Sprite): -
''' -
生成玩家 -
''' -
def __init__(self): -
pygame.sprite.Sprite.__init__(self) -
self.movex = 0 -
self.movey = 0 -
self.frame = 0 -
self.images = [] -
for i in range(1,5): -
img = pygame.image.load(os.path.join('images','hero' + str(i) + '.png')).convert() -
img.convert_alpha() -
img.set_colorkey(ALPHA) -
self.images.append(img) -
self.image = self.images[0] -
self.rect = self.image.get_rect() -
-
def control(self,x,y): -
''' -
控制玩家移動 -
''' -
self.movex += x -
self.movey += y -
-
def update(self): -
''' -
更新妖精位置 -
''' -
-
self.rect.x = self.rect.x + self.movex -
self.rect.y = self.rect.y + self.movey -
-
# 向左移動 -
if self.movex < 0: -
self.frame += 1 -
if self.frame > 3*ani: -
self.frame = 0 -
self.image = self.images[self.frame//ani] -
-
# 向右移動 -
if self.movex > 0: -
self.frame += 1 -
if self.frame > 3*ani: -
self.frame = 0 -
self.image = self.images[(self.frame//ani)+4] -
-
-
''' -
設定 -
''' -
worldx = 960 -
worldy = 720 -
-
fps = 40 # 幀掃清率 -
ani = 4 # 動畫迴圈 -
clock = pygame.time.Clock() -
pygame.init() -
main = True -
-
BLUE = (25,25,200) -
BLACK = (23,23,23 ) -
WHITE = (254,254,254) -
ALPHA = (0,255,0) -
-
world = pygame.display.set_mode([worldx,worldy]) -
backdrop = pygame.image.load(os.path.join('images','stage.png')).convert() -
backdropbox = world.get_rect() -
player = Player() # 生成玩家 -
player.rect.x = 0 -
player.rect.y = 0 -
player_list = pygame.sprite.Group() -
player_list.add(player) -
steps = 10 # 移動速度 -
-
''' -
主迴圈 -
''' -
while main == True: -
for event in pygame.event.get(): -
if event.type == pygame.QUIT: -
pygame.quit(); sys.exit() -
main = False -
-
if event.type == pygame.KEYDOWN: -
if event.key == pygame.K_LEFT or event.key == ord('a'): -
player.control(-steps,0) -
if event.key == pygame.K_RIGHT or event.key == ord('d'): -
player.control(steps,0) -
if event.key == pygame.K_UP or event.key == ord('w'): -
print('jump') -
-
if event.type == pygame.KEYUP: -
if event.key == pygame.K_LEFT or event.key == ord('a'): -
player.control(steps,0) -
if event.key == pygame.K_RIGHT or event.key == ord('d'): -
player.control(-steps,0) -
if event.key == ord('q'): -
pygame.quit() -
sys.exit() -
main = False -
-
# world.fill(BLACK) -
world.blit(backdrop, backdropbox) -
player.update() -
player_list.draw(world) # 更新玩家位置 -
pygame.display.flip() -
clock.tick(fps)
你已經學了很多,但還仍有許多可以做。在接下來的幾篇文章中,你將實現新增敵方妖精、模擬重力等等。與此同時,練習 Python 吧!
知識星球
朋友會在“發現-看一看”看到你“在看”的內容