Python游戏开发:pygame中的冲突检测技术

一、精灵与精灵之间的冲突检测

1.两个精灵之间的矩形检测

在只有两个精灵的时候我们可以使用pygame.sprite.collide_rect()函数来进行一对一的冲突检测。这个函数需要传递2个参数,并且每个参数都是需要继承自pygame.sprite.Sprite。

举个例子:

 spirte_1 = MySprite("sprite_1.png",200,200,1) sprite_2 = MySprite("sprite_2.png",50,50,1) result = pygame.sprite.collide_rect(sprite_1,sprite_2) if result: print "Collision occurred"

MySprite使我们上个博客中创建的类,他继承自sprite。

Hint:这个函数还有一个非常有用的变体:pygame.sprite.collide_rect_ratio()。这个函数需要一个额外的浮点类型的参数。这个参数用来指定检测矩形的百分比。

有的时候我们希望冲突检测更精准一些的话,就可以收缩检测的区域,让矩形更小一些,就是通过这个参数控制的。使用方法如下:

 result = pygame.sprite.collide_rect_ratio( 0.5 )(sprite_1,sprite_2) 

2.两个精灵之间的圆检测

矩形冲突检测并不适用于所有形状的精灵,因此pygame中还有个圆形冲突检测。pygame.sprite.collide_circle(),这个函数是基于每个精灵的半径值来进行检测的。

你可以自己指定半径,或者让函数自己计算半径。

 result = pygame.sprite.collide_circle(sprite_1,sprite_2) if result: print "Collision occurred"

这个函数也有一个变体:pygame.sprite.collide_circle_ratio()。函数的功能和用法和上面的pygame.sprite.collide_rect_ratio()是类似的。

3.两个精灵之间的像素遮罩检测

如果矩形检测和圆形检测都不能满足我们的需求怎么办?别担心,pygame还为我们提供了一个更加精确的检测:pygame.sprite.collide_mask()。

这个函数接收两个精灵作为参数,返回值是一个bool变量。

 if pygame.sprite.collide_mask(sprite_1,sprite_2): print ("Collision occurred")

4.精灵和组之间的矩形冲突检测

pygame.sprite.spritecollide(sprite,sprite_group,bool)。调用这个函数的时候,一个组中的所有精灵都会逐个地对另外一个单个精灵进行冲突检测,发生冲突的精灵会作为一个列表返回。

这个函数的第一个参数就是单个精灵,第二个参数是精灵组,第三个参数是一个bool值,最后这个参数起了很大的作用。当为True的时候,会删除组中所有冲突的精灵,False的时候不会删除冲突的精灵

 list_collide = pygame.sprite.spritecollide(sprite,sprite_group,False);

另外这个函数也有一个变体:pygame.sprite.spritecollideany()。这个函数在判断精灵组和单个精灵冲突的时候,会返回一个bool值。

5.精灵组之间的矩形冲突检测

pygame.sprite.groupcollide()。利用这个函数可以检测两个组之间的冲突,他返回一个字典。(键-值对)

好了大概常用的几种冲突检测函数我们已经了解完了,下面我们做一个小小的实例实际运用一下上面学到的知识。

二、冲突检测实例---吃苹果小游戏

先看一下效果图:

游戏开始会在屏幕上随机生成一些苹果,玩家通过上下左右方向键来控制人物去吃苹果。

吃到一个苹果,能量条就会增长一些,直到吃完所有的苹果,游戏结束。

全部的源代码

 import itertools, sys, time, random, math, pygame from pygame.locals import * from MyLibrary import * ''' 想要学习Python?Python学习交流群:1004391443满足你的需求,资料都已经上传群文件,可以自行下载! ''' def calc_velocity(direction, vel=1.0): velocity = Point(0,0) if direction == 0: #上 velocity.y = -vel elif direction == 2: #右 velocity.x = vel elif direction == 4: #下 velocity.y = vel elif direction == 6: #左 velocity.x = -vel return velocity pygame.init() screen = pygame.display.set_mode((800,600)) pygame.display.set_caption("吃苹果") font = pygame.font.Font(None, 36) timer = pygame.time.Clock() #创建精灵组 player_group = pygame.sprite.Group() food_group = pygame.sprite.Group() #初始化玩家精灵组 player = MySprite() player.load("farmer walk.png", 96, 96, 8) player.position = 80, 80 player.direction = 4 player_group.add(player) #初始化food精灵组 for n in range(1,50): food = MySprite(); food.load("food_low.png", 35, 35, 1) food.position = random.randint(0,780),random.randint(0,580) food_group.add(food) game_over = False player_moving = False player_health = 0 while True: timer.tick(30) ticks = pygame.time.get_ticks() for event in pygame.event.get(): if event.type == QUIT: pygame.quit() sys.exit() keys = pygame.key.get_pressed() if keys[K_ESCAPE]: sys.exit() elif keys[K_UP] or keys[K_w]: player.direction = 0 player_moving = True elif keys[K_RIGHT] or keys[K_d]: player.direction = 2 player_moving = True elif keys[K_DOWN] or keys[K_s]: player.direction = 4 player_moving = True elif keys[K_LEFT] or keys[K_a]: player.direction = 6 player_moving = True else: player_moving = False if not game_over: #根据角色的不同方向,使用不同的动画帧 player.first_frame = player.direction * player.columns player.last_frame = player.first_frame + player.columns-1 if player.frame < player.first_frame: player.frame = player.first_frame if not player_moving: #当停止按键(即人物停止移动的时候),停止更新动画帧 player.frame = player.first_frame = player.last_frame else: player.velocity = calc_velocity(player.direction, 1.5) player.velocity.x *= 1.5 player.velocity.y *= 1.5 #更新玩家精灵组 player_group.update(ticks, 50) #移动玩家 if player_moving: player.X += player.velocity.x player.Y += player.velocity.y if player.X < 0: player.X = 0 elif player.X > 700: player.X = 700 if player.Y < 0: player.Y = 0 elif player.Y > 500: player.Y = 500 #检测玩家是否与食物冲突,是否吃到果实 attacker = None attacker = pygame.sprite.spritecollideany(player, food_group) if attacker != None: if pygame.sprite.collide_circle_ratio(0.65)(player,attacker): player_health +=2; food_group.remove(attacker); if player_health > 100: player_health = 100 #更新食物精灵组 food_group.update(ticks, 50) if len(food_group) == 0: game_over = True #清屏 screen.fill((50,50,100)) #绘制精灵 food_group.draw(screen) player_group.draw(screen) #绘制玩家血量条 pygame.draw.rect(screen, (50,150,50,180), Rect(300,570,player_health*2,25)) pygame.draw.rect(screen, (100,200,100,180), Rect(300,570,200,25), 2) if game_over: print_text(font, 300, 100, "G A M E O V E R") pygame.display.update()