Pygame游戲開發(fā)之太空射擊實(shí)戰(zhàn)子彈與碰撞處理篇
視頻
碰撞
碰撞是游戲開發(fā)的基本組成部分。碰撞檢測(cè)就是要檢測(cè)游戲中的一個(gè)對(duì)象是否正在接觸另一個(gè)對(duì)象。碰撞處理決定了當(dāng)碰撞發(fā)生時(shí)你想要發(fā)生什么。
在我們的游戲中,我們目前有許多敵人的精靈沿著屏幕飛向玩家,我們想知道其中一個(gè)精靈何時(shí)出現(xiàn)。對(duì)于我們游戲的這個(gè)階段,我們只會(huì)說敵人擊中玩家意味著游戲結(jié)束了。
邊界框
請(qǐng)記住,Pygame 中的每個(gè)精靈都有一個(gè)rect定義其坐標(biāo)和大小的屬性。Pygame中的一個(gè)Rect對(duì)象的格式是[x, y, width, height],其中 x和 y表示矩形的左上角。此矩形的另一個(gè)詞是邊界框,因?yàn)樗硎緦?duì)象的邊界。
這種碰撞檢測(cè)稱為AABB,代表“軸對(duì)齊邊界框”,因?yàn)榫匦闻c屏幕軸對(duì)齊 - 它們不會(huì)傾斜一定角度。AABB碰撞非常受歡迎,因?yàn)樗俣群芸?- 計(jì)算機(jī)可以非??焖俚乇容^矩形的坐標(biāo),如果您要比較大量對(duì)象,這將非常有用。
要檢測(cè)碰撞,我們需要查看玩家的rect碰撞,并將其與每個(gè)敵人的rect碰撞進(jìn)行比較?,F(xiàn)在,我們可以通過循環(huán)遍歷敵人并對(duì)執(zhí)行此比較的每個(gè)敵人來執(zhí)行此操作:

在這張圖片中,您可以看到只有矩形#3與大黑色矩形相撞。#1 在 x 軸上重疊,但在 y 軸上不重疊; #2 在 y 中重疊,但在 x 中不重疊。為了使兩個(gè)矩形重疊,它們的邊界必須在每個(gè)軸上重疊。在代碼中編寫以下內(nèi)容:
if mob.rect.right > player.rect.left and \
mob.rect.left < player.rect.right and \
mob.rect.bottom > player.rect.top and \
mob.rect.top < player.rect.bottom:
collide = True
對(duì)我們來說幸運(yùn)的是,Pygame有一個(gè)內(nèi)置的方式,通過使用這個(gè)spritecollide()函數(shù)來做到這一點(diǎn)。
敵人與玩家碰撞
我們將此命令添加到游戲循環(huán)的“更新”部分:
#Update
all_sprites.update()
#check to see if a mob hit the player
hits = pygame.sprite.spritecollide(player, mobs, False)
if hits:
running = False
spritecollide()語法
#Find sprites in a group that intersect another sprite. spritecollide(sprite, group, dokill, collided = None) -> Sprite_list
該spritecollide()函數(shù)采用 3 個(gè)參數(shù):要檢查的 sprite 的名稱、要與之進(jìn)行比較的組的名稱,以及名為dokill 的 True/False 參數(shù)。
dokill參數(shù)如果設(shè)置為 True,則所有發(fā)生碰撞的sprite都將從組中刪除。例如,如果我們?cè)噲D查看玩家是否撿到了一枚硬幣,我們希望將其設(shè)置為True硬幣消失。
collied參數(shù)是一個(gè)回調(diào)函數(shù),用于計(jì)算兩個(gè)精靈是否發(fā)生沖突。它應(yīng)該將兩個(gè)精靈作為值,并返回一個(gè)bool值,指示它們是否發(fā)生沖突。如果未傳遞collied參數(shù),則所有精靈都必須具有“rect”值,該值是精靈區(qū)域的矩形,將用于計(jì)算碰撞。
該spritecollide()命令的返回結(jié)果是被擊中的精靈列表(請(qǐng)記住,玩家可能一次與多個(gè)敵人相撞)。我們將該列表分配給變量hits 。
如果hits列表不為空,則if語句將為True ,并且我們?cè)O(shè)置running為False游戲?qū)⒔Y(jié)束。
射擊
子彈精靈
現(xiàn)在我們準(zhǔn)備添加一個(gè)新的精靈:子彈。這將是一個(gè)精靈,在我們射擊時(shí)生成,出現(xiàn)在玩家精靈的頂部,并以相當(dāng)高的速度向上移動(dòng)。定義精靈現(xiàn)在應(yīng)該開始看起來很熟悉了,所以下面是完整的Bullet類:

class Bullet(pygame.sprite.Sprite):
def __init__(self, x, y):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((10, 20))
self.image.fill(YELLOW)
self.rect = self.image.get_rect()
self.rect.bottom = y
self.rect.centerx = x
self.speedy = -10
def update(self):
self.rect.y += self.speedy
# kill if it moves off the top of the screen
if self.rect.bottom < 0:
self.kill()在子彈精靈的__init__()方法中,我們傳遞x和y值,以便我們可以告訴精靈出現(xiàn)在哪里。由于玩家精靈可以移動(dòng),因此將設(shè)置為玩家射擊時(shí)玩家所在的位置。我們?cè)O(shè)置speedy為負(fù)值,以便它將向上移動(dòng)。
最后,我們檢查子彈是否從屏幕頂部脫落,如果是這樣,我們將其刪除。
按鍵事件
為了保持簡(jiǎn)單,我們將讓它每次玩家按下空格鍵時(shí),都會(huì)發(fā)射一顆子彈。我們需要將其添加到事件檢查中:
for event in pygame.event.get():
# check for closing window
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
player.shoot()我們的新代碼檢查事件,如果存在KEYDOWN事件,則檢查它是否是K_SPACE按鍵。如果是這樣,我們將運(yùn)行玩家精靈的shoot()方法。
生成子彈
首先,我們需要添加一個(gè)新組來保存所有項(xiàng)目符號(hào):
bullets = pygame.sprite.Group()
現(xiàn)在,我們可以將以下方法添加到Player類中:
def shoot(self):
bullet = Bullet(self.rect.centerx, self.rect.top)
all_sprites.add(bullet)
bullets.add(bullet)
該shoot()方法所做的就是生成一顆子彈,使用玩家的頂部中心作為生成點(diǎn)。然后,我們確保將子彈精靈添加到all_sprites(以便將其繪制和更新)和 bullets,我們將用于碰撞。
子彈碰撞
現(xiàn)在我們需要檢查子彈是否擊中了敵人。這里的區(qū)別在于,我們有多個(gè)子彈(在bullets組中)和多個(gè)敵人(在mobs組中),所以我們不能像以前那樣使用spritecollide(),因?yàn)檫@只會(huì)將一個(gè)精靈與一個(gè)組進(jìn)行比較。相反,我們將使用groupcollide() :
groupcollide()語法
# Find all sprites that collide between two groups. groupcollide(group1, group2, dokill1, dokill2, collided = None) -> Sprite_dict
發(fā)現(xiàn)兩組中的所有精靈之間的沖突。通過比較每個(gè) Sprite 的Sprite.rect屬性或使用collided函數(shù)(如果不是 None)來確定碰撞。
返回值是個(gè)字典,未發(fā)生碰撞時(shí),字典為空。發(fā)生碰撞時(shí),字典的keys是group1中發(fā)生碰撞的對(duì)象(一直以為字典的keys值是個(gè)字符串之類的東西,沒想到還可以是對(duì)象),values是一個(gè)列表,其元素是group2中與每一個(gè)group1發(fā)生碰撞的所有對(duì)象。
Groupcollide返回值的形態(tài)和使用是個(gè)難點(diǎn),舉例說明:比如group1有三架飛機(jī)對(duì)象p1、p2、p3,group2有八個(gè)敵人對(duì)象b1、b2、b3、b4、b5、b6、b7、b8,假如p1與b1、b4、b7發(fā)生碰撞,p2與b2、b5、b8發(fā)生碰撞,
groupcollide(group1, group2, 1, 1)
返回值是如下的字典:
{p1:[b1, b4, b7], p2:[b2, b5, b8]}
group1 中的每個(gè)發(fā)生碰撞的Sprite 都會(huì)添加到返回字典中,作為鍵。每個(gè)項(xiàng)的值是組 2 中相交的精靈列表。
如果任一 dokill 參數(shù)為 True,則碰撞的精靈將從其各自的組中刪除。
collieded參數(shù)是一個(gè)回調(diào)函數(shù),用于計(jì)算兩個(gè)精靈是否發(fā)生沖突。它應(yīng)該將兩個(gè)精靈作為值,并返回一個(gè) bool 值,指示它們是否正在碰撞。如果未傳遞collided,則所有精靈都必須具有“rect”值,該值是精靈區(qū)域的矩形,將用于計(jì)算碰撞。
# Update
all_sprites.update()
# check to see if a bullet hit a mob
hits = pygame.sprite.groupcollide(mobs, bullets, True, True)
for hit in hits:
m = Mob()
all_sprites.add(m)
mobs.add(m)
該groupcollide()函數(shù)類似于spritecollide() ,除了您命名兩個(gè)組進(jìn)行比較,并且您將獲得的是被擊中的敵人列表。有兩個(gè)dokill選項(xiàng),每個(gè)組一個(gè)。
如果我們只是刪除敵人,我們將遇到一個(gè)問題:敵人用完了!因此,我們要做的就是hits循環(huán),對(duì)于我們摧毀的每個(gè)敵人,將生成另一個(gè)新的敵人。
現(xiàn)在它實(shí)際上開始感覺像一個(gè)游戲:

在下一課中,我們將學(xué)習(xí)如何向游戲添加圖形,而不是使用那些純色矩形。
# KidsCanCode - Game Development with Pygame video series
# Shmup game - part 3
# Video link: https://www.youtube.com/watch?v=33g62PpFwsE
# Collisions and bullets
import pygame
import random
WIDTH = 480
HEIGHT = 600
FPS = 60
# define colors
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
YELLOW = (255, 255, 0)
# initialize pygame and create window
pygame.init()
pygame.mixer.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Shmup!")
clock = pygame.time.Clock()
class Player(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((50, 40))
self.image.fill(GREEN)
self.rect = self.image.get_rect()
self.rect.centerx = WIDTH / 2
self.rect.bottom = HEIGHT - 10
self.speedx = 0
def update(self):
self.speedx = 0
keystate = pygame.key.get_pressed()
if keystate[pygame.K_LEFT]:
self.speedx = -8
if keystate[pygame.K_RIGHT]:
self.speedx = 8
self.rect.x += self.speedx
if self.rect.right > WIDTH:
self.rect.right = WIDTH
if self.rect.left < 0:
self.rect.left = 0
def shoot(self):
bullet = Bullet(self.rect.centerx, self.rect.top)
all_sprites.add(bullet)
bullets.add(bullet)
class Mob(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((30, 40))
self.image.fill(RED)
self.rect = self.image.get_rect()
self.rect.x = random.randrange(WIDTH - self.rect.width)
self.rect.y = random.randrange(-100, -40)
self.speedy = random.randrange(1, 8)
self.speedx = random.randrange(-3, 3)
def update(self):
self.rect.x += self.speedx
self.rect.y += self.speedy
if self.rect.top > HEIGHT + 10 or self.rect.left < -25 or self.rect.right > WIDTH + 20:
self.rect.x = random.randrange(WIDTH - self.rect.width)
self.rect.y = random.randrange(-100, -40)
self.speedy = random.randrange(1, 8)
class Bullet(pygame.sprite.Sprite):
def __init__(self, x, y):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((10, 20))
self.image.fill(YELLOW)
self.rect = self.image.get_rect()
self.rect.bottom = y
self.rect.centerx = x
self.speedy = -10
def update(self):
self.rect.y += self.speedy
# kill if it moves off the top of the screen
if self.rect.bottom < 0:
self.kill()
all_sprites = pygame.sprite.Group()
mobs = pygame.sprite.Group()
bullets = pygame.sprite.Group()
player = Player()
all_sprites.add(player)
for i in range(8):
m = Mob()
all_sprites.add(m)
mobs.add(m)
# Game loop
running = True
while running:
# keep loop running at the right speed
clock.tick(FPS)
# Process input (events)
for event in pygame.event.get():
# check for closing window
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
player.shoot()
# Update
all_sprites.update()
# check to see if a bullet hit a mob
hits = pygame.sprite.groupcollide(mobs, bullets, True, True)
for hit in hits:
m = Mob()
all_sprites.add(m)
mobs.add(m)
# check to see if a mob hit the player
hits = pygame.sprite.spritecollide(player, mobs, False)
if hits:
running = False
# Draw / render
screen.fill(BLACK)
all_sprites.draw(screen)
# *after* drawing everything, flip the display
pygame.display.flip()
pygame.quit()到此這篇關(guān)于Pygame游戲開發(fā)之太空射擊實(shí)戰(zhàn)子彈與碰撞處理篇的文章就介紹到這了,更多相關(guān)Pygame子彈與碰撞處理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Pygame改編飛機(jī)大戰(zhàn)制作兔子接月餅游戲
- Python+Pygame實(shí)現(xiàn)海洋之神大冒險(xiǎn)游戲
- Python pygame 動(dòng)畫游戲循環(huán)游戲時(shí)鐘實(shí)現(xiàn)原理
- Python+Pygame實(shí)戰(zhàn)之實(shí)現(xiàn)小蜜蜂歷險(xiǎn)記游戲
- Python+Pygame實(shí)戰(zhàn)之英文版猜字游戲的實(shí)現(xiàn)
- Pygame游戲開發(fā)之太空射擊實(shí)戰(zhàn)盾牌篇
- Pygame游戲開發(fā)之太空射擊實(shí)戰(zhàn)碰撞改進(jìn)篇
- Pygame游戲開發(fā)之太空射擊實(shí)戰(zhàn)添加圖形篇
- pygame實(shí)現(xiàn)一個(gè)類似滿天星游戲流程詳解
相關(guān)文章
Python實(shí)現(xiàn)一鍵整理百度云盤中重復(fù)無用文件
有沒有頭疼過百度云盤都要塞滿了,可是又沒有工具能剔除大量重復(fù)無用的文件?這里教你一個(gè)用Python實(shí)現(xiàn)的簡(jiǎn)單方法,通過整理目錄的方式來處理我們?cè)票P中無用的文件吧2022-08-08
使用scrapy實(shí)現(xiàn)爬網(wǎng)站例子和實(shí)現(xiàn)網(wǎng)絡(luò)爬蟲(蜘蛛)的步驟
本文分二個(gè)示例,第一個(gè)是個(gè)簡(jiǎn)單的爬網(wǎng)站的小例子,第二個(gè)例子實(shí)現(xiàn)目是從一個(gè)網(wǎng)站的列表頁抓取文章列表,然后存入數(shù)據(jù)庫中,數(shù)據(jù)庫包括文章標(biāo)題、鏈接、時(shí)間,大家參考使用吧2014-01-01
Python使用Beautiful Soup實(shí)現(xiàn)解析網(wǎng)頁
在這篇文章中,我們將介紹如何使用 Python 編寫一個(gè)簡(jiǎn)單的網(wǎng)絡(luò)爬蟲,以獲取并解析網(wǎng)頁內(nèi)容。我們將使用 Beautiful Soup 庫,它是一個(gè)非常強(qiáng)大的庫,用于解析和操作 HTML 和 XML 文檔。讓我們開始吧2023-05-05
將Django使用的數(shù)據(jù)庫從MySQL遷移到PostgreSQL的教程
這篇文章主要介紹了將Django使用的數(shù)據(jù)庫從MySQL遷移到PostgreSQL的教程,同時(shí)提到了一些注意事項(xiàng),需要的朋友可以參考下2015-04-04
Python Unittest根據(jù)不同測(cè)試環(huán)境跳過用例的方法
這篇文章主要給大家介紹了關(guān)于Python Unittest如何根據(jù)不同測(cè)試環(huán)境跳過用例的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起看看吧2018-12-12
Django之第三方平臺(tái)QQ授權(quán)登錄的實(shí)現(xiàn)
本文主要介紹了Django之第三方平臺(tái)QQ授權(quán)登錄的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-05-05
淺談Pandas中map, applymap and apply的區(qū)別
下面小編就為大家分享一篇淺談Pandas中map, applymap and apply的區(qū)別,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-04-04

