pygame 飞机大战碰撞检查的运用(三)用sprite,实现完美碰撞检查

目标:实现完美的碰撞检查

前面的只是普通的碰撞检查,用到了矩形框的范围。图像如果都不是矩形,用普通检查,两种已经相碰,但画面显示还未相碰,这就很尴尬了。

sprite模块中,有个collide_mask()函数可以利用。

ygame.sprite.collide_mask()

Collision detection between two sprites, using masks.

collide_mask(SpriteLeft, SpriteRight) -> point

Returns first point on the mask where the masks collided, or None if there was no collision.

Tests for collision between two sprites, by testing if their bitmasks overlap. If the sprites have a "mask" attribute, that is used as the mask, otherwise a mask is created from the sprite image. Intended to be passed as a collided callback function to the *collide functions. Sprites must have a "rect" and an optional "mask" attribute.

You should consider creating a mask for your sprite at load time if you are going to check collisions many times. This will increase the performance, otherwise this can be an expensive function because it will create the masks each time you check for collisions.

sprite.mask = pygame.mask.from_surface(sprite.image)

简单的翻译下,被检查对象要有mask属性,用于检查的范围。pygame还有专门的mask模块, from_surface()可以将Surface对象中的非透明部分表示为mask并返回。

在对象中定义

self.mask = pygame.mask.from_surface(self.image)  #类要继承至sprite

碰撞检查的函数改为:
beshoted = pygame.sprite.spritecollide(hero,bullets,True,pygame.sprite.collide_mask)

hero 和 bullets必须都是精灵类。

hero.py

import pygame


class Hero(pygame.sprite.Sprite):
    def __init__(self, scene):
        pygame.sprite.Sprite.__init__(self)
        self.main_scene = scene
        self.image = pygame.image.load('images/hero.png')
        self.rect = self.image.get_rect()
        self.x_speed = 1
        self.y_speed = 1
        self.mask = pygame.mask.from_surface(self.image)
        
    def update(self):
        self.rect.x  += self.x_speed
        self.rect.y += self.y_speed
    def set_pos(self,x, y):
        self.rect.x = x
        self.rect.y = y
       

bullet.py

import pygame
import math
import random 

class Bullet(pygame.sprite.Sprite):
    def __init__(self,scene):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.image.load('images/bullet0.png')
        self.rect = self.image.get_rect()
        self.x_speed = 0.00
        self.y_speed = 2.00
        self.angle = 0
        self.mask = pygame.mask.from_surface(self.image)
        self.main_scent = scene

    #会自动调用
    def update(self):
        self.rect.x += self.x_speed
        self.rect.y += self.y_speed
        # 可以用传递 scene来计算屏幕大小,演示用就简单些吧。
        # if (self.rect.x < 0 or self.rect.y < 0 or
        #     self.rect.x >480 or self.rect.y > 860):
        # 用一条语句实现了跑出屏幕的检查,我真是佩服我自己
        if not self.rect.colliderect(self.main_scent.get_rect()):
            print('the bullet is out of screen')
            #超过屏幕自动删除
            self.kill()

    #为了和飞机的位置配合,需要设置初始位置
    def set_pos(self,x, y):
        self.rect.x = x
        self.rect.y = y 
    
    # 为了跟踪弹用的
    def get_pos(self):
        return (self.rect.x,self.rect.y)

    def set_speed(self,speed, angle = 0):
        self.angle = math.pi * angle/180
        self.x_speed = speed * math.cos(self.angle)
        self.y_speed = speed * math.sin(self.angle)

main.py

import pygame
from sys import exit
from bullet import *
from hero import *

#定义屏幕类
class MainScene(object):
    #初始化
    def __init__(self):
        pygame.init()
        self.size = (480,860)
        self.scene = pygame.display.set_mode(self.size)
        # self.scene.size = self.size
        pygame.display.set_caption("自学飞机大战--V0.98")
        self.image = pygame.image.load('./images/bg.png')
        self.quit_image = pygame.image.load('./images/quit.png')
        self.restart_image = pygame.image.load('./images/restart.png')
        self.quit_rect = self.quit_image.get_rect()
        self.restart_rect = self.restart_image.get_rect()
        bullet = Bullet(self.scene)
        bullet.set_speed(5,45)
        self.bullet_sprite = pygame.sprite.Group()
        self.bullet_sprite.add(bullet)
        self.hero = Hero(self.scene)
        self.hero.set_pos(100,100)
        self.hero_sprite = pygame.sprite.Group()
        self.hero_sprite.add(self.hero)

    #绘制各个元素
    def drawElements(self):
        self.scene.blit(self.image,(0,0))
        
        self.restart_rect.left, self.restart_rect.top = 150,150
        self.scene.blit(self.restart_image,self.restart_rect)
        self.quit_rect.left, self.quit_rect.top = 150, 300
        self.scene.blit(self.quit_image,self.quit_rect)
    
    def collideEvent(self):
#第三个参数为 True,碰撞后消失,False,不消失。
        shoted = pygame.sprite.spritecollide(self.hero,self.bullet_sprite,True,pygame.sprite.collide_mask)
        if shoted:
            print('hero is shoted by bullets')

        for bullet in self.bullet_sprite:
            #用rect的函数来检查
            if bullet.rect.colliderect(self.quit_rect):
                print('collide quit image')
            # 用pygame的函数来检查,两种方法都可以。
            if pygame.Rect.colliderect(bullet.rect, self.restart_rect):
                print('collide restart image')

    #处理键盘和鼠标相应
    def handleEvent(self):
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                exit()
            if event.type == pygame.MOUSEBUTTONDOWN:
                 # 获得鼠标点击三个按钮的点击情况(1,0,0)
                # 如果第一个参数为1,表示左键被按下
                # 如果第二个参数为1,表示滚轮被按下
                # 如果第三个参数为1,表示右键被按下
                buttons = pygame.mouse.get_pressed()
                # 我们只处理左键被按下的情况
                if buttons[0]:
                    
                    # 注释的代码为跟踪弹的实现方式
                    # 获得拖动鼠标的拖动位置
                    pos_mouse = pygame.mouse.get_pos()

                    # print (self.quit_rect,pos_mouse)
                    if self.quit_rect.collidepoint(pos_mouse):
                        print('quit')
                    if self.restart_rect.collidepoint(pos_mouse):
                        print('restart')
                    # if (self.quit_rect.left < pos_mouse[0] < self.quit_rect.right
                    #     and self.quit_rect.top < pos_mouse[1] < self.quit_rect.bottom):
                    #     print ("quit")
    #运行主程序
    def runScene(self):
        while True:
            self.drawElements()
            self.handleEvent()
            self.collideEvent()
            self.hero_sprite.draw(self.scene)
            self.hero_sprite.update()
            self.bullet_sprite.draw(self.scene)
            self.bullet_sprite.update()
            pygame.display.update()
            # pygame.time.delay(100)



if __name__ == '__main__':
    mainScene = MainScene()
    mainScene.runScene()

可以看到,子弹和飞机真正相碰的时候,才判断到相撞,然后子弹消失,下一个图像没碰撞到。

猜你喜欢

转载自blog.csdn.net/hailler1119/article/details/88619307