那應該如何實現呢?
說白了,它這個原理很簡單,就是檢測兩個精靈之間是否存在重疊的部分,像我們上節課的小球,在圖1的情況下,它們就沒有產生重疊,也就是沒有發生碰撞。
圖1
當碰撞發生的那一剎那,width = r1 + r2,如圖2所示。
圖2
當它們產生重疊,產生交集的時候,width < r1 + r2,如圖3所示:
圖3
所以我們判斷兩個小球是否發生碰撞,我們只要檢測兩個小球的圓心距是否小於等於它們兩個半徑之和。
我們先來嘗試自己寫碰撞檢測的函數:
(我當然知道sprite 模塊裡面有提供現成的,但是我提議大家學習任何東西之前我們要搞懂它的原理,懂得了它的原理,以後你學習別的語言、別的知識就會事半功倍了)
函數名叫做collide_check()。
-
-
-
-
from pygame.locals import *
-
-
-
-
class Ball(pygame.sprite.Sprite):
-
def __init__(self, image, position, speed, bg_size):
-
-
pygame.sprite.Sprite.__init__(self)
-
-
self.image = pygame.image.load(image).convert_alpha()
-
self.rect = self.image.get_rect()
-
-
self.rect.left, self.rect.top = position
-
-
self.width, self.height = bg_size[0], bg_size[1]
-
-
-
self.rect = self.rect.move(self.speed)
-
-
-
-
-
self.rect.left = self.width
-
-
elif self.rect.left > self.width:
-
-
-
elif self.rect.bottom < 0:
-
self.rect.top = self.height
-
-
elif self.rect.top > self.height:
-
-
-
def collide_check(item, target):
-
-
-
-
math.pow((item.rect.center[0] - each.rect.center[0]), 2) + \
-
math.pow((item.rect.center[1] - each.rect.center[1]), 2))
-
if distance <= (item.rect.width + each.rect.width) / 2:
-
-
-
-
-
-
-
-
ball_image = "gray_ball.png"
-
bg_image = "background.png"
-
-
-
-
-
bg_size = width, height = 1024, 681
-
screen = pygame.display.set_mode(bg_size)
-
pygame.display.set_caption("Play the ball - Python Demo")
-
-
background = pygame.image.load(bg_image).convert_alpha()
-
-
-
-
-
-
-
for i in range(BALL_NUM):
-
-
position = randint(0, width-100), randint(0, height-100)
-
speed = [randint(-10, 10), randint(-10, 10)]
-
ball = Ball(ball_image, position, speed, bg_size)
-
-
while collide_check(ball, balls):
-
ball.rect.left, ball.rect.top = randint(0, width-100), randint(0, height-100)
-
-
-
clock = pygame.time.Clock()
-
-
-
for event in pygame.event.get():
-
-
-
-
screen.blit(background, (0, 0))
-
-
-
-
screen.blit(each.image, each.rect)
-
-
for i in range(BALL_NUM):
-
-
-
if collide_check(item, balls):
-
item.speed[0] = -item.speed[0]
-
item.speed[1] = -item.speed[1]
-
-
-
-
-
-
-
-
if __name__ == "__main__":
-
這就已經很完美了。
上面是我們自己寫的碰撞檢測。接下來我們來談一下現成的。
為什麼我們要談一下sprite 提供的現成的碰撞檢測函數呢?不知道大家有沒有註意到我們寫的collide_check() 函數只是適用於圓與圓之間的碰撞檢測,其它的多邊形(如矩形、三角形)以及一些不規則的多邊形,那怎麼辦,我們就不會得到相應的結果了,我們可以試圖去為每一種情況寫一個碰撞檢測函數,也不是不可以。
但其實Pygame 的sprite 模塊事實上已經提供了一個成熟的碰撞檢測的函數供我們使用,這也是我們要將我們的類繼承sprite 模塊的Sprite 基類的原因。
sprite 模塊提供了spritecollide() 方法用於檢測某個精靈是否與指定的組中的其它精靈發生碰撞。(和我們寫的collide_check() 原理基本上類似)
spritecollide(sprite, group, dokill, collided = None)
第一個參數sprite 是指定被檢測的精靈;(就是我們寫的里面的item)
第二個參數group 是指定一個組(就是我們寫的里面的target 列表),它是sprite 的組,因此要使用sprite.Group() 來生成;
第三個參數dokill 是設置是否從組中刪除檢測到碰撞的精靈,設置為True,則刪除;
第四個參數collided 是指定一個回調函數,用於定制特殊的檢測方法,如果第四個參數忽略的話,默認是檢測精靈之間的rect 屬性。
(我們這裡不能使用默認的檢測方法,看下圖,圖中的情況會被檢測為碰撞,但其實兩小球還沒碰撞)
collide_circle(left, right) 方法適用於檢測兩個圓之間是否發生碰撞,left 和right 參數分別是兩個精靈,所以我們直接使用
collided = pygame.sprite.collide_circle
另外,collide_circle()方法需要精靈有一個半徑的屬性,所以我們給Ball 類增加一個radius 屬性。
代碼如下:
-
-
-
from pygame.locals import *
-
-
-
-
class Ball(pygame.sprite.Sprite):
-
def __init__(self, image, position, speed, bg_size):
-
-
pygame.sprite.Sprite.__init__(self)
-
-
self.image = pygame.image.load(image).convert_alpha()
-
self.rect = self.image.get_rect()
-
-
self.rect.left, self.rect.top = position
-
-
self.width, self.height = bg_size[0], bg_size[1]
-
self.radius = self.rect.width / 2
-
-
-
self.rect = self.rect.move(self.speed)
-
-
-
-
-
self.rect.left = self.width
-
-
elif self.rect.left > self.width:
-
-
-
elif self.rect.bottom < 0:
-
self.rect.top = self.height
-
-
elif self.rect.top > self.height:
-
-
-
-
-
-
ball_image = "gray_ball.png"
-
bg_image = "background.png"
-
-
-
-
-
bg_size = width, height = 1024, 681
-
screen = pygame.display.set_mode(bg_size)
-
pygame.display.set_caption("Play the ball - Python Demo")
-
-
background = pygame.image.load(bg_image).convert_alpha()
-
-
-
-
group = pygame.sprite.Group()
-
-
-
-
-
position = randint(0, width-100), randint(0, height-100)
-
speed = [randint(-10, 10), randint(-10, 10)]
-
ball = Ball(ball_image, position, speed, bg_size)
-
while pygame.sprite.spritecollide(ball, group, False, pygame.sprite.collide_circle):
-
ball.rect.left, ball.rect.top = randint(0, width-100), randint(0, height-100)
-
-
-
-
clock = pygame.time.Clock()
-
-
-
for event in pygame.event.get():
-
-
-
-
screen.blit(background, (0, 0))
-
-
-
-
screen.blit(each.image, each.rect)
-
-
-
-
-
if pygame.sprite.spritecollide(each, group, False, pygame.sprite.collide_circle):
-
each.speed[0] = -each.speed[0]
-
each.speed[1] = -each.speed[1]
-
-
-
-
-
-
-
-
if __name__ == "__main__":
-
0 留言:
發佈留言