天天看點

【python遊戲程式設計之旅】第六篇---pygame中的Sprite(精靈)子產品和加載動畫

本系列部落格介紹以python+pygame庫進行小遊戲的開發。有寫的不對之處還望各位海涵。

直到現在我們已經學了很多pygame基礎知識了,從這篇部落格開始我們将會學習pygame中進階部分,更多和精靈子產品,沖突檢測相關的知識。

一、Sprite子產品、sprite對象

在pygame.sprite子產品裡面包含了一個名為Sprite類,他是pygame本身自帶的一個精靈。但是這個類的功能比較少,是以我們建立一個類對其繼承,在sprite類的基礎上豐富,以友善我們的使用。

首先來了解一下如何使用sprite類來加載動畫吧。

1、精靈序列圖

将要加載的動畫幀放在一個精靈序列圖裡面,然後在程式裡面調用它。pygame會自動更新動畫幀,這樣一個動态的圖像就會展現在我們面前了。

下面是一個典型的精靈序列圖:行和列的索引都是從0開始的。

【python遊戲程式設計之旅】第六篇---pygame中的Sprite(精靈)子產品和加載動畫

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制作了一個簡單的精靈序列圖,咱們就用這個萌萌的嗷大喵好了:

【python遊戲程式設計之旅】第六篇---pygame中的Sprite(精靈)子產品和加載動畫

代碼:

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()
      

效果圖:萌萌的嗷大喵躍然于螢幕上。看起來功能還不錯的說。

【python遊戲程式設計之旅】第六篇---pygame中的Sprite(精靈)子產品和加載動畫

大家也可以制作一些自己喜歡的精靈序列圖,然後加載并檢視他們的效果。

關于精靈與精靈之間的沖突檢測,精靈與組之間的碰撞檢測,我們将會放在下個部落格一起學習。