用pygame实现网上游戏‘球球情侣‘(检测矩形和某颜色碰撞的例子)
Posted geng_zhaoying
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了用pygame实现网上游戏‘球球情侣‘(检测矩形和某颜色碰撞的例子)相关的知识,希望对你有一定的参考价值。
网上有个’球球情侣’游戏,游戏中有两个不同颜色的球,玩者首先用鼠标画曲线画出球移动的路径,分别右击两个球,使两球沿曲线移动,如果两球碰到一起,进入下一关。编写很多关是游戏公司的事,我们只编写最简单的一关,说明实现游戏的方法。用pygame编写了’球球情侣’游戏。游戏运行效果如下:
可以看到本程序要实现两个功能,第一,用鼠标画多条曲线,右击球后,球移动,不能再画线。第二,球沿所画曲线向下移动,同时检测两球是否发生碰撞。拖动鼠标画线功能被封装在DrawLines类中。球沿所画曲线向下移动功能被封装在Ball类中。
pygame.draw.lines(Surface,color,closed,pointlist,width=1)方法是pygame画多个线段的方法。其中pointlist是列表,记录所有线段端点的坐标,至少需要有两个点。曲线可认为是由很多非常短的线段组成,将这些非常短的线段的端点保存到列表中,就可以用上边方法画曲线了。实现方法见DrawLines类的drawAline(self,Event)方法,其参数Event是事件。当鼠标左键按下且允许画线,令self.mark=1,表示鼠标已按下(第11,12行)。当鼠标按下移动且允许画线,将当前鼠标坐标保存到self.aLine,self.aLine保存当前正在画的曲线(第18,19行)。当鼠标左键抬起且允许画线(第13行),表示该曲线已完成,mark=0,鼠标抬起标志,如果记录当前曲线的列表self.aLine项数(线段端点数)在2以上,说明列表保存的是1条曲线,要把self.aLine保存到列表self.allLines中,这个列表记录了所画的所有曲线,最后清空self.aLine,准备画下一条曲线。DrawLines类的draw()(第20行)方法负责在每1帧渲染时重画所有的曲线,这种方法直接把图形画在主窗体screen上()(第121行)。
pygame创建主窗体screen=pygame.display.set_mode(size),每一帧首先将screen清空,再将图像拷贝screen显示,从而完成动画效果。用上节所介绍的方法是直接把黑线画在screen上,为了重画黑线到screen,必须把曲线所有点保存到列表中。换一种思路,可创建一个Surface类实例bg,和主窗体等宽高,把黑线画在bg上,然后再把bg拷贝到screen显示,这种方法不需要列表。实现的基本代码如下,非完整程序,只是说明问题,要作为类的实例方法,还需做必要改动。本例没有采用这种方法。
white=pygame.Color('white') #定义Color类实例为白色
bg=pygame.surface.Surface(size,0,32) #1个Surface实例,和主窗体尺寸相同
bg.fill(white) #背景色为白色
bg.set_colorkey(white)#背景透明,使在bg上画图似乎是在主窗体上,不影响颜色判断
def drawAline(Event):
global mark,start_pos,bg #下句如鼠标左键按下且允许画线
if Event.type==MOUSEBUTTONDOWN and Event.button==1 and Ball.canDrawL:
mark=1 #mark=1表示鼠标被按下,如鼠标移动则画线
start_pos=event.pos#1条线第1点或画线起点,下句鼠标左键抬起且允许画线
if Event.type==MOUSEBUTTONUP and event.button==1 and Ball.canDrawL:
mark=0 #1条曲线已完成,mark=0鼠标抬起标志,下句鼠标按下移动且允许画线
if Event.type==MOUSEMOTION and mark==1 and Ball.canDrawL:
pygame.draw.line(bg,black,start_pos,event.pos,10)#将短线段画在bg上
start_pos=event.pos #画下个线段的起点
def drawAllLines(aSurface): #将所有曲线显示在主窗体
global bg
aSurface.blit(bg,(0,0))
右击任意两球中的一个,就要停止画线,被右击的球开始沿曲线移动。为确保球正确沿曲线移动,向下移动的初始位置沿y轴方向不能碰到黑线或红色块,而且距离黑线的距离不大于一次移动距离,即类实例变量self.dy。但是所画的黑线可能不能满足这个条件,因此在球沿曲线移动前,要做一些准备工作。移动前所画黑线可能碰到或没碰到球两种情况,如碰到球,球向上移动直到和黑线距离不大于self.dy,称为状态1,如没碰到球,球向下移动直到和黑线距离不大于self.dy,为状态2,这两个状态是准备状态,而球自动沿所画曲线移动为状态3,用self.state记录状态。第42行state1or2(self,pos)方法根据鼠标右击时鼠标的位置pos,先判断是否右击了球,如右击了球,画线结束,再分辨是状态1还是状态2。在方法update()中,根据self.state状态,做不同工作,注意,状态1或状态2结束后,都转为状态3。状态1和2比较好理解,这里重点介绍状态3,如何使球正确沿曲线移动。每次循环(每1帧)执行1次update(),在执行完状态1或2后,进入状态3的第1帧,如上所述,第1帧球一定没碰到黑或红色,第75条语句一定不成立,直接执行第77-78条语句,第79-81条语句判断是否越界。第82-83条语句判断是否碰到黑或红,如没碰到,第2帧执行第75条语句一定不成立,继续执行第77-78条语句,保持x原方向沿曲线下行。如第1帧执行第82-83条语句判断碰到黑或红,可能是+dx,也可能是+dy使球碰到黑或红,无论那种情况,必须执行第83条语句。第2帧执行第75条语句,若没碰到黑或红,说明第1帧是由于+dy使球碰到黑或红,球保持x原方向沿曲线下行;若碰到黑或红,说明第1帧是由于+dx使球碰到黑或红,此时要求球回到上1帧位置,必须反向移动,注意上1帧y方向已-dy,y方向已回到原位。就这样,一帧接着一帧,使球不断运动。方法collide_color(self,aColor)用来判断球是否碰到某种颜色,参数aColor是要检测碰撞的颜色,其原理可参考本人博文:pygame游戏检测矩形是否碰撞指定颜色的自定义函数(仅5行代码)。
完整程序如下:
import pygame
from pygame.locals import *
class DrawLines():
canDraw=True #是否允许画,无论有多少个类实例,类变量是唯一的,所有类实例共用。使用方法:类名.类变量名
def __init__(self,color): #color是画线的颜色
self.color=color
self.aLine = [] #在鼠标按下移动时,将鼠标坐标保存到该列表,是1条曲线所有点
self.allLines=[] #鼠标抬起将aLine列表保存到本列表,因此本列表保存多条曲线
self.mark=0 #=0,表示鼠标未按下
def drawAline(self,Event):
if Event.type==MOUSEBUTTONDOWN and Event.button==1 and DrawLines.canDraw:#鼠标左键按下事件且允许画线
self.mark=1 #mark=1表示鼠标被按下,如鼠标移动则画线
if Event.type==MOUSEBUTTONUP and event.button==1 and DrawLines.canDraw: #鼠标左键抬起且允许画线
self.mark=0 #表示当前曲线已完成,mark=0,鼠标抬起标志
if len(self.aLine) > 1: #1条曲线至少两点,>1表示是1条曲线且完成,保存到allLines
self.allLines.append(self.aLine)
self.aLine=[] #清空self.aLine
if Event.type==MOUSEMOTION and self.mark==1 and DrawLines.canDraw: #鼠标按下移动且允许画线
self.aLine.append(event.pos) #将当前鼠标坐标保存到aLine,aLine保存当前正在画的曲线
def draw(self,aSurface):
if len(self.aLine)>1: #>1,表示当前正在画的曲线已有两个点,两点可画1条线段
pygame.draw.lines(aSurface,self.color,False,self.aLine,10)#该曲线未保存到self.allLines,需单独画出
if len(self.allLines)>0: #除当前正在画的曲线,画其它已完成的曲线
for ps in self.allLines:
pygame.draw.lines(aSurface,self.color,False,ps,10)
class Ball():
stop=False #类变量,无论有多少个类实例,是唯一的,所有类实例共用。使用方法是:类名.类变量名
winFailStr=' Press key r replay!'
def __init__(self,Screen,color, pos, radius): #参数3为球颜色,参数4为球圆心,参数5为球半径
self.screen=Screen
self.rect=pygame.Rect(1,1,2*radius,2*radius) #self.rect为球的外切矩形
self.rect.center=pos
self.color=color
self.dx=5 #每帧沿x轴移动距离dx
self.dy=5 #每帧沿y轴移动距离dy
self.state=0 #=1,2,3。看下面注释。
def collide_color(self,aColor): #判断在self.screen的self.rect区域是否包含aColor颜色
pixel=pygame.PixelArray(self.screen) #锁定对象screen,将其各点颜色保存在2维数组
aPixel=pixel[self.rect.x:self.rect.x+self.rect.width,self.rect.y:self.rect.y+self.rect.height]#2维数组切片
pygame.PixelArray.close(pixel) #解锁screen对象
return aColor in aPixel #如包含aColor颜色返回真,否则为假
def state1or2(self,pos): #首先判断是否结束画曲线,如结束,判断是state=1或2
if self.state>0: #返回False原因,见第125行注释
return False #返回False,表示已确定了等级,不必再一次计算
if self.rect.collidepoint(pos): #如鼠标点击球结束画曲线
DrawLines.canDraw=False #不允许再画曲线
if self.collide_color(black): #调用实例方法检测是否碰到黑色,画线碰到黑线,为状态1
self.state=1
else: #画线碰到黑线,为状态2
self.state=2
return True #如点击球,返回True
else:
return False #如未点击球,返回False
def update(self):
if DrawLines.canDraw:
return
if self.state==1: #为状态1,球将沿y轴向上移动,如能使球距黑线<dy,转state=3,出界游戏结束
if self.collide_color(black):
self.rect.centery-=self.dy
if self.rect.centery<25:
Ball.stop=True #越界,结束程序,输了
Ball.winFailStr='You fail!'+Ball.winFailStr
else:
self.state=3
elif self.state==2: #为状态2,沿y轴向下移动,如能使球距黑线<dy,转state=3,碰到红色游戏结束
if not(self.collide_color(black)):
self.rect.centery+=self.dy
if self.collide_color(red):
Ball.stop=True #没画黑线,结束程序,输了
Ball.winFailStr='You fail!'+Ball.winFailStr
else:
self.rect.centery-=10
self.state=3
elif self.state==3: #为状态3,沿所画曲线移动,x和y坐标都要变,遇到黑或红反向移动,移到边界结束
if self.collide_color(black) or self.collide_color(red): #初始设置保证第1次肯定不成立
self.dx=-self.dx
self.rect.centerx+=self.dx #y和x方向增加dy或dx,可能有两种情况,碰到或没碰到黑或红
self.rect.centery+=self.dy
if self.rect.centerx<24 or self.rect.centerx>475 or self.rect.centery>475: #越界
Ball.stop=True #越界,结束程序,输了
Ball.winFailStr='You fail!'+Ball.winFailStr
if self.collide_color(black) or self.collide_color(red): #如碰到黑或红,y方向退回原位置
self.rect.centery-=self.dy #此时有两种可能,碰到黑或红,下次x方向反向,否则不反向
def draw(self):
pygame.draw.circle(self.screen,self.color,self.rect.center,self.rect.width//2,0)
def reSet(): #重玩游戏,调用此方法
global ballB,ballG,mousePos,rightClick,drawLines,black #全局变量要初始化
ballB=Ball(screen,blue,(160,125),25) #放弃旧圆,创建新圆,使圆在初始状态
ballG=Ball(screen,green,(340,125),25)
Ball.stop=False
DrawLines.canDraw=True
Ball.winFailStr=' Press key r replay!'
drawLines=DrawLines(black)
mousePos=(0,0)
rightClick=False
bgcolor = pygame.Color('cyan')
blue=pygame.Color('blue')
red=pygame.Color('red')
green=pygame.Color('green')
black=pygame.Color('black')
pygame.init()
size = width, height = 500,500
screen = pygame.display.set_mode(size)
pygame.display.set_caption("球球情侣")
reSet()
fclock = pygame.time.Clock()
fps = 20
running = True
font1 = pygame.font.SysFont("arial", 25)
while running:
screen.fill(bgcolor)
for event in pygame.event.get():
if event.type == pygame.QUIT: #是否退出游戏
running = False #退出游戏
if event.type == pygame.KEYUP and event.key == pygame.K_r: #按r键后,重玩游戏
reSet()
drawLines.drawAline(event) #用鼠标画线,将曲线的各端点保存到列表
if event.type==MOUSEBUTTONDOWN and event.button==3: #鼠标右键按下事件
mousePos=event.pos #鼠标右键按下事件时坐标
rightClick=True #鼠标右键按下标志
drawLines.draw(screen)
pygame.draw.rect(screen,red, (100,200,300,50), 0) #画两个红色方块
pygame.draw.rect(screen,red, (230,10,40,200), 0)
if rightClick:
rightClick=False #下句,or前方法返回True,就不再执行or后方法,如or前方法已做过判断,要返回False
if ballB.state1or2(mousePos) or ballG.state1or2(mousePos): #若右键点击了蓝球或绿球,要结束画线
mousePos=(0,0)
if not(Ball.stop):
ballB.update()
ballG.update()
ballB.draw()
ballG.draw()
surface1=font1.render(Ball.winFailStr,True,[255,0,0]) #不能显示中文
screen.blit(surface1, (10, 470)) #显示输赢字符串
pygame.display.update()
if ballB.rect.collidepoint(ballG.rect.center) and not(Ball.stop):#检测1个Rect中心是否在另1Rect中,是返回真
Ball.stop=True #显示你赢了,圆停止运动
Ball.winFailStr='You win!'+Ball.winFailStr
fclock.tick(fps)
pygame.quit()
以上是关于用pygame实现网上游戏‘球球情侣‘(检测矩形和某颜色碰撞的例子)的主要内容,如果未能解决你的问题,请参考以下文章
用pygame实现网上游戏‘球球情侣‘(检测矩形和某颜色碰撞的例子)
用pygame实现网上游戏‘球球情侣‘(用pygame.mask完成碰撞检测)
用pygame实现网上游戏‘球球情侣‘(用pygame.mask完成碰撞检测)
用pygame实现网上游戏‘球球情侣‘(用pygame.mask完成碰撞检测)