游戲中需要用到很多對象,比如人物、野怪、建筑物、UI控件等等,他們的行為不盡相同,但是總有那么些共同點,如果將所有的東西都封裝成各自的類難免會寫上很多重復的代碼,因為兩個對象如果具有相同的行為(比如人物和野怪都可以行走)如果寫兩個move()函數,以后改起來就要修改兩個地方,不容易維護而且擴展很麻煩,但是人物和野怪又有其它不相同的行為,比如技能之類的不同。所以比較好的處理方法是抽象出一個生物類,然后讓人物和野怪去繼承它,舉個例子:
class RenderObject(pygame.sprite.Sprite) :
# 靜態變量
images = []
def __init__(self, selfdata) :
self.image = self.images[ int(selfdata[0]) ]
self.size = [self.image.get_width(), self.image.get_height()]
self.offset = [0,0]
self.position = [0,0]
self.speed = [0.0,0.0]
def move(self, xgo, ygo) :
do_move()
def draw(self, screen) :
do_draw()
以上這個類RenderObject是自己定義的用于渲染的類的基類,它繼承自pygame.sprite.Sprite,這里有必要先介紹一下pygame.sprite,這個模塊在pygame中是選擇性使用的,他可以作為游戲中很多對象的基類,并且將它擁有的東西繪制到Surface上,還有一個Group類是Sprite類的容器類,可以包含很多Sprite。
RenderObject有三個類函數,__init__是Python中的特殊函數,類似C語言中的構造函數,當類實例被創建的時候,這個函數會被自動執行,它的目的是執行一些該對象的必要的初始化工作(我在這個函數中加入了一個selfdata的列表,以便于不同類型的控件添加初始化信息)。move函數是執行移動操作,而draw函數則負責渲染。
首先來看下__init__函數,在Python中使用變量不需要提前聲明,所以要用到的變量我把它放在了__init__函數中進行初始化,注意到每個函數都有一個self參數,這個就好比C語言中的this指針,但是它是顯式聲明,隱式調用的。在使用類內部變量的時候需要加self做修飾,我們可以看到該類的一些成員變量:
self.image # 該渲染對象對應的Surface
self.size # 該渲染對象在Surface的矩形區域的寬高
self.offset # 該渲染對象在Surface的左上角坐標
self.position # 該渲染對象將被繪制到屏幕的位置
self.speed # 該渲染對象的速度
之所以要用到offset和size,是因為該Surface不是完全被用來顯示的,我們只取某一張Surface的某一部分用于顯示,如果不是很明白,那么參看Player類的相關描述就知道為什么了。
對于move(self, xgo, ygo)函數,我們暫時可以這樣實現:
if xgo :
self.position[0] += self.speed[0] * xgo
if ygo :
self.position[1] += self.speed[1] * ygo
xgo和ygo都是三值變量(1,0,-1),分別表示在兩個坐標軸方向是否發生了位移,并且這個位移是正向的還是逆向的。于是我們可以通過鍵盤輸入來傳遞對應的參數,position的值就會隨之變化了。注意如果這里速度為0,則表示靜態物體,position不會做任何變化。
對于draw(self, screen)函數,用于將當前對象繪制到屏幕上,screen參數是上一節中提到的屏幕Surface,我們調用Surface的blit函數就可以在屏幕上繪制當前對象了,像這樣:
screen.blit( self.image, self.position, (self.offset, self.size))
(self.offset, self.size)表示self.image這個Surface上對應的矩形區域。
基類完成后,我們需要派生出一些子類,例如:
class Player(RenderObject) :
actionwait = 50
def __init__(self, selfdata) :
RenderObject.__init__(self, selfdata)
self.size[0] /= 4
self.size[1] /= 4
self.speed = [0.1,0.1]
self.direction=3
self.action=0
def move(self, xgo, ygo) :
RenderObject.move(self, xgo, ygo)
if xgo or ygo :
self.action = (self.action + 1) % (self.actionwait * 4)
self.offset[0] = (self.action / self.actionwait) * self.size[0]
self.offset[1] = self.direction * self.size[1]