讀古今文學網 > 父與子的編程之旅:與小卡特一起學Python > 25.2 障礙物 >

25.2 障礙物

接下來學習怎麼製作障礙物,也就是樹和小旗。在這一部分,為了簡化起見,我們再次從零開始——沒有滑雪者,只有障礙物。我們會在最後將滑雪者的代碼和障礙物的代碼放到一起。

Skier 遊戲的窗口大小是 640×640 像素。為了簡化,也為了防止障礙物靠得太近,我們將窗口分割為一個 10×10 的網格。這樣,一共有 100 個格子,每個格子的大小是 64×64 像素。因為我們的障礙物尺寸並不是太大,所以即使兩個障礙物位於相鄰的格子中,它們之間也有一些空隙。

創建單個障礙物

首先我們需要創建單個障礙物。為此,我們創建了一個名為 ObstacleClass 的類。和滑雪者一樣,這也是一個 Pygame Sprite 類。

class ObstacleClass(pygame.sprite.Sprite):    def __init__(self, image_file, location, type):pygame.sprite.Sprite.__init__(self)self.image_file = image_fileself.image = pygame.image.load(image_file)self.rect = self.image.get_rectself.rect.center = locationself.type = typeself.passed = False  

創建障礙物地圖

現在我們來創建多個障礙物,以便填充在 640×640 像素的窗口中。我們將 10 個障礙物(小旗和樹)隨機地分佈在 100 個格子中,每個障礙物既可以是小旗也可以是樹。最終結果可能是 2 面小旗 8 棵樹,7 面小旗 3 棵樹,或者是總和為 10 的任意組合。總之,小旗和樹是隨機選擇的,它們的位置也是隨機的。

我們唯一需要當心的是,不要嘗試將兩個障礙物放在同一位置,所以我們需要知道哪些位置是已經使用過的。變量 locations 是一個用於記錄使用過的位置的列表。當想要在某個位置放置一個新的障礙物時,我們首先要查看這個位置是否已有一個障礙物。

 

好眼力!我們不希望遊戲開始時滿屏都是障礙物,而是希望它是空白的,然後障礙物從底部出現。所以,我們將障礙物的場景創建在窗口底部的「下方」。為此,我們需要為每個障礙物位置的 y 值增加 640 像素(窗口的高度)。

遊戲開始時,我們希望障礙物從底部滾動上來。為此,我們修改每個障礙物位置的 y 值。改動的大小取決於滑雪者滑下小山的速度。我們將它放入一個名為 update 的方法中,它是 ObstacleClass 的一部分。

def update(self):     global speed     self.rect.centery -= speed[1]  

變量 speed 是滑雪者的速度,它是一個全局變量,包含了 x 和 y 方向的速度,所以我們使用索引 [1] 來獲取 y(垂直)方向的速度。

和創建的第一屏障礙物一樣,我們還需要在窗口下方創建另外一屏的障礙物。那怎麼知道應該在什麼時候創建呢?我們可以創建一個名為 map_position 的變量,由它來告訴我們場景已經向上滾動了多少。我們在主循環中像下面這樣處理。

我們用 animate 函數來重繪屏幕,就像在只有滑雪者的代碼中那樣。合在一起,只有障礙物的代碼看起來像下面這樣。

代碼清單 25-2 創建 Skier 遊戲——只有障礙物

如果運行以上代碼,你應該可以看到樹和小旗在屏幕上往上滾。

 

問得好。在當前的代碼中,我們讓它們繼續在窗口上邊界外向上滾動,這樣其位置的 y 值(負值)絕對值會越來越大。如果遊戲運行了比較長的時間,則會創建並積累大量的障礙物場景。這有可能會導致程序變慢或者在某個時間點發生內存不足的情況。所以我們需要做一點清理工作。

在障礙物類的 update 方法中,我們添加一個判斷邏輯,看看障礙物是否已經移出屏幕。如果是的話,就移除它。Pygame 有一個名為 kill 的原生方法可用來做這件事。新的 update 方法看起來像下面這樣。

現在我們可以將滑雪者和障礙物的代碼放到一起了。

  • 我們需要 SkierClassObstacleClass

  • 我們的 animate 函數需要同時繪製滑雪者和障礙物。

  • 我們的初始化代碼需要創建滑雪者和初始地圖。

  • 主循環需要同時包括滑雪者的鍵盤事件綁定和障礙物場景的創建。

基本上,這組合了代碼清單 25-1 和代碼清單 25-2,結果如下所示。

代碼清單 25-3 滑雪者代碼與障礙物代碼相結合

如果運行以上代碼,你就能操作滑雪者滑下小山,並且會看到障礙物向上滾過。你也會注意到滑雪者向左右滑和向下滑的速度取決於他的轉向。至此,這個遊戲已經快要完成了。

最後我們需要處理的兩項是:

  • 檢測滑雪者是否碰到了樹或者是否撿到了小旗

  • 記錄並顯示分數

你已經在第 17 章中學到如何進行碰撞檢測了。代碼清單 25-3 已經將障礙物精靈放到了一個 sprite 組中,所以我們可以直接用 spritecollide 函數來檢測滑雪者是否碰到樹或者小旗。接下來我們需要知道這個障礙物是什麼(樹還是小旗),然後:

  • 如果是樹,則將滑雪者的圖像切換為「碰撞」的圖像,並將分數減去 100。

  • 如果是小旗,則將分數加 10,然後將小旗從屏幕中移除。

為此所需的代碼位於主循環中,看起來像下面這樣:

變量 hit 告訴我們滑雪者撞到了哪個障礙物。它是一個列表,但在這裡列表中只有一個條目,因為滑雪者一次只可能碰到一個障礙物,所以他碰到的障礙物是 hit[0]

passed 變量用於標記是否碰撞過樹。這能保證當滑雪者在撞樹之後繼續往下滑時,不會立刻運行撞到同一棵樹的邏輯。

現在我們需要顯示分數。這只需要再寫 3 行代碼。在初始化代碼中,我們創建一個 font 對象,它是 Pygame 的 Font 類的實例:

font = pygame.font.Font(None, 50)  

在主循環中,我們使用一個新的分數文本來渲染 font 對像:

score_text = font.render(\"Score: \" +str(points), 1, (0, 0, 0))  

animate 函數中,我們在左上角顯示分數:

screen.blit(score_text, [10, 10])  

這就是這個遊戲的全部了。如果你把這些組合在一起,會發現這就是第 10 章的代碼,只是現在你理解得更加深入了。理解 Skier 遊戲的工作原理可以幫助你更好地策劃和開發自己的遊戲。

你學到了什麼

在這一章,你學到了以下內容。

  • Skier 遊戲的各個部分是如何工作的

  • 怎樣創建一個滾動的背景

動手試一試

1. 嘗試修改 Skier 遊戲,使遊戲的難度隨著遊戲的進行逐漸增大。可以嘗試以下建議:

  • 使遊戲的速度隨著遊戲的進行逐漸加快。

  • 滑雪者滑下小山時樹越來越多。

  • 添加障礙物「冰」,使得滑雪者轉向時更加困難。

2. Skier 遊戲的靈感來自一個名為 SkiFree 的遊戲,這個遊戲中有一個討厭的雪人會隨機出現並追趕滑雪者。如果你想挑戰一下,可以嘗試在 Skier 遊戲中添加一些類似的物體。你需要找到或者創建一個新的圖片,並且修改代碼以獲得你想要的行為。