pygame障碍游戏将障碍直接画在背景中,实现用mask侦测背景中障碍和用颜色区分不同障碍的方法

大型障碍游戏有很多关,障碍很多,将所有障碍设计为角色显然不是好办法。比较实用的方法是创建一个Surface类实例bg,和主窗体等宽等高,把障碍图形画在bg上,然后再把bg拷贝到主窗体上显示作为游戏的背景。将bg和游戏角色不希望显示部分设为透明,将透明部分和不透明部分的标记保存到mask。角色在背景bg上移动,用mask侦测背景中不透明的障碍和角色是否发生碰撞,如发生碰撞,侦测方法将返回碰撞点在bg上的坐标,根据该坐标可得到该点的颜色,就能知道角色所碰到的图形是那种颜色,根据颜色知道碰到那种障碍,还可用pygame.mask.from_threshold函数直接侦测角色是否和指定颜色发生碰撞,确定障碍物。根据确定的障碍物用相应程序去处理。这样每关只需修改背景图形,而程序预先设计好角色在障碍上所有动作的对应程序,以后每一关程序基本无需再修改,极大减少了工作量。
第1个例子不考虑颜色,在窗体两侧画两条黑色线,红色圆沿x方向前进,碰到黑线反向运动。这里两条黑线是障碍,将其画在和主窗体等宽等高的一个Surface类实例bg上,红色圆是角色,用mask侦测bg和红色圆的碰撞,判断红色圆是否碰到黑线,碰到黑线,红色圆反向运动。最关键的是第39-41行语句。rect定义bg的宽和高以及位置,红色圆是一个Surface类实例,rect1定义该实例的宽和高以及位置,它们位置都是相对于窗体的坐标。offset是红色圆和bg的相对位置(第39行),将offset提供给第40行的判断碰撞的方法overlap,该方法使用bg的mask1和红色圆的mask2根据两者相对位置offset判断bg和红色圆是否发生碰撞。注意offset是参数1的位置减bg位置,次序不能交换。本例中,bg的(rect.x,rect.y)=(0,0),因此offset=rect1.x,rect1.y。完整程序如下,拷贝源程序运行后就能立刻看到运行效果。可以看到背景是蓝色的,这是由第33行语句设定的,由于bg大部分是透明的,因此可以看到到蓝色背景。可将第33行改为背景图片,因背景图片并不参加碰撞,不会对程序程序设计产生什么影响,但能够使游戏更加逼真,漂亮。

import pygame

bgcolor = pygame.Color('white')
red=pygame.Color('red')
blue=pygame.Color('blue')
black=pygame.Color('black')
pygame.init()
size = width, height = 200,100
pygame.display.set_caption("圆碰黑线反向移动")
screen = pygame.display.set_mode(size)

bg=pygame.surface.Surface(size,0,32)            #作为背景的Surface实例bg
bg.fill(bgcolor)                                #底色
bg.set_colorkey(bgcolor)                        #底色设置为透明
pygame.draw.line(bg,black,(10,10),(10,90),5)    #画窗体两侧的两条黑线
pygame.draw.line(bg,black,(190,10),(190,90),5)
rect = bg.get_rect()
#创建bg的mask,记录那些点是透明的,哪些点不透明,只有不透明点参与碰撞检测
mask1=pygame.mask.from_surface(bg)              

surf = pygame.surface.Surface((20,20), 0, 32)   #移动的圆
surf.fill(bgcolor)                              #圆的底色
surf.set_colorkey(bgcolor)                      #设置圆的底色为透明
pygame.draw.circle(surf,red,(10,10),10)         #画红色实心圆
mask2=pygame.mask.from_surface(surf)
rect1 = surf.get_rect(center=(90,35))           #圆初始位置
               
dx=5                                            #圆移动速度
fclock = pygame.time.Clock()
fps = 4
running = True
while running:
    screen.fill(blue)       #窗体的背景是蓝色,bg背底色透明,运行后看到背景色是蓝色                       
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False        
    screen.blit(bg, (0,0))                      #显示bg,其底色透明,只能看到两条黑线
    rect1.x+=dx                                 #圆移动
    offset = rect1.x - rect.x, rect1.y - rect.y #被减数和减数次序不能交换
    if mask1.overlap(mask2, offset)!=None:      #碰到黑线就反向
        dx=-(dx)
    screen.blit(surf, rect1)    
    pygame.display.update()                     #显示红色圆                           
    fclock.tick(fps)
pygame.quit()

一般情况下,是把每一关的障碍图用画图程序预先完成,再用程序载入画好障碍的图片作为游戏背景。障碍图片文件必须是png格式,底色必须透明。这里用一个迷宫游戏说明载入障碍图片作为游戏背景的具体步骤。效果图如下:
在这里插入图片描述

完整程序如下:

import pygame

white = pygame.Color('white')
red=pygame.Color('red')
blue=pygame.Color('blue')
black=pygame.Color('black')
pygame.init()
size = width, height = 350,320
pygame.display.set_caption("迷宫")
screen = pygame.display.set_mode(size)

background=pygame.image.load('迷宫去底色.png').convert_alpha()
#background.set_colorkey(white) #使用此条语句不行,可能原图是rgb格式?
rect = background.get_rect()
mask1=pygame.mask.from_surface(background)

surf = pygame.surface.Surface((20,20), 0, 32)
surf.fill(white)
surf.set_colorkey(white)
pygame.draw.circle(surf,red,(10,10),10)
mask2=pygame.mask.from_surface(surf)
rect1 = surf.get_rect(center=(15,155))
               
fclock = pygame.time.Clock()
fps = 10
running = True
while running:
    screen.fill(white)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
    if event.type == pygame.KEYDOWN:            
            if event.key==pygame.K_LEFT:
                rect1.x-=5              #下句参数2被减数和减数次序不能交换   
                if rect1.x<0 or mask1.overlap(mask2,(rect1.x-rect.x,rect1.y-rect.y))!=None:
                    rect1.x+=5                    
            elif event.key==pygame.K_RIGHT:
                rect1.x+=5                
                if rect1.x>330 or mask1.overlap(mask2,(rect1.x-rect.x,rect1.y-rect.y))!=None:
                    rect1.x-=5
            elif event.key==pygame.K_UP:
                rect1.y-=5                
                if rect1.y<0 or mask1.overlap(mask2,(rect1.x-rect.x,rect1.y-rect.y))!=None:
                    rect1.y+=5
            elif event.key==pygame.K_DOWN:
                rect1.y+=5                
                if rect1.y>310 or mask1.overlap(mask2,(rect1.x-rect.x,rect1.y-rect.y))!=None:
                    rect1.y-=5
    screen.blit(background, (0,0))                                    #背景
    screen.blit(surf, rect1)    
    pygame.display.update()                           
    fclock.tick(fps)
pygame.quit()

迷宫游戏程序很简单,有兴趣可在此基础上完成吃豆游戏。豆子被吃掉就要消失,不能放到障碍背景中。要定义以pygame.sprite.Sprite为基类的豆子类和吃豆者类,创建pygame.sprite.Group列表保存所有豆子,用pygame.sprite.spritecollide(吃豆者,group,True,collided)方法侦测吃豆者和豆子的碰撞,参数collided选pygame.sprite.collide_mask,参数3为True,被吃豆者吃掉的豆子就会自动被从group列表中删除。
现在介绍如何用颜色区分不同的障碍物。如果在第一个例子中,在两条黑线之间增加一条红线,希望只有圆碰到黑线转向,碰到红线不转向。用mask侦测bg和红色圆的碰撞,红色圆无论碰到黑线还是红线,都会侦测到发生碰撞,碰撞发生后,红色圆必须根据颜色决定是否转向。参加下边完整程序,第40行的overlap方法,如红色圆没有和bg上障碍物发生碰撞,返回None,如发生碰撞,则返回两者的第1个碰撞点在bg的mask1的坐标(x,y)。mask1是一个2维数组,每一项仅占2进制的1位,用来记录bg的图片中每一点是否透明,这些概念可推广到所有mask。bg的图片的每一点都有不同颜色,也可以用2维数组描述,因此mask1的2维数组和bg的图片2维数组是一一对应的,即mask1的坐标(x,y)是记录bg图片该点是否透明,bg图片2维数组是记录该点的颜色值。由此很容易从第40行语句得到的坐标p,得到碰撞点颜色。第39-43语句,实现了红色圆碰到黑线转向,碰到红线不转向。完整程序如下,拷贝源程序运行后就能立刻看到运行效果。

import pygame

bgcolor = pygame.Color('white')
red=pygame.Color('red')
blue=pygame.Color('blue')
black=pygame.Color('black')
pygame.init()
size = width, height = 200,100
pygame.display.set_caption("圆仅碰黑线反向移动")
screen = pygame.display.set_mode(size)

bg=pygame.surface.Surface(size,0,32)
bg.fill(bgcolor)                                    #背景色
bg.set_colorkey(bgcolor)
pygame.draw.line(bg,black,(10,10),(10,90),5)        #画窗体两侧的两条黑线
pygame.draw.line(bg,black,(190,10),(190,90),5)
pygame.draw.line(bg,red,(70,10),(70,90),5)
rect = bg.get_rect()
mask1=pygame.mask.from_surface(bg)

surf = pygame.surface.Surface((20,20), 0, 32)           #所画小圆
surf.fill(bgcolor)
surf.set_colorkey(bgcolor)
pygame.draw.circle(surf,red,(10,10),10)
mask2=pygame.mask.from_surface(surf)
rect1 = surf.get_rect(center=(90,35))
               
dx=5                                               #小圆移动速度
fclock = pygame.time.Clock()
fps = 4
running = True
while running:
    screen.fill(blue)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False        
    screen.blit(bg, (0,0))                                    #背景
    rect1.x+=dx
    offset = rect1.x - rect.x, rect1.y - rect.y    #被减数和减数次序不能交换
    p=mask1.overlap(mask2, offset)
    if p!=None:                 #如果发生碰撞
        if bg.get_at(p)==black: #碰到红色线,速度不反向,碰到黑色线,速度反向
            dx=-(dx)
    screen.blit(surf, rect1)    
    pygame.display.update()                           
    fclock.tick(fps)
pygame.quit()

第4个程序,有4个位置固定、颜色不同的圆,一个随鼠标移动的圆,当随鼠标移动的圆碰到固定位置的圆,随鼠标移动的圆的颜色变为和其发生碰撞的固定位置圆的颜色。实现方法和上一个例子基本相同。但有一点需要注意,随鼠标移动的圆改变自己的颜色,是在原位置重画了一个不同颜色的新圆,覆盖了旧的圆。图形变动并没有重新设置mask,程序仍能正常运行。这告诉我们,仅修改图片中障碍物的颜色,不修改障碍物的位置和形状,不必重新设置其mask。完整程序如下,拷贝源程序运行后就能立刻看到运行效果。

import pygame

bgcolor = pygame.Color('white')
red=pygame.Color('red')
blue=pygame.Color('blue')
black=pygame.Color('black')
pink=pygame.Color('pink')
green=pygame.Color('green')
pygame.init()
size = width, height = 300,200
pygame.display.set_caption("移动圆仅碰到蓝色圆产生碰撞")
screen = pygame.display.set_mode(size)

bg=pygame.surface.Surface(size,0,32)
bg.fill(bgcolor)                                    #背景色
bg.set_colorkey(bgcolor)
pygame.draw.circle(bg, red, (100,60),  20, 0)
pygame.draw.circle(bg, blue, (200,60),  20, 0)
pygame.draw.circle(bg, pink, (100,120), 20, 0)
pygame.draw.circle(bg, black, (200,120), 20, 0)
rect = bg.get_rect()
mask=pygame.mask.from_surface(bg)

surf = pygame.surface.Surface((32,32), 0, 32)           #所画小圆
surf.fill(bgcolor)
surf.set_colorkey(bgcolor)
pygame.draw.circle(surf,green,(16,16),16)
mask1=pygame.mask.from_surface(surf)
rect1 = surf.get_rect(center=(90,35))

fclock = pygame.time.Clock()
fps = 5
running = True
while running:
    screen.fill(bgcolor)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False        
    screen.blit(bg, (0,0))                                    #背景
    color1=green
    rect1.center = pygame.mouse.get_pos()    
    offset = rect1.x - rect.x, rect1.y - rect.y    #被减数和减数次序不能交换
    p=mask.overlap(mask1, offset)
    if p!=None:
        color1=bg.get_at(p)
    pygame.draw.circle(surf,color1,(16,16),16)#用不同颜色重画了surf中的圆,因在原位置对其mask无影响
    screen.blit(surf, rect1)    
    pygame.display.update()                           
    fclock.tick(fps)
pygame.quit()

还可以用函数pygame.mask.from_threshold()侦测角色是否和指定颜色发生碰撞。该函数有5个参数,这里只是用前3个参数,见程序第21行,参数1是需要创建mask的surface,参数2是要侦测的颜色,参数3是颜色阈值,一般设定为(1,1,1,255)。该方法返回一个pygame.mask,这个mask只对参数2指定颜色产生碰撞响应。如想更多了解该函数,可见本人博客:函数pygame.mask.from_threshold()用阈值确定mask碰撞点原理及使用方法。用第5个程序介绍该函数使用方法。该例和第4个例子相同,创建一个Surface类实例bg,和主窗体等宽等高,在bg上画4个不同颜色的圆形。再创建1个能随鼠标移动的圆角色,该圆角色只有碰到bg上的蓝色圆才发生碰撞,将自己颜色改为蓝色。碰到其它颜色的固定位置的圆形完不发生碰撞,该圆角色保持原来颜色。但使用函数pygame.mask.from_threshold()实现。如果需要侦测多个颜色,只需为bg创建多个mask,侦测那种颜色,使用相应mask。完整程序如下,拷贝源程序运行后就能立刻看到运行效果。

import pygame
bgcolor = pygame.Color('white')
red=pygame.Color('red')
blue=pygame.Color('blue')
black=pygame.Color('black')
pink=pygame.Color('pink')
green=pygame.Color('green')
pygame.init()
size = width, height = 300,200
pygame.display.set_caption("移动圆仅碰到蓝色圆产生碰撞")
screen = pygame.display.set_mode(size)

bg=pygame.surface.Surface(size,0,32)                #和窗体同宽同高,将成为背景
bg.fill(bgcolor)                                    #背景为白色
bg.set_colorkey(bgcolor)                            #白色设置为透明色
pygame.draw.circle(bg, red, (100,60),  20, 0)       #在bg上画4个不同颜色的圆
pygame.draw.circle(bg, blue, (200,60),  20, 0)
pygame.draw.circle(bg, pink, (100,120), 20, 0)
pygame.draw.circle(bg, black, (200,120), 20, 0)
rect = bg.get_rect()
mask=pygame.mask.from_threshold(bg,pygame.Color('blue'),(1,1,1,255))#移动圆只有碰到蓝色,才产生碰撞

surf = pygame.surface.Surface((32,32), 0, 32)           #所画小圆
surf.fill(bgcolor)
surf.set_colorkey(bgcolor)
pygame.draw.circle(surf,green,(16,16),16)
mask1=pygame.mask.from_surface(surf)
rect1 = surf.get_rect(center=(90,35))

fclock = pygame.time.Clock()
fps = 5
running = True
while running:
    screen.fill(bgcolor)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False        
    screen.blit(bg, (0,0))                          
    color1=green
    rect1.center = pygame.mouse.get_pos()    
    offset = rect1.x - rect.x, rect1.y - rect.y    #被减数和减数次序不能交换
    if mask.overlap(mask1, offset)!=None:          #如产生碰撞,使移动圆变为蓝色
        color1=blue
    pygame.draw.circle(surf,color1,(16,16),16)#用不同颜色重画了surf中的半径相同圆,因在原位置对其mask无影响
    screen.blit(surf, rect1)    
    pygame.display.update()                           
    fclock.tick(fps)
pygame.quit()

还可以使用本人博客中方法:pygame游戏检测矩形是否碰撞指定颜色的自定义函数(仅5行代码),但这个方法使用注意事项太多,显然不如上边两种方法。

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值