Python pygame(GUI编程)模块最完整教程(4)

这篇具有很好参考价值的文章主要介绍了Python pygame(GUI编程)模块最完整教程(4)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

上一篇文章:

Python pygame(GUI编程)模块最完整教程(3)_pygame教程pdf_Python-ZZY的博客-CSDN博客

总目录:

README.md · Python-ZZY/Python-Pygame最完整教程 - Gitee.com

11 图形

参考资料:pygame.draw — pygame-ce v2.4.0 documentation

draw模块提供了一些直接在表面上绘制常用图形的操作,如绘制矩形、圆形、多边形、椭圆、弧形等。

11.1 绘制矩形或圆角矩形

pg.draw.rect方法用于绘制矩形。绘制成功后将返回一个Rect对象表示绘制时在表面上改变的像素的矩形对象。


pg.draw.rect(surface, color, rect) -> Rect
pg.draw.rect(surface, color, rect, width=0, border_radius=0, border_top_left_radius=-1, border_top_right_radius=-1, border_bottom_left_radius=-1, border_bottom_right_radius=-1) -> Rect

pg.draw.rect方法中,必需的参数是surface, color, rect,表示进行矩形绘制的表面,矩形的颜色,矩形的位置。如果指定width参数,那么不会填充矩形除边框外的内部,并将该矩形边框的宽设为width。


import pygame as pg 
from pygame.locals import *

pg.init()
screen = pg.display.set_mode((440, 200), pg.RESIZABLE)

image = pg.Surface((200, 200))
pg.draw.rect(image, (255, 0, 0), (0, 0, 50, 100), width=2) #红色;左上角位于(0,0)且宽高分别为(50,100);边框宽2px

while True:
    screen.fill((0, 0, 0))
    screen.blit(image, (0, 0)) #在(0,0)位置绘制
    
    for event in pg.event.get():
        if event.type == QUIT:
            pg.quit()

    pg.display.flip()
pygame绘制圆角矩形,Python pygame(GUI编程)模块教程,pygame,python,Powered by 金山文档

如果希望绘制圆角矩形,可以设置border_radius等几个参数。border_radius表示矩形的圆角的半径大小。另外几个参数则表示在不同的位置矩形圆角的半径大小。


pg.draw.rect(image, (255, 0, 0), (0, 0, 50, 100), width=2,
             border_radius=10)
pygame绘制圆角矩形,Python pygame(GUI编程)模块教程,pygame,python,Powered by 金山文档

11.2 绘制圆形或半圆形

pg.draw.circle用于绘制圆形。


pg.draw.circle(surface, color, center, radius) -> Rect
pg.draw.circle(surface, color, center, radius, width=0, draw_top_right=None, draw_top_left=None, draw_bottom_left=None, draw_bottom_right=None) -> Rect

surface, color分别表示绘制的表面和颜色,center表示圆心的位置,radius表示半径大小,width表示边框宽度。

例如:


pg.draw.circle(image, (255, 0, 0), (50, 50), 30, width=2)
pygame绘制圆角矩形,Python pygame(GUI编程)模块教程,pygame,python,Powered by 金山文档

剩余的几个参数表示只绘制圆的一部分。如


pg.draw.circle(image, (255, 0, 0), (50, 50), 30, width=2,
               draw_top_right=True, draw_bottom_left=True)
pygame绘制圆角矩形,Python pygame(GUI编程)模块教程,pygame,python,Powered by 金山文档

运行后会发现只绘制了圆的右上和左下部分。如果不设定width=2,效果如下:

pygame绘制圆角矩形,Python pygame(GUI编程)模块教程,pygame,python,Powered by 金山文档

11.3 绘制多边形

pg.draw.polygon用于绘制多边形。


pg.draw.polygon(surface, color, points) -> Rect
pg.draw.polygon(surface, color, points, width=0) -> Rect

surface, color, width表示绘制的表面,绘制的颜色,以及边框的宽度。必需的参数points是一个列表,每个项目都是一个位置点。实际绘制时将points中的所有点连接形成一个多边形。


pg.draw.polygon(image, (255, 0, 0), [(0, 0), (0, 50), (50, 50)], width=2)
pygame绘制圆角矩形,Python pygame(GUI编程)模块教程,pygame,python,Powered by 金山文档

11.4 draw模块索引-图形绘制

rect(surface, color, rect) -> Rect

rect(surface, color, rect, width=0, border_radius=0, border_top_left_radius=-1, border_top_right_radius=-1, border_bottom_left_radius=-1, border_bottom_right_radius=-1) -> Rect

绘制矩形或圆角矩形。

polygon(surface, color, points) -> Rect

polygon(surface, color, points, width=0) -> Rect

绘制多边形。

circle(surface, color, center, radius) -> Rect

circle(surface, color, center, radius, width=0, draw_top_right=None, draw_top_left=None, draw_bottom_left=None, draw_bottom_right=None) -> Rect

绘制圆形或半圆形。

ellipse(surface, color, rect) -> Rect

ellipse(surface, color, rect, width=0) -> Rect

通过外接的矩形rect绘制椭圆形。

arc(surface, color, rect, start_angle, stop_angle) -> Rect

arc(surface, color, rect, start_angle, stop_angle, width=1) -> Rect

绘制弧形。rect表示这个弧形的轮廓将会沿着外接rect的椭圆形。start_angle表示绘制的起始角度,以弧度为单位(注意:不是角度!);stop_angle表示绘制的结束角度,也以弧度为单位。绘制的弧形将由start_angle一直到stop_angle。示例如下:


pg.draw.ellipse(image, (255, 255, 255), rect, width=2) #绘制白色的椭圆
pg.draw.arc(image, (255, 0, 0), rect, 0, math.pi/3, width=4) #绘制弧形
pygame绘制圆角矩形,Python pygame(GUI编程)模块教程,pygame,python,Powered by 金山文档

line(surface, color, start_pos, end_pos) -> Rect

line(surface, color, start_pos, end_pos, width=1) -> Rect

绘制线段。start_pos和end_pos分别是线段的起始点和结束点。

lines(surface, color, closed, points) -> Rect

lines(surface, color, closed, points, width=1) -> Rect

绘制多条线段。在给出的points列表中,将多个点用线段首尾相连。如果设置closed=True,则在第一个点和最后一个点之间额外绘制一条线段,形成一个多边形的形状。

aaline(surface, color, start_pos, end_pos) -> Rect

绘制具有抗锯齿效果的线段。

aalines(surface, color, closed, points) -> Rect

绘制多条具有抗锯齿效果的线段。

12 精灵与碰撞检测

参考资料:pygame.sprite — pygame-ce v2.4.0 documentation

12.1 精灵、精灵组

pygame提供了sprite模块,支持对精灵进行操作。精灵对象可以理解为一个同时具有表面和位置属性的对象,并且还支持一系列方法进行管理。

在游戏制作中,为了简化游戏代码,通常会使用精灵。比如制作一个跑酷游戏,要把游戏主角绘制到屏幕上,必须知道这个主角的图片和绘制的位置。如果设置很多个变量,比如player_surf, player_rect,整个代码就会非常烦琐。这时最好的办法是将这样一个对象封装到一个类中,自己构造一个类当然没问题,但最好的方式还是使用pygame自带的精灵类。

pg.sprite.Sprite创建一个精灵对象。但我们通常不会直接调用这个方法,而是把它作为一个基类来继承。标准的精灵对象应包含一个image属性和rect属性,分别表示这个精灵的表面和位置。下面的代码展示了一个最简单的精灵对象。


class Player(pg.sprite.Sprite):
    def __init__(self):
        super().__init__() #初始化精灵基类

        self.image = pg.image.load("player.png")
        self.rect = self.image.get_rect(center=screen.get_rect().center)

如果需要的精灵比较多,如在游戏中添加敌人,就需要非常多的敌人精灵,管理起来十分不便。这时最好的办法是将精灵进行分类,所有的敌人精灵都放入一个类似列表的结构,然后遍历列表对敌人精灵进行刷新操作。这个类似于列表的结构在pygame中同样支持,它被称作一个精灵组对象,通过pg.sprite.Group方法创建,示例如下。精灵组对象有一系列方法,可以用于绘制、刷新、添加、删除精灵等。


enemy_group = pg.sprite.Group()

创建精灵组后,首先需要了解添加精灵组的方法add()。add方法用于将实例化的精灵对象添加到组中。


enemy_group.add(pg.sprite.Sprite())

draw()方法用于把当前组中的精灵绘制到某个表面上。这个方法需要在主循环中持续调用,需要传递一个参数表示绘制的表面。这个方法会遍历所有的精灵并按照它们的位置绘制。示例如下:


import pygame as pg 
from pygame.locals import *
from random import randint

pg.init()
screen = pg.display.set_mode((440, 200))

class Enemy(pg.sprite.Sprite):
    def __init__(self):
        super().__init__()

        self.image = pg.image.load("enemy.png")
        self.rect = self.image.get_rect(center=(randint(0, 200), randint(0, 200)))

enemy_group = pg.sprite.Group()
pg.time.set_timer(USEREVENT, 1000)

while True:
    screen.fill((0, 0, 0))
    enemy_group.draw(screen) #在屏幕上绘制
    
    for event in pg.event.get():
        if event.type == QUIT:
            pg.quit()
        elif event.type == USEREVENT:
            enemy_group.add(Enemy()) #添加一个新的敌人

    pg.display.flip()

运行后,每过一秒就会随机添加一位敌人。

pygame绘制圆角矩形,Python pygame(GUI编程)模块教程,pygame,python,Powered by 金山文档

精灵组还提供了一个update()方法,用于刷新每个精灵。这里的刷新指的是调用每个精灵对象的update()方法。精灵的update()方法默认是没有效果的,如果想要改变update()方法,可以在精灵类的地方覆盖。示例如下:


...

class Enemy(pg.sprite.Sprite):
    def __init__(self):
        ...

    def update(self):
        self.image.set_alpha(randint(0, 255))
        
...

while True:
    screen.fill((0, 0, 0))
    enemy_group.draw(screen) #在屏幕上绘制
    enemy_group.update()
    
    ...

运行后,敌人在屏幕上随机闪动。

pygame绘制圆角矩形,Python pygame(GUI编程)模块教程,pygame,python,Powered by 金山文档

精灵对象还提供了一个kill方法,用于杀死当前精灵。每次调用精灵组的update时,都会先检测当前精灵是否已经被杀死,如果被杀死将会把它自动移出精灵组。


...

class Enemy(pg.sprite.Sprite):
    def __init__(self):
        super().__init__()

        self.image = pg.image.load("enemy.png")
        self.rect = self.image.get_rect(center=(randint(0, 200), randint(0, 200)))
        self.kill_time = pg.time.get_ticks() + 2000 #2000ms后销毁
        
    def update(self):
        if pg.time.get_ticks() > self.kill_time:
            self.kill() #杀死精灵
            
enemy_group = pg.sprite.Group()
pg.time.set_timer(USEREVENT, 1000)

while True:
    screen.fill((0, 0, 0))
    enemy_group.draw(screen) #在屏幕上绘制
    enemy_group.update()
    
    for event in pg.event.get():
        if event.type == QUIT:
            pg.quit()
        elif event.type == USEREVENT:
            enemy_group.add(Enemy()) #添加一个新的敌人

    pg.display.flip()

12.2 精灵、精灵组的碰撞检测

碰撞检测指的是两个游戏对象的位置是否有重叠的部分。pygame提供了一系列功能来支持碰撞检测。比如检测玩家的子弹是否击中敌人,就需要用碰撞检测的技术。

pg.sprite模块中有一些用于碰撞检测的函数,较为常用的是groupcollide和spritecollide两个函数。


pg.sprite.groupcollide(group1, group2, dokill1, dokill2, collided = None) -> Sprite_dict

groupcollide方法表示两个组之间进行碰撞。group1和group2分别是需要检测的两个精灵组对象。当碰撞发生时,还有dokill1和dokill2两个参数,表示是否检测到碰撞后杀死第一个组中发生碰撞的精灵和第二个组中发生碰撞的精灵。

collided是一个可选的回调函数,可以用于判定碰撞是否发生,必须包含两个参数分别代表两个碰撞的精灵,返回一个布尔值表示碰撞的结果。

这个函数会返回一个字典,key为group1中发生碰撞的对象,value为一个列表,包含group2中所有与group1发生碰撞的对象。

下面的示例中,会随机添加子弹和敌人。如果子弹与敌人发生碰撞,敌人将被删除(dokill2=True)。


import pygame as pg 
from pygame.locals import *
from random import randint

pg.init()
screen = pg.display.set_mode((440, 200))

class Bullet(pg.sprite.Sprite):
    def __init__(self):
        super().__init__()

        self.image = pg.image.load("bullet.png")
        self.rect = self.image.get_rect(center=(randint(0, 200), randint(0, 200)))

class Enemy(pg.sprite.Sprite):
    def __init__(self):
        super().__init__()

        self.image = pg.image.load("enemy.png")
        self.rect = self.image.get_rect(center=(randint(0, 200), randint(0, 200)))

bullet_group = pg.sprite.Group()            
enemy_group = pg.sprite.Group()
pg.time.set_timer(USEREVENT, 1000)

while True:
    screen.fill((0, 0, 0))
    enemy_group.draw(screen)
    bullet_group.draw(screen)

    pg.sprite.groupcollide(bullet_group, enemy_group, False, True)
    
    for event in pg.event.get():
        if event.type == QUIT:
            pg.quit()
        elif event.type == USEREVENT:
            bullet_group.add(Bullet())
            enemy_group.add(Enemy())

    pg.display.flip()
pygame绘制圆角矩形,Python pygame(GUI编程)模块教程,pygame,python,Powered by 金山文档

子弹(叉形)与敌人(骷髅)发生碰撞后骷髅会被删除

前面已经介绍过了精灵的kill方法。如果不设置dokill1和dokill2,上面那段碰撞的代码还可以用以下方式,实现在碰撞时删除敌人的效果。


col = pg.sprite.groupcollide(bullet_group, enemy_group, False, False)
    for enemys in col.values():
        for enemy in enemys:
            enemy.kill()

spritecollide方法用于检测单个精灵和一个精灵组的碰撞。


spritecollide(sprite, group, dokill, collided = None) -> Sprite_list

sprite表示精灵,group表示精灵组,dokill表示是否检测到碰撞时移除group中的精灵。这个方法返回一个列表,其中包含与sprite发生碰撞的精灵。

12.3 sprite模块索引-管理精灵对象

Sprite(*groups) -> Sprite

创建一个精灵对象,指定groups可以让精灵被添加到其他精灵组中。

Sprite.update(*args, **kwargs) -> None

当精灵组调用update方法时,会依次调用组中精灵的update方法。

Sprite.add(*groups) -> None

将精灵添加到组中。

Sprite.remove(*groups) -> None

将精灵从组中移除。

Sprite.kill() -> None

杀死精灵。在精灵组调用update方法后所有被杀死的精灵将被移除。

Sprite.alive() -> bool

判断精灵是否被添加到一个或多个组中。

Sprite.groups() -> group_list

返回精灵所在的精灵组的列表。

DirtySprite(*groups) -> DirtySprite

创建一个脏精灵对象,比普通的Sprite对象支持更多属性。(脏精灵支持的精灵组是LayeredDirty对象,而不是Group)脏精灵对象支持的方法和Sprite一样,此处不再赘述,下面介绍它的额外属性。

DirtySprite.dirty -> int

表示脏精灵是否为“脏”的。如果设置为0,则调用LayeredDirty(脏精灵组)的时候不会进行绘制。如果设置为1,那么只绘制一次,然后重新设为dirty=0(极容易被fill方法覆盖掉,所以脏精灵并不常用)。如果设置为2,则持续绘制。默认值为1。

DirtySprite.blendmode -> int

绘制脏精灵时,传递给blit方法的special_flags参数。

DirtySprite.source_rect -> None

DirtySprite.source_rect -> Rect

绘制脏精灵时进行的裁剪。

DirtySprite.visible -> int

精灵的可见性。设置为1则不会重新绘制。

DirtySprite.layer -> int

只读属性(可通过LayeredDirty.change_layer设置),表示脏精灵位于精灵组的层数。层数越大,绘制顺序越靠后。层数相同时,后添加的精灵绘制顺序靠后。

Group(*sprites) -> Group

普通精灵组对象,用于管理多个精灵。sprites是需要被添加进来的精灵。Group支持以下python标准操作:

  • in:某个精灵是否在组中

  • len:获取组中精灵的数量

  • bool:组中是否有精灵

  • iter:精灵组可以作为迭代对象,遍历组中所有精灵对象。

Group.sprites() -> sprite_list

返回组中所有精灵的列表。下面两行代码起到同样的效果。


for sprite in group:
for sprite in group.sprites():

Group.copy() -> Group

复制精灵组。

Group.add(*sprites) -> None

将精灵添加到组中。

Group.remove(*sprites) -> None

将精灵从组中删除。

Group.has(*sprites) -> bool

判断精灵是否在组中。下面两行代码起到同样的效果:


group.has(sprite)
sprite in group #注意:in操作符只能用来检测单个精灵是否在组中

Group.update(*args, **kwargs) -> None

按添加顺序调用每个精灵的update方法。如果该精灵已经被杀死但还没有移除,将会被移出去。

注意:绘制精灵的顺序与精灵的堆叠顺序相关。绘制较晚的精灵会覆盖到绘制较早的精灵之上。

Group.draw(Surface) -> List[Rect]

按添加顺序在Surface上绘制组中的精灵。这个方法需要每个精灵提供一个image和rect参数。

Group.clear(Surface_dest, background) -> None

在Surface_dest清除精灵绘制的内容,通过用background填充绘制的精灵位置来清除。

background可以是一个表面对象,通常与Surface_dest大小相同。这样,当调用clear时,会用background中和精灵位置相同的部分绘制到Surface_dest上。

background也可以是一个回调函数,必须带有两个参数,分别表示Surface_dest表面和位置。比如把下面的回调函数传递给clear,将把精灵所在位置填充为红色:


def clear_callback(surf, rect):
    color = 255, 0, 0 #红色
    surf.fill(color, rect) #将surf中rect的位置填充为红色

Group.empty() -> None

清空精灵组。

LayeredUpdates(*sprites, **kwargs) -> LayeredUpdates

和Group类似,但是支持通过图层控制绘制精灵的顺序。

LayeredUpdates.add(*sprites, **kwargs) -> None

添加精灵。如果指定layer关键字参数,将会设置精灵的层数。

LayeredUpdates.sprites() -> sprites

返回组中所有精灵的列表,有图层顺序。

LayeredUpdates.draw(surface) -> Rect_list

按图层顺序在Surface上绘制组中的精灵。这个方法需要每个精灵提供一个image和rect参数。

LayeredUpdates.get_sprites_at(pos) -> colliding_sprites

返回一个列表,其中包含所有与pos位置发生碰撞的精灵。

LayeredUpdates.get_sprite(idx) -> sprite

返回在组中位于指定索引处的精灵

LayeredUpdates.remove_sprites_of_layer(layer_nr) -> sprites

移除位于某个图层的所有精灵,并将它们返回。

LayeredUpdates.layers() -> layers

返回一个列表,包含所有的图层数。

LayeredUpdates.change_layer(sprite, new_layer) -> None

将某个精灵移动到新的图层

LayeredUpdates.get_layer_of_sprite(sprite) -> layer

返回精灵位于的图层数。

LayeredUpdates.get_top_layer() -> layer, get_bottom_layer() -> layer

分别返回层数最高和最低的图层数。

LayeredUpdates.move_to_front(sprite) -> None, move_to_back(sprite) -> None

分别将sprite的图层设为最靠前(层数最高)的图层和最靠后(层数最低)的图层。

LayeredUpdates.get_top_sprite() -> Sprite

返回最顶层的精灵(图层数最高,添加顺序最晚)。

LayeredUpdates.get_sprites_from_layer(layer) -> sprites

返回某个图层的精灵列表。

LayeredUpdates.switch_layer(layer1_nr, layer2_nr) -> None

将layer1_nr图层的精灵切换到layer2_nr图层

LayeredDirty(*sprites, **kwargs) -> LayeredDirty

脏精灵(DirtySprite)组对象。

LayeredDirty.draw(surface, bgd=None) -> Rect_list

将脏精灵进行绘制,bgd类似于Group.clear中的background参数,但是将绘制在精灵的底层。

LayeredDirty.clear(surface, bgd) -> None

用bgd清除目标表面surface。

LayeredDirty.repaint_rect(screen_rect) -> None

在屏幕区域screen_rect中进行重绘精灵。

LayeredDirty.set_clip(screen_rect=None) -> None, get_clip() -> Rect

设置或获取绘制精灵的裁剪区域。

LayeredDirty.change_layer(sprite, new_layer) -> None

改变精灵的图层。

GroupSingle(sprite=None) -> GroupSingle

与Group精灵组类似,但只容纳单个精灵对象。添加一个新的精灵对象后会移除先前添加的精灵。

spritecollide(sprite, group, dokill, collided = None) -> Sprite_list

判断单个精灵对象和精灵组中的对象是否发生碰撞(即发生重合),dokill表示是否在碰撞后杀死group中与sprite发生碰撞的精灵。collided是一个包含两个精灵对象作为参数的、返回一个布尔值指示碰撞结果的回调函数。返回发生碰撞的精灵列表。

collide_rect(left, right) -> bool

判断两个精灵对象是否发生碰撞(left和right是两个包含rect参数的Sprite对象)

collide_rect_ratio(ratio) -> collided_callable

按照ratio比例缩放矩形(如:1.5表示缩放为原来大小的1.5倍)再检测碰撞。返回一个回调函数,可以直接传给碰撞函数的collided参数。

collide_circle(left, right) -> bool

检测两个精灵对象的圆是否发生碰撞。如果精灵对象有一个radius属性,将会以精灵的rect中心为圆心,radius为半径作为用于碰撞检测的圆。如果没有,则用于碰撞检测的圆会足够大,直到能够完全围绕精灵的矩形。

collide_circle_ratio(ratio) -> collided_callable

按照ratio比例缩放圆的半径再检测碰撞,返回用于collided参数的回调函数。

collide_mask(sprite1, sprite2) -> (int, int)

collide_mask(sprite1, sprite2) -> None

通过Mask.overlap实现两个精灵之间精准的碰撞检测。

groupcollide(group1, group2, dokill1, dokill2, collided = None) -> Sprite_dict

组与组之间进行碰撞检测,dokill指定是否杀死该组中发生碰撞的精灵。

spritecollideany(sprite, group, collided = None) -> Sprite or None

与spritecollide类似,但是舍去了部分功能,速度会稍快。

12.4 矩形与矩形的碰撞检测

Rect对象的colliderect方法用于矩形之间的碰撞检测。


pg.Rect.colliderect(Rect) -> bool

如果两个Rect发生碰撞将返回True。

关于Rect方法的更多内容参见后文。

12.5 矩形与点的碰撞检测

Rect对象与某个点的碰撞检测常用于检测鼠标是否点击了按钮所在的位置。


pg.Rect.collidepoint(x, y) -> bool
pg.Rect.collidepoint((x,y)) -> bool

如果矩形与(x, y)的点发生碰撞则返回True。

12.6 矩形与线段的碰撞检测

Rect.clipline方法可以对于矩形和线段进行碰撞检测,并返回碰撞的结果。


pg.Rect.clipline(x1, y1, x2, y2) -> ((cx1, cy1), (cx2, cy2)) or ()
pg.Rect.clipline((x1, y1), (x2, y2)) -> ((cx1, cy1), (cx2, cy2)) or ()
pg.Rect.clipline((x1, y1, x2, y2)) -> ((cx1, cy1), (cx2, cy2)) or ()
pg.Rect.clipline(((x1, y1), (x2, y2))) -> ((cx1, cy1), (cx2, cy2)) or ()

(x1, y1), (x2, y2)表示线段的两个端点的坐标。如果发生了碰撞,则返回该线段位于矩形范围内的部分的两个端点的坐标;否则返回空元组。

12.7 完美碰撞检测

假如有一个游戏角色的图片,带有一个纯色或透明色的背景,而游戏人物的图片是不规则的。如果只进行矩形的碰撞,游戏角色中空白的部分的碰撞也会被检测到。pygame还提供了圆形检测之类的方法,但它们都不适用于不规则图片的碰撞检测。

这时就需要用到Mask.overlap,精准地进行碰撞检测。这个方法将两个掩模对象(Mask)进行碰撞检测。如果两个掩模中设为1的点有重合的地方则表示检测到发生碰撞。

pg.mask.from_surface方法将表面转换成Mask对象,转换时会将不透明的地方设为1,透明的地方设为0。如果表面使用了colorkey(将某个颜色设为透明色),则会将设置了colorkey的颜色所在位置设为0,其余地方设为1。


Mask.overlap(other, offset) -> (x, y)
Mask.overlap(other, offset) -> None

offset表示偏移,也就是other的位置减去Mask对象本身的位置得到的(x, y)。Mask不携带任何位置属性,和Surface一样,所以碰撞检测时需要提供位置。

示例如下:


import pygame as pg 
from pygame.locals import *
from random import randint

def collideMask(mask1, mask2, pos1, pos2): #用于检测Mask碰撞的函数
    return mask1.overlap(mask2, (pos2[0] - pos1[0], pos2[1] - pos1[1]))
                      
pg.init()
screen = pg.display.set_mode((440, 200))

star = pg.image.load("star.png")
star.set_colorkey((255, 255, 255)) #将白色设为透明色,导出Mask的时候会将白色的背景设为0
star_mask = pg.mask.from_surface(star)

cursor = pg.image.load("cursor.png")
cursor_rect = cursor.get_rect()
cursor_mask = pg.mask.from_surface(cursor)

while True:
    screen.fill((0, 0, 0))
    screen.blit(star, (0, 0))
    screen.blit(cursor, cursor_rect)

    cursor_rect.center = pg.mouse.get_pos() #让cursor图片位置跟随鼠标位置

    if collideMask(star_mask, cursor_mask, (0, 0), cursor_rect.topleft):
        pg.display.set_caption("Collide!!") #如果检测到碰撞,则更改标题
    else:
        pg.display.set_caption("No collide")

    for event in pg.event.get():
        if event.type == QUIT:
            pg.quit()

    pg.display.flip()
注意:这里的star.png是一个纯白色背景的图片,上面绘制一个星形。cursor.png是一个纯灰色的矩形图片,尺寸比star.png小。
pygame绘制圆角矩形,Python pygame(GUI编程)模块教程,pygame,python,Powered by 金山文档

运行效果

注意:完美碰撞检测速度必然较慢,如果不必要的话不应该使用完美碰撞检测。

实战:飞机大战游戏

本章是实战环节,读者将综合pygame知识,做出一个真正意义上的游戏。

本游戏使用的素材如下(存放在与主程序同目录的assets文件夹中):

pygame绘制圆角矩形,Python pygame(GUI编程)模块教程,pygame,python,Powered by 金山文档
注:两张图片背景均为透明色。

完整代码


import pygame as pg
from pygame.locals import * #导入所有常量
from functools import lru_cache
from random import randint

@lru_cache() #lru_cache将函数的返回值储存起来,以避免重复加载同一张图片
def load_image(path):
    return pg.image.load(path)

@lru_cache()
def clip_image(surf, rect): #裁剪图片并返回结果
    return surf.subsurface(rect) #subsurface方法获取Surface指定位置的子表面

def load_images(path, count): #将某个表面裁剪成多个表面的列表
    surf = load_image(path)
    w = surf.get_width() / count #每个划分的表面的宽度
    h = surf.get_height()
    
    images = []    
    for i in range(count):
        subsurf = clip_image(surf, (i * w, 0, w, h))
        images.append(subsurf)

    return images
    
def collideMask(mask1, mask2, pos1, pos2): #用于检测Mask碰撞的函数
    return mask1.overlap(mask2, (pos2[0] - pos1[0], pos2[1] - pos1[1]))

class Player(pg.sprite.Sprite): #玩家
    def __init__(self, *group):
        super().__init__(*group) #将精灵加入到group精灵组中

        self.image = load_image("assets/player.png")
        self.rect = self.image.get_rect(midbottom=screen.get_rect().midbottom) #飞机位于窗口底部
        self.mask = pg.mask.from_surface(self.image) #掩模对象,用于精准碰撞检测
        
    def update(self):
        for enemy in enemy_group: #遍历敌机组(也可以写成enemy_group.sprites())
            if collideMask(self.mask, enemy.mask, self.rect, enemy.rect): #判断玩家是否与敌机发生碰撞
                pg.quit() #死亡,退出pygame

        keys = pg.key.get_pressed() #获取按下且未松开的按键
        if keys[K_UP]:
            self.rect.y -= PLAYERSPEED
        elif keys[K_DOWN]:
            self.rect.y += PLAYERSPEED
        if keys[K_LEFT]:
            self.rect.x -= PLAYERSPEED
        elif keys[K_RIGHT]:
            self.rect.x += PLAYERSPEED

        if self.rect.centerx > WIDTH: #限制飞机移出屏幕
            self.rect.centerx = WIDTH
        elif self.rect.centerx < 0:
            self.rect.centerx = 0
        if self.rect.centery > HEIGHT:
            self.rect.centery = HEIGHT
        elif self.rect.centery < 0:
            self.rect.centery = 0

class Enemy(pg.sprite.Sprite): #敌机
    def __init__(self, *group):
        super().__init__(*group) #将精灵加入到group精灵组中

        self.anms = load_images("assets/enemy.png", 3) #分割单个的帧序列图片,分成3张图片
        self.anm_idx = 0 #当前帧图在列表中的索引位置
        self.last_animate = 0 #上一次刷新帧图
        
        self.image = self.anms[self.anm_idx]
        self.rect = self.image.get_rect(bottom=0, centerx=randint(0, WIDTH))
        self.mask = pg.mask.from_surface(self.image)

    def update(self):
        now = pg.time.get_ticks() #获取游戏运行时间(ms)
        if now - self.last_animate > 150: #每隔150ms更新一次帧
            self.last_animate = now
            self.anm_idx += 1
            self.anm_idx %= len(self.anms)
            self.image = self.anms[self.anm_idx]
            
        self.rect.y += ENEMYSPEED #移动敌机
        if self.rect.y > HEIGHT: #如果敌机飞出屏幕则移除此精灵
            self.kill()

class Bullet(pg.sprite.Sprite): #玩家子弹
    def __init__(self, *group):
        super().__init__(*group)

        self.image = pg.Surface((3, 25)) #子弹为3x25的纯黑色表面
        self.rect = self.image.get_rect(midbottom=player.rect.midtop) #子弹的中下位置位于飞机的中上位置

    def update(self):
        self.rect.y -= BULLETSPEED
        if self.rect.bottom < 0: #如果子弹飞出屏幕则移除此精灵
            self.kill()

# 定义常量
WIDTH = 500
HEIGHT = 600
PLAYERSPEED = 4 #玩家速度
ENEMYSPEED = 5 #敌机速度
BULLETSPEED = 10 #子弹速度
ENEMYRATES = 1000 #添加敌机频率
BULLETRATES = 600 #添加子弹频率(即玩家射击频率)

# 窗口
pg.init()
screen = pg.display.set_mode((WIDTH, HEIGHT))
pg.display.set_caption("飞机大战") #标题

# FPS
clock = pg.time.Clock()

# 精灵
all_group = pg.sprite.Group() #创建一个包含所有精灵的组以简化绘制精灵代码
enemy_group = pg.sprite.Group() #敌机组
bullet_group = pg.sprite.Group() #子弹组

player = Player(all_group)

last_add_enemy = 0
last_add_bullet = 0

# 游戏循环
while True:
    screen.fill((255, 255, 255))
    all_group.draw(screen) #绘制精灵
    
    player.update()
    all_group.update()

    pg.sprite.groupcollide(bullet_group, enemy_group, True, True) #子弹和敌机发生碰撞则同时移除

    now = pg.time.get_ticks() #获取游戏运行时间(ms)
    if now - last_add_enemy > ENEMYRATES: #每隔ENEMYRATES毫秒添加一次敌机
        last_add_enemy = now
        enemy_group.add(Enemy(all_group, enemy_group)) #添加敌机

    if now - last_add_bullet > BULLETRATES:
        last_add_bullet = now
        bullet_group.add(Bullet(all_group, bullet_group)) #添加子弹
        
    for event in pg.event.get():
        if event.type == QUIT:
            pg.quit()

    clock.tick(60) #刷新速度为60帧每秒
    pg.display.flip()
pygame绘制圆角矩形,Python pygame(GUI编程)模块教程,pygame,python,Powered by 金山文档

缓存算法

当一张图片需要被多次重复加载时,可以使用缓存。functools.lru_cache返回一个装饰器,可以用于保存函数的返回值。调用一次被lru_cache装饰的函数后,返回值会被储存起来,如果第二次调用该函数的参数与先前调用的参数相同,那么就从储存的值中直接返回,大大提高了加载速度。


@functools.lru_cache()
def load_image(path):
    return pg.image.load(path)

lru_cache方法有一个maxsize参数,指定缓存返回值的最大数量。

需要注意的是,lru_cache并不适合于给定参数相同,但返回值不一定相同的函数,如返回一个随机结果的函数。

分割单张帧序列图片

Surface.subsurface方法用于获取某个表面的子表面,常用于切割帧序列图。


@lru_cache()
def clip_image(surf, rect): #裁剪图片并返回结果
    return surf.subsurface(rect) #subsurface方法获取Surface指定位置的子表面

def load_images(path, count): #将某个表面裁剪成多个表面的列表
    surf = load_image(path)
    w = surf.get_width() / count #每个划分的表面的宽度
    h = surf.get_height()
    
    images = []    
    for i in range(count):
        subsurf = clip_image(surf, (i * w, 0, w, h))
        images.append(subsurf)

    return images

下一篇文章

Python pygame(GUI编程)模块最完整教程(5)_pygame 全屏_Python-ZZY的博客-CSDN博客文章来源地址https://www.toymoban.com/news/detail-629084.html

到了这里,关于Python pygame(GUI编程)模块最完整教程(4)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • pygame 绘制爱心函数 r = 1-cos(θ). Tag: python | 图形界面 | GUI

    最近做数学题,遇到一个“爱心函数” 即 r = 1 - cos(θ) . ——极坐标下函数表达式 用参数方程表示即: x = (1 - cos(θ)) * cos(θ) y = (1 - cos(θ)) * sin(θ) 放个函数图像 函数详情点这里 代码原理很简单,采用极坐标的方式,使用一个变量 t(即 θ ),每次循环自增(充当计时器/角度

    2024年02月13日
    浏览(38)
  • 【100天精通python】Day39:GUI界面编程_PyQt 从入门到实战(下)_图形绘制和动画效果,数据可视化,刷新交互

    目录 专栏导读  6 图形绘制与动画效果 6.1 绘制基本图形、文本和图片

    2024年02月12日
    浏览(53)
  • Pygame 教程(3):绘制图形

    本章,你将学习如何在 Pygame 中绘制图形。 上一章:重要的概念及对象 下一章:图像传输和绘制文本 抗锯齿(anti-aliasing,简称 AA)是一种消除显示器输出的画面中图物边缘出现凹凸锯齿的技术。实现抗锯齿效果需要更多的计算时间,因此在进行高质量绘制的同时,也会带来

    2024年02月06日
    浏览(32)
  • python图形界面化编程GUI(三)事件绑定(鼠标和键盘、OptionMenu、scale、颜色、文件对话框、菜单和工具栏、ttk子模块)

    Tkinter类 名称 简介 Toplevel 顶层 容器类,可用于为其他组件提供单独的容器,类似于窗口 Button 按钮 代表按钮组件 Canvas 画布 提供绘图功能,包括直线、矩形、椭圆、多边形、位图等 Entry 单行输入框 用户可输入内容 Frame 容器 用于装载其他GUI组件 Label 标签 用于显示不可编辑

    2024年02月12日
    浏览(47)
  • 使用pip安装Pygame模块的教程

    Pygame是一个用于开发2D游戏的Python模块,它提供了丰富的功能和工具,使得游戏开发变得简单而有趣。在本教程中,我将向你介绍如何使用pip来安装Pygame模块,并提供相应的源代码示例。 步骤1:安装Python和pip 在开始之前,确保你已经安装了Python和pip。你可以从Python官方网站

    2024年02月21日
    浏览(48)
  • PyQt5 GUI编程(QMainWindow与QWidget模块结合使用)

    QWidget是所有用户界面对象的基类,而QMainWindow 用于创建主应用程序窗口的类。它是 QWidget 的一个子类,提供了创建具有菜单栏、工具栏、状态栏等的主窗口所需的功能。上篇主要介绍了基本使用,创建窗口时都是继承单个模块,本章主要介绍下两个模块的结合使用。 1.我们先

    2024年04月08日
    浏览(77)
  • Python和Pygame绘制自动驾驶和移动机器本地规划器算法

    可视化自动驾驶车辆路径规划和移动机器人中使用的众多不同的本地规划器算法。 该应用程序提供可定制的参数,以更好地了解每种算法的内部工作原理并探索它们的优点和缺点。 它是用 Python 编写的,并使用 Pygame 来渲染可视化。 基类 概率路线图 快速探索随机树 势场 迪

    2024年02月02日
    浏览(43)
  • python如何安装pygame模块

    游戏设计作品集,艺术留学申请条件/费用/流程全面详解查看详情广告 1 首先按键盘上的“Win + R”键。 游戏设计作品集,艺术留学申请_ACG国际艺术教育查看详情广告 2 在弹出的运行窗口中输入cmd,输入完成后点击确定。 3 然后在弹出的命令提示符窗口中输入python,输入完成后点

    2024年02月03日
    浏览(44)
  • NSBezierPath绘制圆角矩形的圆角不够圆滑?

    在Macos应用开发过程中,使用OC语言编码,效果是:圆角的线宽 比 边框的 大或者浓。 经过大量查询,发现:如果圆角矩形宽高和View的宽高一样大,就导致圆角矩形的边框线有一半在View外面而被裁剪。 调整后的代码如下: 参考链接: https://outofmemory.cn/web/1018338.html     htt

    2024年02月10日
    浏览(35)
  • python安装pygame模块可能问题解决

    一般我们添加python中模块有两种方法 1、 在开发软件pycharm中文件中设置-项目 然后点击加号,输入自己要安装的模块,如pygame 但是我这种安装存在可能安装失败,但是简单 第二种就是用系统cmd命令进行安装 win+R打开cmd,先输入pip install wheel安装wheel工具,检查pip是不是最新版

    2024年02月12日
    浏览(47)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包