利用pygame完成動畫精靈和碰撞檢測
動畫精靈和碰撞檢測
一、動畫精靈
動畫精靈:四處移動的單個圖像或圖像部分稱為動畫精靈(sprite),pygame有一個特殊的模塊幫助跟蹤屏幕上移動的大量圖像。利用這個模塊,可以更容易地移動圖形對象。
具備特征:
- 圖像(image):為動畫精靈顯示的圖片。
- 矩形區(rect):包含動畫精靈的矩形區域。
①、一堆沙灘球都反彈
Pygame 的 sprite 模塊提供瞭一個動畫精靈基類Sprite,基於pygame.sprite.Sprite 來創建自己的子類。
這裡用瞭常規的python列表
import sys, pygame class MyBallClass(pygame.sprite.Sprite): def __init__(self, image_file, location): pygame.sprite.Sprite.__init__(self) #初始化動畫精靈 self.image = pygame.image.load(image_file) #加載圖片 self.rect = self.image.get_rect() #得到定義圖像邊界矩形 self.rect.left, self.rect.top = location #設置球的初始位置 #設置窗口大小和顏色 size = width, height = 640, 480 screen = pygame.display.set_mode(size) screen.fill([255, 255, 255]) img_file = "beach_ball.png" balls = [] #將球增加到列表 for row in range(0, 3): for column in range(0, 3): location = [column * 180 + 10, row * 180 + 10] ball = MyBallClass(img_file, location) balls.append(ball) for ball in balls: screen.blit(ball.image, ball.rect) pygame.display.flip() running = True while running: for event in pygame.event.get(): if event.type == pygame.QUIT: running = False pygame.quit()
顯示效果:
②、讓小球動起來
move() 方法
創建一個新的類方法
def move(self): self.rect = self.rect.move(self.speed) #碰到窗口左右兩邊 if self.rect.left < 0 or self.rect.right > width: self.speed[0] = -self.speed[0] #碰到窗口上下兩邊 if self.rect.top < 0 or self.rect.bottom > height: self.speed[1] = -self.speed[1] ### 其中 self.speed告訴對象要移動多遠,包含2個列表。
import sys, pygame from random import * class MyBallClass(pygame.sprite.Sprite): def __init__(self, image_file, location, speed): pygame.sprite.Sprite.__init__(self) #初始化動畫精靈 self.image = pygame.image.load(image_file) #加載圖片 self.rect = self.image.get_rect() #得到定義圖像邊界矩形 self.rect.left, self.rect.top = location #設置球的初始位置 self.speed = speed #創建一個速度 def move(self): self.rect = self.rect.move(self.speed) if self.rect.left < 0 or self.rect.right > width: self.speed[0] = -self.speed[0] if self.rect.top < 0 or self.rect.bottom > height: self.speed[1] = -self.speed[1] #設置窗口大小和顏色 size = width, height = 640, 480 screen = pygame.display.set_mode(size) screen.fill([255, 255, 255]) img_file = "beach_ball.png" balls = [] #將球增加到列表 for row in range(0, 3): for column in range(0, 3): location = [column * 180 + 10, row * 180 + 10] speed = [choice([-2, 2]), choice([-2, 2])] #讓每個球變得隨機性 ball = MyBallClass(img_file, location, speed) print("y = ", ball.rect) balls.append(ball) running = True while running: for event in pygame.event.get(): if event.type == pygame.QUIT: running = False #這裡不是單獨擦除(覆蓋各個球),直接用白色填充窗口,重新繪制 pygame.time.delay(20) screen.fill([255, 255, 255]) for ball in balls: ball.move() screen.blit(ball.image, ball.rect) pygame.display.flip() pygame.quit()
二、碰撞檢測
上面的動畫,僅僅隻是移動和反彈,球與球之間的碰撞還不能反彈.
Pygame 中已經內置有這種碰撞檢測。
Pygame 還提供瞭一種方法對動畫精靈分組。 Pygame 的 group 類。例如,在保齡球遊戲中,所有球瓶可能在一組,球則在另一組。
動畫精靈模塊的spritecollide()函數用來檢測某個精靈是否與制定組的其他精靈發生碰撞,
這個函數的形式如下:
spritecollide(被檢測的精靈(sprite),指定組(group),是否重疊(False))
要檢查組中精靈之間的碰撞:
- 從這個組中刪除這個精靈;
- 檢查這個精靈與組中其他精靈之間的碰撞;
- 再把這個精靈添加回原來的組中。
問:為什麼要先從組刪除?
答:如果開始時沒有從組中刪除這個精靈,spritecollide() 會檢測到這個精靈與它自身發生瞭碰撞,因為它也在這個組中
import sys, pygame from random import * class MyBallClass(pygame.sprite.Sprite): def __init__(self, image_file, location, speed): pygame.sprite.Sprite.__init__(self) #初始化動畫精靈 self.image = pygame.image.load(image_file) #加載圖片 self.rect = self.image.get_rect() #得到定義圖像邊界矩形 self.rect.left, self.rect.top = location #設置球的初始位置 self.speed = speed #創建一個速度 def move(self): self.rect = self.rect.move(self.speed) if self.rect.left < 0 or self.rect.right > width: self.speed[0] = -self.speed[0] if self.rect.top < 0 or self.rect.bottom > height: self.speed[1] = -self.speed[1] def animate(group): screen.fill([255, 255, 255]) for ball in group: group.remove(ball) #從組刪除精靈 #檢查精靈與組的碰撞 if pygame.sprite.spritecollide(ball, group, False): ball.speed[0] = -ball.speed[0] ball.speed[1] = -ball.speed[1] group.add(ball) ball.move() screen.blit(ball.image, ball.rect) pygame.display.flip() pygame.time.delay(20) #設置窗口大小和顏色 size = width, height = 640, 480 screen = pygame.display.set_mode(size) screen.fill([255, 255, 255]) img_file = "beach_ball.png" group = pygame.sprite.Group() #創建精靈組 #將球增加到列表 for row in range(0, 2): for column in range(0, 2): location = [column * 180 + 10, row * 180 + 10] speed = [choice([-2, 2]), choice([-2, 2])] #讓每個球變得隨機性 ball = MyBallClass(img_file, location, speed) group.add(ball) running = True while running: for event in pygame.event.get(): if event.type == pygame.QUIT: running = False #這裡不是單獨擦除(覆蓋各個球),直接用白色填充窗口,重新繪制 pygame.time.delay(20) screen.fill([255, 255, 255]) animate(group) pygame.quit()
上述代碼存在問題:
- 球碰撞時,它們會“顫抖”或者發生兩次 碰撞;
- 有時球會“卡”在窗口邊界上,顫抖一段時間。
可能原因:上述代碼是先移動一個球,檢查碰撞,然後移動球在檢查下一個。這樣子可能造成球的滯後性
修改animate函數() def animate(group): screen.fill([255, 255, 255]) for ball in group: ball.move() for ball in group: group.remove(ball) #從組刪除精靈 #檢查精靈與組的碰撞 if pygame.sprite.spritecollide(ball, group, False): ball.speed[0] = -ball.speed[0] ball.speed[1] = -ball.speed[1] group.add(ball) #ball.move() screen.blit(ball.image, ball.rect) pygame.display.flip() pygame.time.delay(20)
矩形碰撞與像素完美結合
在觀察可以方向,球“碰撞”時並不是完全接觸。因為 spritecollide() 沒有使用球的圓形輪廓來檢測碰撞。它使用瞭球的 rect,也就是球的外圍矩形。
如果希望球的圓形部分(而不是矩形邊界)真正接觸時球才會相互反彈,就必須使用一種稱為“像素完美碰撞檢測 ” 的 方 法。
三、統計時間
在之前我們都用time.delay(50)來控制動畫運行的快慢,
pygame.time.delay(50)
在計算機圖形學中,每個動畫步叫做一幀,遊戲程序員討論圖形更新的快慢時都會提到幀速率(每秒幀數,fps)
問:要怎麼控制未知代碼運行時間?
——Pygame 的 time 模塊提供瞭這樣的工具:一個名為 Clock 的類。
用 pygame.time.Clock() 控制幀速率
並不是向每個循環增加一個延遲, pygame.time.Clock() 會控制每個循環多長時間運行一次。
clock = pygame.time.Clock() clock.tick(60) #此處的60指示:這個循環應當每秒運行 60 次。 每秒 60 個循環(或幀)時,每個循環需要 1000 / 60 = 16.66 ms(大約 17 ms)。 如果循環中的代碼運行時間超過 17 ms,在 clock 指出開始下一次循環時當前循環將無法完成。
檢查幀速率
clock.get_fps() 知道程序能以多快的速度運行,檢查幀速率
調整幀速率
假設設置運行 clock.tick(30),每秒39幀,但 clock.get_fps()檢測實際得到的速率為20fps,說明每個循環運行的時間比預計的長,這樣達不到原來要的效果,需要調整幀速率。
以小球為例,原來每秒30幀,可以將小球移動比較遠,但是目前的代碼每秒隻能運行20幀,達不到預期的要求,需要在限有的時間內到達移動的距離,就需要更改小球移動的速度。可以按期望幀頻率與實際幀速率的比值來增加
如果小球對象距離是10,期望的幀速率是30fps,程序實際運行速率為20fps.
object_speed = current_speed * (desired fps / actual fps) object_speed = 10 * (30 / 20) object_speed = 15
沙灘球程序中使用 Clock 和 get_fps()
import sys, pygame from random import * class MyBallClass(pygame.sprite.Sprite): def __init__(self, image_file, location, speed): pygame.sprite.Sprite.__init__(self) #初始化動畫精靈 self.image = pygame.image.load(image_file) #加載圖片 self.rect = self.image.get_rect() #得到定義圖像邊界矩形 self.rect.left, self.rect.top = location #設置球的初始位置 self.speed = speed #創建一個速度 def move(self): self.rect = self.rect.move(self.speed) if self.rect.left < 0 or self.rect.right > width: self.speed[0] = -self.speed[0] if self.rect.top < 0 or self.rect.bottom > height: self.speed[1] = -self.speed[1] def animate(group): screen.fill([255, 255, 255]) for ball in group: ball.move() for ball in group: group.remove(ball) #從組刪除精靈 #檢查精靈與組的碰撞 if pygame.sprite.spritecollide(ball, group, False): ball.speed[0] = -ball.speed[0] ball.speed[1] = -ball.speed[1] group.add(ball) screen.blit(ball.image, ball.rect) pygame.display.flip() pygame.time.delay(20) #設置窗口大小和顏色 size = width, height = 640, 480 screen = pygame.display.set_mode(size) screen.fill([255, 255, 255]) img_file = "beach_ball.png" clock = pygame.time.Clock() group = pygame.sprite.Group() #創建精靈組 #將球增加到列表 for row in range(0, 2): for column in range(0, 2): location = [column * 180 + 10, row * 180 + 10] speed = [choice([-4, 4]), choice([-4, 4])] #讓每個球變得隨機性 ball = MyBallClass(img_file, location, speed) group.add(ball) running = True while running: for event in pygame.event.get(): if event.type == pygame.QUIT: running = False frame_rate = clock.get_fps() print( "frame rate = ", frame_rate) animate(group) clock.tick(30) pygame.quit()
到此這篇關於利用pygame完成動畫精靈和碰撞檢測的文章就介紹到這瞭,希望對大傢有幫助,更多相關pygame內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章,希望大傢以後多多支持WalkonNet!
推薦閱讀:
- None Found