Python进阶篇十一Pygame的精灵和碰撞检测

Posted deepboat

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python进阶篇十一Pygame的精灵和碰撞检测相关的知识,希望对你有一定的参考价值。

十一、Pygame的精灵和碰撞检测

精灵(英文译为 Sprite),其实在一个游戏程序中,精灵本质指的是一张张小尺寸的图片,比如游戏中的各种道具、人物、场景装饰等,它们都可以看做成一张张小的“精灵”图。

精灵有个特点就是允许精灵之间进行交互,也称之为碰撞,而碰撞检测,指的就是检测两个精灵之间是否发生了碰撞。比如在贪吃蛇游戏中蛇的头部是否与食物发生了碰撞,或者飞机大战游戏中子弹是否击中了外星人等等。当检测到碰撞发生后,接下来会触发某些事件,比如子弹击中外星人,外星人就会消失,玩家的得分也会随之增加,并且在游戏屏幕上又会出现一个外星人。

Pygame 专门提供了一个处理精灵的模块,也就是 sprite(pygame.sprite)模块。通常情况下,我们使用该模块的基类 Sprite 来创建一个子类,从而达到处理精灵的目的,该子类提供了操作精灵的常用属性和方法,如下所示:

名称说明
self.image加载要显示的精灵图片,控制图片大小和填充色
self.rect精灵图片显示在哪个位置
Sprite.update()刷新精灵图,使其相应效果生效
Sprite.add()添加精灵图到精灵组中(groups)
Sprite.remove()从精灵组中删除选中的精灵图
Sprite.kill()删除精灵组中全部的精灵
Sprite.alive()判断某个精灵是否属于精灵组

注意,当游戏中有大量的精灵时,操作它们将变得复杂,此时通过构建精灵容器(group 类)也就是精灵组来统一管理这些精灵。构建方法如下:

# 创建精灵组
group = pygame.sprite.Group()
# 向组内添加一个精灵
group.add(sprite_one)

pygame.sprite模块也提供了多种检测精灵是否碰撞的方法,如下所示:

名称说明
pygame.sprite.collide_rect() 两个精灵之间的矩形检测,即矩形区域是否有交汇,返回一个布尔值。
pygame.sprite.collide_circle()两个精灵之间的圆形检测,即圆形区域是否有交汇,返回一个布尔值。
pygame.sprite.collide_mask() 两个精灵之间的像素蒙版检测,更为精准的一种检测方式。
pygame.sprite.spritecollide() 精灵和精灵组之间的矩形碰撞检测,一个组内的所有精灵会逐一地对另外一个单个精灵进行碰撞检测,返回值是一个列表,包含了发生碰撞的所有精灵。
pygame.sprite.spritecollideany()精灵和精灵组之间的矩形碰撞检测,上述函数的变体,当发生碰撞时,返回组内的一个精灵,无碰撞发生时,返回 None。
pygame.sprite.groupcollide()检测在两个组之间发生碰撞的所有精灵,它返回值是一个字典,将第一组中发生碰撞的精灵作为键,第二个组中发生碰撞的精灵作为值。

下面看一组简单的示例,代码如下所示:

import pygame


class Snake(pygame.sprite.Sprite):
    # 定义构造函数
    def __init__(self, filename, location):
        # 调父类来初始化子类
        pygame.sprite.Sprite.__init__(self)
        # 加载图片
        self.image = pygame.image.load(filename)
        # 获取图片rect区域
        self.rect = self.image.get_rect()
        # 设置位置
        self.rect.topleft = location


# 初始化pygame
pygame.init()
screen = pygame.display.set_mode((500, 400))
pygame.display.set_caption('Python自学网')
# 填充为白色屏幕
screen.fill((255, 255, 255))
filename = "./python.png"
location = (100, 150)
snake1 = Snake(filename, location)
# 碰撞检测,必须有两个精灵,因此再创建一个精灵,并使用location来控制第二个精灵的位置
location_2 = (100, 80)
snake2 = Snake('./hudie.jpg', location_2)
# 调用 collide_rect()进行矩形区域检测,返回一个布尔值,碰撞返回True,否则返回False
crash_result = pygame.sprite.collide_rect(snake1, snake2)
if crash_result:
    print("精灵碰撞了!")
    pass
else:
    print('精灵没碰撞')
while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            exit()
    # 绘制精灵到屏幕上
    screen.blit(snake1.image, snake1.rect)
    screen.blit(snake2.image, snake2.rect)
    # 刷新显示屏幕
    pygame.display.update()

除上述内容外,Pygame 还提供许多其他模块,比如 mixer(声音)、movie(播放视频)、music(播放音频)、sndarray(操作声音数据)等模块,由于这些模块使用起来较为简单,因此这里不再逐一介绍,感兴趣的朋友可以阅读 Pygame 官方文档 —>点击前往

pygame之精灵碰撞检测

1、创建精灵和精灵组并检测碰撞

import random

import pygame
from pygame.locals import *


class Player(pygame.sprite.Sprite):  # 继承pygame.sprite.Sprite精灵对象
    def __init__(self):
        super().__init__()
        self.image = pygame.Surface((50, 50))  # 创建Surface平面对象
        self.image.fill(\'orange\')  # 填充颜色
        self.rect = self.image.get_rect()  # 获取X,Y坐标和大小
        print(f\'rect:{self.rect}\')  # 不设置默认是0,0
        self.rect.x = 20  # 重新设置X轴坐标
        self.rect.y = 100  # 重新设置Y轴坐标

    def update(self, pressed_keys, *args, **kwargs):
        if pressed_keys[K_UP]:  # 如果按下的是方向上键
            self.rect.move_ip(0, -5)  # 更新rect的X,Y
        if pressed_keys[K_DOWN]:  # 如果按下的是方向下键
            self.rect.move_ip(0, 5)  # 更新rect的X,Y
        if pressed_keys[K_LEFT]:  # 如果按下的是方向左键
            self.rect.move_ip(-5, 0)  # 更新rect的X,Y
        if pressed_keys[K_RIGHT]:  # 如果按下的是方向右键
            self.rect.move_ip(5, 0)  # 更新rect的X,Y
        self.stay_inside()  # 保证一直在窗口里面

    def stay_inside(self):  # 保证一直在窗口里面
        if self.rect.top < 0:  # 如果上边界小于0
            self.rect.top = 0  # 设置上边界为0
        if self.rect.bottom > screen.get_height():  # 如果下边界超过窗口高度
            self.rect.bottom = screen.get_height()  # 设置下边界为窗口高度
        if self.rect.left < 0:  # 如果左边界小于0
            self.rect.left = 0  # 设置左边界为0
        if self.rect.right > screen.get_width():  # 如果右边界超过窗口宽度
            self.rect.right = screen.get_width()  # 设置右边界为窗口宽度


class Enemy(pygame.sprite.Sprite):  # 继承pygame.sprite.Sprite精灵对象
    def __init__(self):
        super().__init__()
        self.image = pygame.Surface((50, 50))  # 创建Surface平面对象
        self.image.fill(\'blue\')  # 填充颜色
        self.rect = self.image.get_rect()  # 获取X,Y坐标和大小
        self.rect.centerx = screen.get_width() + 20  # 设置中心X轴坐标,设置在窗口最右边距离20
        self.rect.centery = random.randint(0, screen.get_height())  # 设置中心Y轴坐标,随机坐标
        self.speed = random.randint(1, 5)  # 设置速度随机

    def update(self, *args, **kwargs):
        self.rect.move_ip(-self.speed, 0)  # 一直向左跑,速度随机
        if self.rect.right < 0:  # 如果跑到头
            self.kill()  # 从精灵组移除自己


ADDENEMY = USEREVENT + 1  # 自定义事件的值,必须大于USEREVENT,因为在USEREVENT之下的数值都被pygame用完了
pygame.time.set_timer(ADDENEMY, 500)  # 定时器,用于生成敌人,负责把事件传入PyGame的事件队列中
pygame.init()  # 初始化pygame
screen = pygame.display.set_mode((300, 300))  # 创建窗口
player = Player()  # 创建Player对象
running = True
clock = pygame.time.Clock()  # 创建Clock用于跟踪时间
enemies = pygame.sprite.Group()  # 定义精灵组,用于存放enemy
all_sprite = pygame.sprite.Group()  # 定义精灵组,用于存放所有精灵
all_sprite.add(player)  # 添加player到精灵组

while running:
    for event in pygame.event.get():  # 获取所有事件
        if event.type == KEYDOWN:  # 获取按钮按下事件
            if event.key == K_ESCAPE:  # 如果是按下了ESC键
                running = False
        if event.type == QUIT:  # 如果是QUIT事件,如点击关闭窗口按钮
            running = False
        if event.type == ADDENEMY:  # 监控自定义事件
            enemy = Enemy()  # 创建敌人
            enemies.add(enemy)  # 添加到enemies精灵组
            all_sprite.add(enemy)  # 添加到all_sprite精灵组
    clock.tick(100)  # 每秒刷新多少帧,不设置按键后移动的飞快
    pressed_key = pygame.key.get_pressed()  # 获取按键
    screen.fill(color=\'white\')  # 为窗口填充白色
    all_sprite.update(pressed_key)  # 更新组中的精灵位置
    all_sprite.draw(screen)  # 显示整个精灵组中的精灵

    # 碰撞检测,检测player和enemies精灵组中精灵是否碰撞
    collide_sprite = pygame.sprite.spritecollideany(player, enemies)
    if collide_sprite:  # 如果碰撞,返回精灵组中的碰撞精灵
        collide_sprite.kill()  # 从精灵组移除自己
    pygame.display.update()  # 刷新整个平面 或者 pygame.display.flip()
pygame.quit()  # 退出pygame

以上是关于Python进阶篇十一Pygame的精灵和碰撞检测的主要内容,如果未能解决你的问题,请参考以下文章

如何检测pygame中的碰撞?

Python-项目实战--飞机大战-碰撞检测

Python碰撞使用pygame检测图像(图像类型:png)

浅谈pygame.sprite的精灵碰撞

pygame碰撞检测函数python3中的语法错误[重复]

python3.5 的flappy bird pygame编的