本系列部落格介紹以python+pygame庫進行小遊戲的開發。有寫的不對之處還望各位海涵。
直到現在我們已經學了很多pygame基礎知識了,從這篇部落格開始我們将會學習pygame中進階部分,更多和精靈子產品,沖突檢測相關的知識。
一、Sprite子產品、sprite對象
在pygame.sprite子產品裡面包含了一個名為Sprite類,他是pygame本身自帶的一個精靈。但是這個類的功能比較少,是以我們建立一個類對其繼承,在sprite類的基礎上豐富,以友善我們的使用。
首先來了解一下如何使用sprite類來加載動畫吧。
1、精靈序列圖
将要加載的動畫幀放在一個精靈序列圖裡面,然後在程式裡面調用它。pygame會自動更新動畫幀,這樣一個動态的圖像就會展現在我們面前了。
下面是一個典型的精靈序列圖:行和列的索引都是從0開始的。
2、加載精靈圖序列:
在加載一個精靈圖序列的時候,我們需要告知程式一幀的大小,(傳入幀的寬度和高度,檔案名)。
除此之外,還需要告訴精靈類,精靈序列圖裡面有多少列。load函數可以加載一個精靈序列圖。
def load(self, filename, width, height, columns):
self.master_image = pygame.image.load(filename).convert_alpha()
self.frame_width = width
self.frame_height = height
self.rect = 0,0,width,height
self.columns = columns
3.更新幀
一個循環動畫通常是這樣工作的:從第一幀不斷的加載直到最後一幀,然後在折傳回第一幀,并不斷重複這個操作。
self.frame += 1
if self.frame > self.last_frame:
self.frame = self.first_frame
self.last_time = current_time
但是如果隻是這樣做的話,程式會一股腦地将動畫播放完了,我們想讓它根據時間間隔一張一張的播放,是以加入定時的代碼。
pygame中的time子產品有一個get_ticks()方法可以滿足定時的需要。
ticks = pygame.time.get_ticks()
然後将ticks變量傳遞給sprite的update函數,這樣就可以輕松讓動畫按照幀速率來播放了。哦,幀速率還沒有設定,咱們現在設定一下幀速率。
啟動一個定時器,然後調用tick(num)函數就可以讓遊戲以num幀來運作了。
framerate = pygame.time.Clock()
framerate.tick(60)
4、繪制幀
sprite.draw()方法是用來繪制幀的,但是這個函數是由精靈來自動調用的,我們沒有辦法重寫它,是以需要在update函數裡面做一些工作。
首先需要計算單個幀左上角的x,y位置值(x表示列編号,y表示行編号):
frame_x = (self.frame % self.columns) * self.frame_width
#用幀數目除以行數,然後在乘上幀的高度
frame_y = (self.frame // self.columns) * self.frame_height
然後将計算好的x,y值傳遞給位置rect屬性。
frame_x = (self.frame % self.columns) * self.frame_width
frame_y = (self.frame // self.columns) * self.frame_height
rect = ( frame_x, frame_y, self.frame_width, self.frame_height )
self.image = self.master_image.subsurface(rect)
5、精靈組
當程式中有大量的實體的時候,操作這些實體将會是一件相當麻煩的事,那麼有沒有什麼容器可以将這些精靈放在一起統一管理呢?答案就是精靈組。
pygame使用精靈組來管理精靈的繪制和更新,精靈組是一個簡單的容器。
使用pygame.sprite.Group()函數可以建立一個精靈組:
group = pygame.sprite.Group()
group.add(sprite_one)
精靈組也有update和draw函數:
group.update()
group.draw()
二、自定義的精靈類
好了,通過前面的學習,我們已經了解了一些精靈的知識了,現在我們将前面說到的方法封裝成一個自定義的類,以友善我們的調用,這個類繼承自pygame.sprite.Sprite:
1 class MySprite(pygame.sprite.Sprite):
2 def __init__(self, target):
3 pygame.sprite.Sprite.__init__(self) #基類的init方法
4 self.target_surface = target
5 self.image = None
6 self.master_image = None
7 self.rect = None
8 self.topleft = 0,0
9 self.frame = 0
10 self.old_frame = -1
11 self.frame_width = 1
12 self.frame_height = 1
13 self.first_frame = 0
14 self.last_frame = 0
15 self.columns = 1
16 self.last_time = 0
17
18 def load(self, filename, width, height, columns):
19 self.master_image = pygame.image.load(filename).convert_alpha()
20 self.frame_width = width
21 self.frame_height = height
22 self.rect = 0,0,width,height
23 self.columns = columns
25 rect = self.master_image.get_rect()
26 self.last_frame = (rect.width // width) * (rect.height // height) - 1
27
28 def update(self, current_time, rate=60):
29 #更新動畫幀
30 if current_time > self.last_time + rate:
31 self.frame += 1
32 if self.frame > self.last_frame:
33 self.frame = self.first_frame
34 self.last_time = current_time
35
37 if self.frame != self.old_frame:
38 frame_x = (self.frame % self.columns) * self.frame_width
39 frame_y = (self.frame // self.columns) * self.frame_height
40 rect = ( frame_x, frame_y, self.frame_width, self.frame_height )
41 self.image = self.master_image.subsurface(rect)
42 self.old_frame = self.frame
好了現在我們寫一個小程式來測試一下這個類的性能怎麼樣。
這裡我用ps制作了一個簡單的精靈序列圖,咱們就用這個萌萌的嗷大喵好了:
代碼:
import pygame
from pygame.locals import *
class MySprite(pygame.sprite.Sprite):
def __init__(self, target):
pygame.sprite.Sprite.__init__(self)
self.target_surface = target
self.image = None
self.master_image = None
self.rect = None
self.topleft = 0,0
self.frame = 0
self.old_frame = -1
self.frame_width = 1
self.frame_height = 1
self.first_frame = 0
self.last_frame = 0
self.columns = 1
self.last_time = 0
def load(self, filename, width, height, columns):
self.master_image = pygame.image.load(filename).convert_alpha()
self.frame_width = width
self.frame_height = height
self.rect = 0,0,width,height
self.columns = columns
rect = self.master_image.get_rect()
self.last_frame = (rect.width // width) * (rect.height // height) - 1
def update(self, current_time, rate=60):
if current_time > self.last_time + rate:
self.frame += 1
if self.frame > self.last_frame:
self.frame = self.first_frame
self.last_time = current_time
if self.frame != self.old_frame:
frame_x = (self.frame % self.columns) * self.frame_width
frame_y = (self.frame // self.columns) * self.frame_height
rect = ( frame_x, frame_y, self.frame_width, self.frame_height )
self.image = self.master_image.subsurface(rect)
self.old_frame = self.frame
pygame.init()
screen = pygame.display.set_mode((800,600),0,32)
pygame.display.set_caption("精靈類測試")
font = pygame.font.Font(None, 18)
framerate = pygame.time.Clock()
cat = MySprite(screen)
cat.load("sprite.png", 100, 100, 4)
group = pygame.sprite.Group()
group.add(cat)
while True:
framerate.tick(30)
ticks = pygame.time.get_ticks()
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
exit()
key = pygame.key.get_pressed()
if key[pygame.K_ESCAPE]:
exit()
screen.fill((0,0,100))
group.update(ticks)
group.draw(screen)
pygame.display.update()
效果圖:萌萌的嗷大喵躍然于螢幕上。看起來功能還不錯的說。
大家也可以制作一些自己喜歡的精靈序列圖,然後加載并檢視他們的效果。
關于精靈與精靈之間的沖突檢測,精靈與組之間的碰撞檢測,我們将會放在下個部落格一起學習。