当前位置:   article > 正文

《零基础入门学习Python》第086讲:Pygame:碰撞检测_碰撞检测方法python

碰撞检测方法python

上节课我们介绍了动画精灵,这节课我们把重点放在碰撞检测上,大部分游戏都是需要做碰撞检测的,因为你需要知道小球是否发生了碰撞,子弹是否击中了目标,主角是否踩到了狗屎。

那应该如何实现呢?

说白了,它这个原理很简单,就是检测两个精灵之间是否存在重叠的部分,像我们上节课的小球,在图1的情况下,它们就没有产生重叠,也就是没有发生碰撞。

图1

当碰撞发生的那一刹那,width = r1 + r2,如图2所示。

图2

当它们产生重叠,产生交集的时候,width < r1 + r2,如图3所示:

图3

所以我们判断两个小球是否发生碰撞,我们只要检测两个小球的圆心距是否小于等于它们两个半径之和。

我们先来尝试自己写碰撞检测的函数:

(我当然知道 sprite 模块里面有提供现成的,但是我提议大家学习任何东西之前我们要搞懂它的原理,懂得了它的原理,以后你学习别的语言、别的知识就会事半功倍了)

函数名叫做 collide_check()。

  1. import pygame
  2. import sys
  3. import math
  4. from pygame.locals import *
  5. from random import *
  6. # 球类继承自Spirte类
  7. class Ball(pygame.sprite.Sprite):
  8. def __init__(self, image, position, speed, bg_size):
  9. # 初始化动画精灵
  10. pygame.sprite.Sprite.__init__(self)
  11. self.image = pygame.image.load(image).convert_alpha()
  12. self.rect = self.image.get_rect()
  13. # 将小球放在指定位置
  14. self.rect.left, self.rect.top = position
  15. self.speed = speed
  16. self.width, self.height = bg_size[0], bg_size[1]
  17. def move(self):
  18. self.rect = self.rect.move(self.speed)
  19. # 如果小球的左侧出了边界,那么将小球左侧的位置改为右侧的边界
  20. # 这样便实现了从左边进入,右边出来的效果
  21. if self.rect.right < 0:
  22. self.rect.left = self.width
  23. elif self.rect.left > self.width:
  24. self.rect.right = 0
  25. elif self.rect.bottom < 0:
  26. self.rect.top = self.height
  27. elif self.rect.top > self.height:
  28. self.rect.bottom = 0
  29. def collide_check(item, target): #碰撞检测
  30. col_balls = []
  31. for each in target:
  32. distance = math.sqrt(\
  33. math.pow((item.rect.center[0] - each.rect.center[0]), 2) + \
  34. math.pow((item.rect.center[1] - each.rect.center[1]), 2))
  35. if distance <= (item.rect.width + each.rect.width) / 2:
  36. col_balls.append(each)
  37. return col_balls #返回碰撞的小球
  38. def main():
  39. pygame.init()
  40. ball_image = "gray_ball.png"
  41. bg_image = "background.png"
  42. running = True
  43. # 根据背景图片指定游戏界面尺寸
  44. bg_size = width, height = 1024, 681
  45. screen = pygame.display.set_mode(bg_size)
  46. pygame.display.set_caption("Play the ball - Python Demo")
  47. background = pygame.image.load(bg_image).convert_alpha()
  48. # 用来存放小球对象的列表
  49. balls = []
  50. # 创建五个小球
  51. BALL_NUM = 5
  52. for i in range(BALL_NUM):
  53. # 位置随机,速度随机
  54. position = randint(0, width-100), randint(0, height-100)
  55. speed = [randint(-10, 10), randint(-10, 10)]
  56. ball = Ball(ball_image, position, speed, bg_size)
  57. while collide_check(ball, balls):#在创建小球的时候也要进行一次碰撞检测,防止一生成就与别的小球重叠了
  58. ball.rect.left, ball.rect.top = randint(0, width-100), randint(0, height-100)
  59. balls.append(ball)
  60. clock = pygame.time.Clock()
  61. while running:
  62. for event in pygame.event.get():
  63. if event.type == QUIT:
  64. sys.exit()
  65. screen.blit(background, (0, 0))
  66. for each in balls:
  67. each.move()
  68. screen.blit(each.image, each.rect)
  69. for i in range(BALL_NUM):
  70. item = balls.pop(i) #把自身拿出来
  71. if collide_check(item, balls): #把自己和别的球进行碰撞检测
  72. item.speed[0] = -item.speed[0]
  73. item.speed[1] = -item.speed[1]
  74. balls.insert(i, item) #还要把自己放进去
  75. pygame.display.flip()
  76. clock.tick(30)
  77. if __name__ == "__main__":
  78. 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 属性。

代码如下:

  1. import pygame
  2. import sys
  3. from pygame.locals import *
  4. from random import *
  5. # 球类继承自Spirte类
  6. class Ball(pygame.sprite.Sprite):
  7. def __init__(self, image, position, speed, bg_size):
  8. # 初始化动画精灵
  9. pygame.sprite.Sprite.__init__(self)
  10. self.image = pygame.image.load(image).convert_alpha()
  11. self.rect = self.image.get_rect()
  12. # 将小球放在指定位置
  13. self.rect.left, self.rect.top = position
  14. self.speed = speed
  15. self.width, self.height = bg_size[0], bg_size[1]
  16. self.radius = self.rect.width / 2 #增加半径属性
  17. def move(self):
  18. self.rect = self.rect.move(self.speed)
  19. # 如果小球的左侧出了边界,那么将小球左侧的位置改为右侧的边界
  20. # 这样便实现了从左边进入,右边出来的效果
  21. if self.rect.right < 0:
  22. self.rect.left = self.width
  23. elif self.rect.left > self.width:
  24. self.rect.right = 0
  25. elif self.rect.bottom < 0:
  26. self.rect.top = self.height
  27. elif self.rect.top > self.height:
  28. self.rect.bottom = 0
  29. def main():
  30. pygame.init()
  31. ball_image = "gray_ball.png"
  32. bg_image = "background.png"
  33. running = True
  34. # 根据背景图片指定游戏界面尺寸
  35. bg_size = width, height = 1024, 681
  36. screen = pygame.display.set_mode(bg_size)
  37. pygame.display.set_caption("Play the ball - Python Demo")
  38. background = pygame.image.load(bg_image).convert_alpha()
  39. # 用来存放小球对象的列表
  40. balls = []
  41. group = pygame.sprite.Group()
  42. # 创建五个小球
  43. for i in range(5):
  44. # 位置随机,速度随机
  45. position = randint(0, width-100), randint(0, height-100)
  46. speed = [randint(-10, 10), randint(-10, 10)]
  47. ball = Ball(ball_image, position, speed, bg_size)
  48. while pygame.sprite.spritecollide(ball, group, False, pygame.sprite.collide_circle):#在创建小球这里必须进行一下碰撞检测
  49. ball.rect.left, ball.rect.top = randint(0, width-100), randint(0, height-100)
  50. balls.append(ball)
  51. group.add(ball)
  52. clock = pygame.time.Clock()
  53. while running:
  54. for event in pygame.event.get():
  55. if event.type == QUIT:
  56. sys.exit()
  57. screen.blit(background, (0, 0))
  58. for each in balls:
  59. each.move()
  60. screen.blit(each.image, each.rect)
  61. for each in group:
  62. group.remove(each) #把自身拿出来
  63. if pygame.sprite.spritecollide(each, group, False, pygame.sprite.collide_circle):#把自己和别的球进行碰撞检测
  64. each.speed[0] = -each.speed[0]
  65. each.speed[1] = -each.speed[1]
  66. group.add(each)#还要把自己放进去
  67. pygame.display.flip()
  68. clock.tick(30)
  69. if __name__ == "__main__":
  70. main()

 

本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号