pygame障碍游戏将障碍直接画在背景中,实现用mask侦测背景中障碍和用颜色区分不同障碍的方法
Posted geng_zhaoying
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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:以上是关于pygame障碍游戏将障碍直接画在背景中,实现用mask侦测背景中障碍和用颜色区分不同障碍的方法的主要内容,如果未能解决你的问题,请参考以下文章
pygame障碍游戏将障碍直接画在背景中,实现用mask侦测背景中障碍和用颜色区分不同障碍的方法
超人游戏_将障碍画在背景中用pygame.mask.from_threshold实现超人和不同颜色障碍精准碰撞检测
超人游戏_将障碍画在背景中用pygame.mask.from_threshold实现超人和不同颜色障碍精准碰撞检测
超人游戏_将障碍画在背景中用pygame.mask.from_threshold实现超人和不同颜色障碍精准碰撞检测