讀古今文學網 > 父與子的編程之旅:與小卡特一起學Python > 17.1 動畫精靈 >

17.1 動畫精靈

從上一章我們已經瞭解到,看似簡單的動畫實際上並不簡單。如果有大量圖像在四處移動,要想跟蹤每個圖像「底下」有些什麼,以便在移動圖像時能夠重繪,這可能要費很大的功夫。在我們的第一個沙灘球例子中,由於背景是白色的,所以更容易一些。不過你也可以想像,倘若背景上有一些圖形,這肯定會複雜得多。

幸運的是,Pygame 可以為我們提供額外的幫助。四處移動的單個圖像或圖像部分稱為動畫精靈(sprite),Pygame 有一個特殊的模塊來處理動畫精靈。利用這個模塊,我們可以更容易地移動圖形對象。

在上一章中,我們讓一個沙灘球在屏幕上反彈。如果希望一堆沙灘球都反彈呢?當然可以編寫代碼來單獨地管理各個球,不過我們不會去這樣做,而是使用 Pygame 的 sprite 模塊,這樣更簡單一些。

術語箱

動畫精靈表示作為一個單位來移動和顯示的一組像素,這是一種圖形對象。

「『動畫精靈』(sprite)這個詞是從老式的計算機和遊戲機流傳下來的。這些老式的遊戲機不能很快地繪製和擦除圖形來保證遊戲正常工作。這些遊戲機有一些特殊的硬件,專門用來處理需要快速移動的遊戲類對象。這些對象就稱為『動畫精靈』。它們有一些特殊的限制,不過可以非常快地繪製和更新……如今,一般來講,計算機的速度已經足夠快了,不需要專門的硬件也可以很好地處理類似動畫精靈的對象。不過『動畫精靈』這個詞仍用來表示二維(2D)遊戲中的所有動畫對象。」

( 摘自 Pete Shinners 的「Pygame 教程——Sprite 模塊介紹」,http://www.pygame.org/docs/tut/SpriteIntro.html。)

什麼是動畫精靈

可以把動畫精靈想成一個小圖片——一種可以在屏幕上移動的圖形對象,並且可以與其他圖形對像交互。

大多數動畫精靈都有以下兩個基本屬性。

  • 圖像(image):為動畫精靈顯示的圖片。

  • 矩形區(rect):包含動畫精靈的矩形區域。

動畫精靈的圖像可以是使用 Pygame 繪製函數繪製的圖像(如上一章看到的圖像),也可以是原來就有的圖像文件。

Sprite

Pygame 的 sprite 模塊提供了一個動畫精靈基類,名為 Sprite。(還記得幾章前討論過的對象和類嗎?)正常情況下,我們不會直接使用基類,而是基於 pygame.sprite.Sprite 來創建自己的子類。下面將完成這樣一個例子,並把我們的類命名為 MyBallClass。創建這個類的代碼如下:

要仔細分析這些代碼的最後一行。location 是一個 [x, y] 位置,這是個包含兩個元素的列表。因為 = 號的一邊是包含兩個元素的列表(xy),所以可以在另一邊賦兩個值。這裡我們為動畫精靈矩形的 lefttop 屬性賦值。

既然已經定義了 MyBallClass,接下來必須創建它的一些實例。(要記住,類定義只是一個藍圖;現在必須動手蓋房子。)我們仍然需要和上一章同樣的代碼來創建 Pygame 窗口,另外還要在屏幕上創建一些球,按行列擺放。這要利用一個嵌套循環來完成:

我們還需要把球塊移到顯示表面。(還記得那個好玩的詞「塊移」嗎?我們在上一章討論過的。)

for ball in balls:screen.blit(ball.image, ball.rect)pygame.display.flip  

把所有這些代碼放在一起,就構成了我們的程序,見代碼清單 17-1。

代碼清單 17-1 使用動畫精靈在屏幕上放多個沙灘球圖像

如果運行這個程序,會看到 9 個沙灘球出現在 Pygame 窗口中,就像右圖顯示的這樣:

稍後,我們就會讓它們動起來。

有沒有注意到第 10 行和第 11 行有一個小小的變化?(就是設置 Pygame 窗口大小的那兩行代碼。)我們將代碼

screen = pygame.display.set_mode([640,480])  

替換為

size = width, height = 640, 480screen = pygame.display.set_mode(size)  

這個代碼不僅設置了窗口的大小(像前面一樣),還定義了兩個變量,widthheight,後面將會用到這兩個變量。這裡有一點很棒,我們不僅定義了一個列表 size(其中包含兩個元素),還定義了兩個整型變量 widthheight,而且所有這些都在一個語句中完成。另外,我們的列表兩邊沒有加中括號,在 Python 中這是允許的。

我這樣做只是想告訴你:在 Python 中有時做同樣的事情可以有多種不同的方法。這些方法不存在絕對的優劣(只要它們都能工作),不能說哪種方法一定比另一種方法強。你得遵循 Python 的語法(語言規則),這是必須保證的,儘管如此,自由表述的空間還是有的。如果你讓 10 個程序員編寫同樣的程序,可能得不到兩個完全相同的代碼。

move 方法

因為我們把球創建為 MyBallClass 的實例,應該可以使用一個類方法來移動這些球。下面就來創建一個新的類方法,名為 move

動畫精靈(實際上是其中的 rect)有一個內置方法 move。這個方法需要一個 speed 參數來告訴它對像要移動多遠(也就是移動多快)。因為我們處理的是二維(2D)圖形,而 speed 是一個包含兩個數的列表,一個對應 x-speed,另一個對應 y-speed。我們還要檢查球是否碰到窗口的邊界,使球能夠在屏幕上「反彈」。

下面修改 MyBallClass 定義,增加 speed 屬性和 move 方法:

注意第 2 行(def__init__(self,image_file,location,speed):)中的修改,這裡增加了第 7 行(self.speed=speed),另外第 9 行到第 15 行增加了新的 move 方法。

現在創建球的各個實例時,需要告訴它速度,還要指出圖像文件以及位置:

speed = [2, 2]ball = MyBallClass(img_file, location, speed)  

前面的代碼把所有球都創建為相同的速度(相同方向),不過如果球的移動有些隨機性可能更有意思。下面使用 random.choice 函數來設置速度,如下:

from random import *speed = [choice([-2, 2]), choice([-2, 2])]  

這會為 x 和 y 速度選擇 -2 或 2。

代碼清單 17-2 給出了完整的程序。

代碼清單 17-2 使用動畫精靈移動球的程序

這個程序使用一個列表來跟蹤所有球。第 32 行(balls.append(ball))上,創建每個球時會把球增加到這個列表。

最後 5 行代碼會重繪屏幕。這裡我們走了一條捷徑,並不是單獨地「擦除」(覆蓋)各個球,我們直接用白色填充窗口,然後重繪所有球。

可以試驗一下這些代碼,看看有更多(或更少)球時會怎麼樣,可以改變它們的速度,還可以改變它們移動和「反彈」的方式,等等。你會注意到,球會四處移動,而且在窗口四周會反彈,不過它們相互之間還不能反彈!