之前我们通过摩擦摩擦,可以使得小球变绿色,并不再随机移动:
现在我们需要设置在小球变绿之后只用键盘来控制他。
——通过 w,a,s,d 或者 ↑,↓,←,→ ,来完成对变成绿色的小球的 向上,向左,向下,向右 的控制。
from pygame.locals import *
from random import *
import traceback
import pygame
import math
import time
import sys
class Ball(pygame.sprite.Sprite) : #继承动画精灵基类
def __init__ (self,grayball_image,greenball_image,position,speed,bg_size,target) :
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load(grayball_image).convert_alpha()
self.grayball_image = grayball_image
self.greenball_image = greenball_image
self.rect = self.image.get_rect() #获得球的尺寸
self.rect.left , self.rect.top = position #将出现的位置赋给球
self.direction = [ speed[0]/abs(speed[0]) , speed[1]/abs(speed[1]) ] #小球当前运动的方向
self.speed = speed #设置速度大小
self.colide = False #设置是否发生碰撞属性
self.target = target #设置一个使小球变为可控的目标
self.control = False #小球是否人为可控的标志
self.width , self.height = bg_size[0] , bg_size[1] #获得活动边界,就是背景的边界
def drawball(self):
if self.control : #如果小球可用,control=1
self.image = pygame.image.load(self.greenball_image) #重新绘制小球为绿色的小球
else : #否则绘制灰色小球
self.image = pygame.image.load(self.grayball_image)
def move(self):
self.rect = self.rect.move(self.speed) #根据自身的速度进行移动
if self.rect.right < 0: #图片的右边已经超出边界的左边,即整个球已经出界
self.rect.left = self.width #让他从右边界回来
if self.rect.bottom < 0: #图片的底已经超出边界的上面
self.rect.top = self.height #让他从底部回来
if self.rect.left > self.width: #图片的左边已经超出边界的右边
self.rect.right = 0 #让他从左边回来
if self.rect.top > self.height: #如果图片的顶部已经超出边界的底部
self.rect.bottom = 0 #让他从顶部回来
def check(self,motion): #检查鼠标移动的频率是否打到控制小球的目标值
if self.target - 4 <= motion <= self.target + 4 :
self.control = True #小球可控标志设为1
return True
else :
return False
#判断碰撞检测函数
def collide_check(item,target):
col_balls = [] #添加碰撞小球
for each in target: #对 target 中所有的目标小球进行检测
#两个球心之间的距离
distance = math.sqrt( math.pow( (item.rect.center[0] - each.rect.center[0]) , 2 ) + \
math.pow( (item.rect.center[1] - each.rect.center[1]) , 2) )
if distance <= ( item.rect.width + each.rect.width ) / 2: #如果距离小于等于两者间的半径之和也就是两个直径之和的一半
col_balls.append(each) #将这个发生碰撞的小球添加到列表中
return col_balls
def main() :
pygame.init() #初始化
#将所有图片的路径写入
bg_image = r"D:\Code\Python\Pygame\pygame9:摩擦摩擦\background.png" #背景图
grayball_image = r"D:\Code\Python\Pygame\pygame9:摩擦摩擦\gray_ball.png" #灰小球的图片
greenball_image = r"D:\Code\Python\Pygame\pygame9:摩擦摩擦\green_ball.png" #绿小球的图片
galss_image = r"D:\Code\Python\Pygame\pygame9:摩擦摩擦\glass.png" #玻璃板图片
hand_image = r"D:\Code\Python\Pygame\pygame9:摩擦摩擦\hand.png" #鼠标在玻璃板上的样子
hand1_image = r"D:\Code\Python\Pygame\pygame9:摩擦摩擦\hand1.png" #鼠标在其他地方的样子
running = True #为了以后而已有多种方法退出程序
#加载背景音乐
pygame.mixer.music.load(r"D:\Code\Python\Pygame\pygame8:播放声音和音效\bg_music.ogg")
pygame.mixer.music.set_volume(0.2) #设置音量
pygame.mixer.music.play() #播放背景音乐
#设置一个背景音乐完毕之后的结束事件
GAMEOVER = USEREVENT
pygame.mixer.music.set_endevent(GAMEOVER) #当音乐播完后,发送一个GAMEOVER事件
#加载四个音效
hole_music = pygame.mixer.Sound(r"D:\Code\Python\Pygame\pygame9:摩擦摩擦\loser.wav")
laugh_music = pygame.mixer.Sound(r"D:\Code\Python\Pygame\pygame9:摩擦摩擦\laugh.wav")
winner_music = pygame.mixer.Sound(r"D:\Code\Python\Pygame\pygame9:摩擦摩擦\winner.wav")
loser_music = pygame.mixer.Sound(r"D:\Code\Python\Pygame\pygame9:摩擦摩擦\loser.wav")
#设置背景
bg_size = width , height = 1024 , 681 #背景大小
screen = pygame.display.set_mode(bg_size) # 设置背景大小
background = pygame.image.load(bg_image).convert_alpha() #画背景
#绘制用于摩擦的玻璃板,要花在小球前面,不然的话,后面小球会从玻璃板的下方划过
galss = pygame.image.load(galss_image).convert_alpha() #画玻璃板
galss_rect = galss.get_rect() #获得玻璃板的尺寸
galss_rect.left , galss_rect.top = ((width-galss_rect[2])/2 , (height-galss_rect[3])) #玻璃板的位置
balls = []
#加载鼠标图片
hand = pygame.image.load(hand_image).convert_alpha() #画玻璃版内鼠标
hand1 = pygame.image.load(hand1_image).convert_alpha() #画平常鼠标
flag = False #设置一个变量,用来表示鼠标是否进入玻璃板范围
#设置响应键盘连续输入
pygame.key.set_repeat(100,100)
#用来计数一秒钟内移动的次数
motion = 0
#设置一个自定义事件,用来检测鼠标移动的值是否符合控制小球的目标值
MYTIMER = USEREVENT + 1 #因为之前已经定义了一个自定义事件,所以根据之前说的这个自定义事件应该是之前的加1
pygame.time.set_timer(MYTIMER,1000) #计时器事件为1s
# 创建五个小球
BALL_NUM = 5
for i in range (BALL_NUM) : #生成5个球
position = randint (0,width-100) , randint(0,height-100) #要减去100是因为球图片尺寸的大小为100,随机生成位置
speed = [ randint (-10,10) , randint(-10,10) ]
ball = Ball(grayball_image,greenball_image,position,speed,bg_size,5*(i+1)) #生成球的对象
while collide_check(ball,balls): #如果生成的小球和之前的球发生碰撞,那么重新生成小球
ball.rect.left , ball.rect.top = randint (0,width-100) , randint(0,height-100)
balls.append(ball) #将所有的球对象添加到类表中,方便管理
clock = pygame.time.Clock() #生成刷新帧率控制器
while running :
for event in pygame.event.get():
if event.type == QUIT: #如果事件类型是退出
sys.exit()
elif event.type == GAMEOVER: #如果音乐结束事件类型为其返回的自定义事件游戏结束
loser_music.play() #播放失败的音乐
pygame.time.delay(2000) #延时2s
laugh_music.play() #播放大笑音效
running = False
elif event.type == MOUSEMOTION: #如果事件类型为鼠标移动
mouse_x , mouse_y = pygame.mouse.get_pos() #获取鼠标移动的当前位置
if (galss_rect.left <= mouse_x <= galss_rect.right) \
and (galss_rect.top <= mouse_y <= galss_rect.bottom): #如果鼠标在玻璃板内,那么flag=1
motion += 1 #如果鼠标在玻璃版内滑动,那么motion+1
flag = True
else : #不再范围内鼠标可见
flag = False
elif event.type == MYTIMER: #如果事件类型为自己的定义的定时器事件
for each in balls : #遍历所有的球
if each.check(motion): #调用他们各自的check()函数,看是否打到控制要求的目标,如果达到要求,即返回值为真
each.drawball() #调用球类中的画图方法将灰色球划成绿色球
each.speed = [0,0] #将他们的速度设为0,等待人类的控制
motion = 0 #等到所有的球都进行判断之后将motion重新设为0,进行下一秒的循环
elif event.type == KEYDOWN : #如果时间类型是键盘上的键按下
if event.key == K_UP or event.key == K_w : #如果按下的键是向上键或者w键
for each in balls : #遍历所有的小球 查看他们的control属性
if each.control : #如果control属性为真,即小球可控
each.speed[1] -= 1 #每按一下就减一 营造加速度的效果 下面的类似
if event.key == K_DOWN or event.key == K_s : #如果按下的键是向下键或者s键
for each in balls : #遍历所有的小球 查看他们的control属性
if each.control : #如果control属性为真,即小球可控
each.speed[1] += 1
if event.key == K_LEFT or event.key == K_a : #如果按下的键是向左键或者a键
for each in balls : #遍历所有的小球 查看他们的control属性
if each.control : #如果control属性为真,即小球可控
each.speed[0] -= 1
if event.key == K_RIGHT or event.key == K_d : #如果按下的键是向右键或者d键
for each in balls : #遍历所有的小球 查看他们的control属性
if each.control : #如果control属性为真,即小球可控
each.speed[0] += 1
screen.blit(background, (0, 0)) #将背景画到screen上
screen.blit(galss,(galss_rect.left , galss_rect.top)) #将玻璃板绘制在screen上
if flag : #如果鼠标进入玻璃板
#设置鼠标不可见
mouse_x , mouse_y = pygame.mouse.get_pos() #获取鼠标移动的当前位置
pygame.mouse.set_visible(False) #原鼠标不可见
screen.blit(hand,(mouse_x,mouse_y)) #画上我们玻璃版内鼠标
else : #如果鼠标没进入玻璃板
pygame.mouse.set_visible(False) #原鼠标不可见
screen.blit(hand1,(mouse_x,mouse_y)) #画上我们自己的平常鼠标
for each in balls: #每个球进行移动并重新绘制
each.move()
screen.blit(each.image, each.rect)
for i in range (BALL_NUM) : #循环5个小球,分别判断这个小球有没有和另外四个小球发生碰撞
item = balls.pop(i) #因为是判断和其他四个小球,所以需要先将这个小球取出
if collide_check( item , balls ): #调用碰撞检测的函数,如果结果为真,也就是有发生碰撞的小球
item.speed[0] = - item.speed[0] #碰撞后向反方向运动
item.speed[1] = - item.speed[1]
balls.insert(i , item) #最后不要忘记把这个小球放回原位
pygame.display.flip()
clock.tick(30)
if __name__ == "__main__":
main()
这一部分需要特别讲解的就是:
在默认情况下,无论你是简单的按一下按键或是按住按键不放,
Pygame都只为你发送一个键盘按下的事件
不过我们可以通过 key 模块的 set_repeat() 方法来设置是否重复响应按下某个按键
pygame.key.set_repeat(delay, interval)
–delay 参数制动第一次发送事件的延迟时间
–interval 参数指定重复发送事件的时间间隔
–如果不带任何参数,表示取消重复发送事件
这一部分成功了,接下来就是解决当小球发生碰撞时,无论你是绿色还是灰色,都会失控并脱离控制状态,这就只需要在检测到碰撞时把 control 属性设置为 False。
for i in range (BALL_NUM) : #循环5个小球,分别判断这个小球有没有和另外四个小球发生碰撞
item = balls.pop(i) #因为是判断和其他四个小球,所以需要先将这个小球取出
if collide_check( item , balls ): #调用碰撞检测的函数,如果结果为真,也就是有发生碰撞的小球
item.speed[0] = - item.speed[0] #碰撞后向反方向运动
item.speed[1] = - item.speed[1]
item.control = False #发生碰撞后小球重新变成不可控
item.drawball() #调用小球的绘制函数,重新把小球画成灰色
balls.insert(i , item) #最后不要忘记把这个小球放回原位
这个很好搞定,只需要在碰撞判断中发生碰撞时,把球的 control 属性变为 False 即可,然后重新绘图 ,接下来我们要做的就是让小球在碰撞的时候获得一个新的随机速度,这将大大的加大游戏的难度,因为均匀的速度是可以测算出来的,但是如果是以很慢的速度碰撞,却以新的速度反弹,你就会躲闪不及。
所以我们仍然需要改一下碰撞检测这一部分的代码:
但是如果我们这样子改,就会出现一个bug:
for i in range (BALL_NUM) : #循环5个小球,分别判断这个小球有没有和另外四个小球发生碰撞
item = balls.pop(i) #因为是判断和其他四个小球,所以需要先将这个小球取出
if collide_check( item , balls ): #调用碰撞检测的函数,如果结果为真,也就是有发生碰撞的小球
item.speed = [ randint(-10,10) , randint(-10,10) ]
item.control = False #发生碰撞后小球重新变成不可控
item.drawball() #调用小球的绘制函数,重新把小球画成灰色
balls.insert(i , item) #最后不要忘记把这个小球放回原位
就会出现有的两个小球碰撞之后,不停的来回碰撞,像新婚的小夫妻一样不愿意分开,如下图红框所示:
但是这样这就会让玩家感觉这个游戏像卡顿了一样,优化没做好,所以肯定是要解决的!
解决一个东西之前呢,首先要知道产生这种情况的原因:
因为碰撞之后,我们随机给一个新的速度,这个速度是带方向的速度,如果两个小球碰撞之后得到的新的随机速度的方向是相像的,它们就会再次发生碰撞,然后速度再相像,再检测到碰撞。。。然后。。。。就一直纠缠不清了。直到获得的速度是反向的,并且速度的大小能够脱离彼此,才会分开。
既然知道了原因,那么解决方法也就不难说了。
解决这个问题的方法就是 将方向和速度这两个概念独立开来,大家注意到了,问题的根源在于我们得到的随机速度是带方向的,它可能获取一个向左的,也可能获取一个向右的,所以我们把问题细分,把方向和速度独立开,速度永远为正,只描述书速度的大小,而方向为 -1表示向左,+1 表示向右。把方向乘以速度得到带方向的速度。然后在每一次撞击的时候,它以撞击的速度反向移动一格,这两个小球就分开了,再获得一个随机的速度就可以了。
我们来尝试是否能够实现:
from pygame.locals import *
from random import *
import traceback
import pygame
import math
import time
import sys
class Ball(pygame.sprite.Sprite) : #继承动画精灵基类
def __init__ (self,grayball_image,greenball_image,position,speed,bg_size,target) :
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load(grayball_image).convert_alpha()
self.grayball_image = grayball_image
self.greenball_image = greenball_image
self.rect = self.image.get_rect() #获得球的尺寸
self.rect.left , self.rect.top = position #将出现的位置赋给球
self.direction = [ speed[0]/abs(speed[0]) , speed[1]/abs(speed[1]) ] #小球当前运动的方向
self.speed = [ abs(speed[0]) , abs(speed[1]) ] #设置速度大小
self.colide = False #设置是否发生碰撞属性
self.target = target #设置一个使小球变为可控的目标
self.control = False #小球是否人为可控的标志
self.width , self.height = bg_size[0] , bg_size[1] #获得活动边界,就是背景的边界
def drawball(self):
if self.control : #如果小球可用,control=1
self.image = pygame.image.load(self.greenball_image) #重新绘制小球为绿色的小球
else : #否则绘制灰色小球
self.image = pygame.image.load(self.grayball_image)
def move(self):
self.rect = self.rect.move(self.direction[0]*self.speed[0] , self.direction[0]*self.speed[1]) #根据自身的速度进行移动
if self.rect.right < 0: #图片的右边已经超出边界的左边,即整个球已经出界
self.rect.left = self.width #让他从右边界回来
if self.rect.bottom < 0: #图片的底已经超出边界的上面
self.rect.top = self.height #让他从底部回来
if self.rect.left > self.width: #图片的左边已经超出边界的右边
self.rect.right = 0 #让他从左边回来
if self.rect.top > self.height: #如果图片的顶部已经超出边界的底部
self.rect.bottom = 0 #让他从顶部回来
def check(self,motion): #检查鼠标移动的频率是否打到控制小球的目标值
if self.target - 4 <= motion <= self.target + 4 :
self.control = True #小球可控标志设为1
return True
else :
return False
#判断碰撞检测函数
def collide_check(item,target):
col_balls = [] #添加碰撞小球
for each in target: #对 target 中所有的目标小球进行检测
#两个球心之间的距离
distance = math.sqrt( math.pow( (item.rect.center[0] - each.rect.center[0]) , 2 ) + \
math.pow( (item.rect.center[1] - each.rect.center[1]) , 2) )
if distance <= ( item.rect.width + each.rect.width ) / 2: #如果距离小于等于两者间的半径之和也就是两个直径之和的一半
col_balls.append(each) #将这个发生碰撞的小球添加到列表中
return col_balls
def main() :
pygame.init() #初始化
#将所有图片的路径写入
bg_image = r"D:\Code\Python\Pygame\pygame9:摩擦摩擦\background.png" #背景图
grayball_image = r"D:\Code\Python\Pygame\pygame9:摩擦摩擦\gray_ball.png" #灰小球的图片
greenball_image = r"D:\Code\Python\Pygame\pygame9:摩擦摩擦\green_ball.png" #绿小球的图片
galss_image = r"D:\Code\Python\Pygame\pygame9:摩擦摩擦\glass.png" #玻璃板图片
hand_image = r"D:\Code\Python\Pygame\pygame9:摩擦摩擦\hand.png" #鼠标在玻璃板上的样子
hand1_image = r"D:\Code\Python\Pygame\pygame9:摩擦摩擦\hand1.png" #鼠标在其他地方的样子
running = True #为了以后而已有多种方法退出程序
#加载背景音乐
pygame.mixer.music.load(r"D:\Code\Python\Pygame\pygame8:播放声音和音效\bg_music.ogg")
pygame.mixer.music.set_volume(0.2) #设置音量
pygame.mixer.music.play() #播放背景音乐
#设置一个背景音乐完毕之后的结束事件
GAMEOVER = USEREVENT
pygame.mixer.music.set_endevent(GAMEOVER) #当音乐播完后,发送一个GAMEOVER事件
#加载四个音效
hole_music = pygame.mixer.Sound(r"D:\Code\Python\Pygame\pygame9:摩擦摩擦\loser.wav")
laugh_music = pygame.mixer.Sound(r"D:\Code\Python\Pygame\pygame9:摩擦摩擦\laugh.wav")
winner_music = pygame.mixer.Sound(r"D:\Code\Python\Pygame\pygame9:摩擦摩擦\winner.wav")
loser_music = pygame.mixer.Sound(r"D:\Code\Python\Pygame\pygame9:摩擦摩擦\loser.wav")
#设置背景
bg_size = width , height = 1024 , 681 #背景大小
screen = pygame.display.set_mode(bg_size) # 设置背景大小
background = pygame.image.load(bg_image).convert_alpha() #画背景
#绘制用于摩擦的玻璃板,要花在小球前面,不然的话,后面小球会从玻璃板的下方划过
galss = pygame.image.load(galss_image).convert_alpha() #画玻璃板
galss_rect = galss.get_rect() #获得玻璃板的尺寸
galss_rect.left , galss_rect.top = ((width-galss_rect[2])/2 , (height-galss_rect[3])) #玻璃板的位置
balls = []
#加载鼠标图片
hand = pygame.image.load(hand_image).convert_alpha() #画玻璃版内鼠标
hand1 = pygame.image.load(hand1_image).convert_alpha() #画平常鼠标
flag = False #设置一个变量,用来表示鼠标是否进入玻璃板范围
#设置响应键盘连续输入
pygame.key.set_repeat(100,100)
#用来计数一秒钟内移动的次数
motion = 0
#设置一个自定义事件,用来检测鼠标移动的值是否符合控制小球的目标值
MYTIMER = USEREVENT + 1 #因为之前已经定义了一个自定义事件,所以根据之前说的这个自定义事件应该是之前的加1
pygame.time.set_timer(MYTIMER,1000) #计时器事件为1s
# 创建五个小球
BALL_NUM = 5
for i in range (BALL_NUM) : #生成5个球
position = randint (0,width-100) , randint(0,height-100) #要减去100是因为球图片尺寸的大小为100,随机生成位置
#speed = [ randint (-10,10) , randint(-10,10) ] 因为这种方法会生成新bug有的时候速度生成值为0所以使用新的方法
speed = [choice([-1,1])*randint(1,10),choice([-1,1])*randint(1,10)] #随机生成速度
ball = Ball(grayball_image,greenball_image,position,speed,bg_size,5*(i+1)) #生成球的对象
while collide_check(ball,balls): #如果生成的小球和之前的球发生碰撞,那么重新在随机位置生成小球
ball.rect.left , ball.rect.top = randint (0,width-100) , randint(0,height-100)
balls.append(ball) #将所有的球对象添加到类表中,方便管理
clock = pygame.time.Clock() #生成刷新帧率控制器
while running :
for event in pygame.event.get():
if event.type == QUIT: #如果事件类型是退出
sys.exit()
elif event.type == GAMEOVER: #如果音乐结束事件类型为其返回的自定义事件游戏结束
loser_music.play() #播放失败的音乐
pygame.time.delay(2000) #延时2s
laugh_music.play() #播放大笑音效
running = False
elif event.type == MOUSEMOTION: #如果事件类型为鼠标移动
mouse_x , mouse_y = pygame.mouse.get_pos() #获取鼠标移动的当前位置
if (galss_rect.left <= mouse_x <= galss_rect.right) \
and (galss_rect.top <= mouse_y <= galss_rect.bottom): #如果鼠标在玻璃板内,那么flag=1
motion += 1 #如果鼠标在玻璃版内滑动,那么motion+1
flag = True
else : #不再范围内鼠标可见
flag = False
elif event.type == MYTIMER: #如果事件类型为自己的定义的定时器事件
for each in balls : #遍历所有的球
if each.check(motion): #调用他们各自的check()函数,看是否打到控制要求的目标,如果达到要求,即返回值为真
each.drawball() #调用球类中的画图方法将灰色球划成绿色球
each.speed = [0,0] #将他们的速度设为0,等待人类的控制
motion = 0 #等到所有的球都进行判断之后将motion重新设为0,进行下一秒的循环
elif event.type == KEYDOWN : #如果时间类型是键盘上的键按下
if event.key == K_UP or event.key == K_w : #如果按下的键是向上键或者w键
for each in balls : #遍历所有的小球 查看他们的control属性
if each.control : #如果control属性为真,即小球可控
each.speed[1] -= 1 #每按一下就减一 营造加速度的效果 下面的类似
if event.key == K_DOWN or event.key == K_s : #如果按下的键是向下键或者s键
for each in balls : #遍历所有的小球 查看他们的control属性
if each.control : #如果control属性为真,即小球可控
each.speed[1] += 1
if event.key == K_LEFT or event.key == K_a : #如果按下的键是向左键或者a键
for each in balls : #遍历所有的小球 查看他们的control属性
if each.control : #如果control属性为真,即小球可控
each.speed[0] -= 1
if event.key == K_RIGHT or event.key == K_d : #如果按下的键是向右键或者d键
for each in balls : #遍历所有的小球 查看他们的control属性
if each.control : #如果control属性为真,即小球可控
each.speed[0] += 1
screen.blit(background, (0, 0)) #将背景画到screen上
screen.blit(galss,(galss_rect.left , galss_rect.top)) #将玻璃板绘制在screen上
if flag : #如果鼠标进入玻璃板
#设置鼠标不可见
mouse_x , mouse_y = pygame.mouse.get_pos() #获取鼠标移动的当前位置
pygame.mouse.set_visible(False) #原鼠标不可见
screen.blit(hand,(mouse_x,mouse_y)) #画上我们玻璃版内鼠标
else : #如果鼠标没进入玻璃板
pygame.mouse.set_visible(False) #原鼠标不可见
screen.blit(hand1,(mouse_x,mouse_y)) #画上我们自己的平常鼠标
for each in balls: #每个球进行移动并重新绘制
each.move()
if each.colide : #如果这个小球刚发生过碰撞
each.speed = [randint(3,10),randint(3,10)] #改变速度
each.colide = False
screen.blit(each.image, each.rect)
for i in range (BALL_NUM) : #循环5个小球,分别判断这个小球有没有和另外四个小球发生碰撞
item = balls.pop(i) #因为是判断和其他四个小球,所以需要先将这个小球取出
if collide_check( item , balls ): #调用碰撞检测的函数,如果结果为真,也就是有发生碰撞的小球
item.colide = True #小球碰撞属性设为真
item.direction[0] = - item.direction[0] #小球的运动方向反向,然后速度随机
item.direction[1] = - item.direction[1]
if item.control:
item.direction[0] = -1
item.direction[1] = -1
item.control = False
item.drawball() #调用小球的绘制函数,重新把小球画成灰色
balls.insert(i , item) #最后不要忘记把这个小球放回原位
pygame.display.flip()
clock.tick(30)
if __name__ == "__main__":
main()
上面的问题是解决了,可是我们发现有的时候 两个绿色的小球在键盘的操控下,居然向着相反的方向移动,这是为什么呢?
新的Bug 出现了,控制权交到玩家手中的时候,小球并不能正确的按照玩家的操纵去移动。我遇到的就是按下 A 的时候,两个绿色小球一个向左,一个向右。
其实在现实的开发中你常常会遇到这样的事情:好不容易补完一个Bug,或者说添加一个新的功能进去,直接影响了原来的程序逻辑,导致了另一个Bug甚至一些Bug的出现,那么既然有Bug,我们就应该及时的补上。
为什么会导致玩家的操作无法正确的控制小球呢?
原来是我们将带方向的速度拆分为方向和速度,而响应玩家的按键这里我们依然认为速度是带方向的,所以说你这里尽管说是减到0 了,它还是会继续减,而move() 方法这里写的是方向乘以速度,如果我的速度减到变为负数,方向为负数,那得到的却是一个反方向的移动的效果。
为了保留玩家操控小球是带加速度的效果,我们对于操控绿色小球这一部分的代码就不能怎么去修改,如果硬要去改的话,就会使得代码变得更加麻烦,逻辑上变得更加复杂,所以我们这里不妨将小球的移动给区分开:
随机移动的话就按照方向乘以速度,而由玩家操控的移动我们还是保留带方向的速度。
那应该怎么做呢?
我们在move这里对小球进行修改,
def move(self):
if self.control: #如果小球被控制
self.rect = self.rect.move(self.speed) #根据自己的速度移动
else:
self.rect = self.rect.move((self.direction[0] * self.speed[0],\
self.direction[1] * self.speed[1])) #没被控制的小球则要乘上方向
if self.rect.right < 0: #图片的右边已经超出边界的左边,即整个球已经出界
self.rect.left = self.width #让他从右边界回来
if self.rect.bottom < 0: #图片的底已经超出边界的上面
self.rect.top = self.height #让他从底部回来
if self.rect.left > self.width: #图片的左边已经超出边界的右边
self.rect.right = 0 #让他从左边回来
if self.rect.top > self.height: #如果图片的顶部已经超出边界的底部
self.rect.bottom = 0 #让他从顶部回来
def check(self,motion): #检查鼠标移动的频率是否打到控制小球的目标值
if self.target - 4 <= motion <= self.target + 4 :
self.control = True #小球可控标志设为1
return True
else :
return False
然后还需要对个绿球的相撞进行修改,
for i in range (BALL_NUM) : #循环5个小球,分别判断这个小球有没有和另外四个小球发生碰撞
item = balls.pop(i) #因为是判断和其他四个小球,所以需要先将这个小球取出
if collide_check( item , balls ): #调用碰撞检测的函数,如果结果为真,也就是有发生碰撞的小球
item.colide = True #小球碰撞属性设为真
item.direction[0] = - item.direction[0] #小球的运动方向反向,然后速度随机
item.direction[1] = - item.direction[1]
if item.control:
item.direction[0] = -1
item.direction[1] = -1
item.control = False
item.drawball() #调用小球的绘制函数,重新把小球画成灰色
balls.insert(i , item) #最后不要忘记把这个小球放回原位
接下来,当绿色的小球移动到黑洞的正上方的时候,只要玩家点击键盘上的空格键,就会提示一个音效,并且填入黑洞中,此后其它小球就会忽略它,直接从它上面飘过,无视它的存在,音乐结束之前,如果你能够把所有的小球都填入到各个黑洞中,那么游戏胜利。
这里有两点需要注意的:
1、每个黑洞只能填入一个绿色的小球。
2、当小球填入黑洞时,其它小球是从它上方飘过,而不是从下方。
首先,我们5个黑洞的位置已经定义好了,我在作图的时候,已经把位置量好了。由于如果要100%命中太难了,所以我们保留了一定活动的范围,也就是说,代码给出的范围比黑洞实际范围大一点。
最终完整代码如下:
from pygame.locals import *
from random import *
import traceback
import pygame
import math
import time
import sys
class Ball(pygame.sprite.Sprite) : #继承动画精灵基类
def __init__ (self,grayball_image,greenball_image,position,speed,bg_size,target) :
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load(grayball_image).convert_alpha()
self.grayball_image = grayball_image
self.greenball_image = greenball_image
self.rect = self.image.get_rect() #获得球的尺寸
self.rect.left , self.rect.top = position #将出现的位置赋给球
self.direction = [ speed[0]/abs(speed[0]) , speed[1]/abs(speed[1]) ] #小球当前运动的方向
self.speed = [ abs(speed[0]) , abs(speed[1]) ] #设置速度大小
self.colide = False #设置是否发生碰撞属性
self.hole = False #设置是否进黑洞
self.target = target #设置一个使小球变为可控的目标
self.control = False #小球是否人为可控的标志
self.width , self.height = bg_size[0] , bg_size[1] #获得活动边界,就是背景的边界
def drawball(self):
if self.control : #如果小球可用,control=1
self.image = pygame.image.load(self.greenball_image) #重新绘制小球为绿色的小球
else : #否则绘制灰色小球
self.image = pygame.image.load(self.grayball_image)
def move(self):
if not self.hole : #如果小球没有进过黑洞
if self.control: #如果小球被控制
self.rect = self.rect.move(self.speed) #根据自己的速度移动
else:
self.rect = self.rect.move((self.direction[0] * self.speed[0],\
self.direction[1] * self.speed[1])) #没被控制的小球则要乘上方向
if self.rect.right < 0: #图片的右边已经超出边界的左边,即整个球已经出界
self.rect.left = self.width #让他从右边界回来
if self.rect.bottom < 0: #图片的底已经超出边界的上面
self.rect.top = self.height #让他从底部回来
if self.rect.left > self.width: #图片的左边已经超出边界的右边
self.rect.right = 0 #让他从左边回来
if self.rect.top > self.height: #如果图片的顶部已经超出边界的底部
self.rect.bottom = 0 #让他从顶部回来
def check(self,motion): #检查鼠标移动的频率是否打到控制小球的目标值
if self.target - 4 <= motion <= self.target + 4 :
self.control = True #小球可控标志设为1
return True
else :
return False
#判断碰撞检测函数
def collide_check(item,target):
col_balls = [] #添加碰撞小球
for each in target: #对 target 中所有的目标小球进行检测
#两个球心之间的距离
distance = math.sqrt( math.pow( (item.rect.center[0] - each.rect.center[0]) , 2 ) + \
math.pow( (item.rect.center[1] - each.rect.center[1]) , 2) )
if distance <= ( item.rect.width + each.rect.width ) / 2: #如果距离小于等于两者间的半径之和也就是两个直径之和的一半
col_balls.append(each) #将这个发生碰撞的小球添加到列表中
return col_balls
def main() :
pygame.init() #初始化
#将所有图片的路径写入
bg_image = r"D:\Code\Python\Pygame\pygame10:游戏胜利\background.png" #背景图
grayball_image = r"D:\Code\Python\Pygame\pygame10:游戏胜利\gray_ball.png" #灰小球的图片
greenball_image = r"D:\Code\Python\Pygame\pygame10:游戏胜利\green_ball.png" #绿小球的图片
galss_image = r"D:\Code\Python\Pygame\pygame10:游戏胜利\glass.png" #玻璃板图片
hand_image = r"D:\Code\Python\Pygame\pygame10:游戏胜利\hand.png" #鼠标在玻璃板上的样子
hand1_image = r"D:\Code\Python\Pygame\pygame10:游戏胜利\hand1.png" #鼠标在其他地方的样子
running = True #为了以后而已有多种方法退出程序
# 存放要打印的消息
msgs = []
#加载背景音乐
pygame.mixer.music.load(r"D:\Code\Python\Pygame\pygame10:游戏胜利\bg_music.ogg")
pygame.mixer.music.set_volume(0.2) #设置音量
pygame.mixer.music.play() #播放背景音乐
#设置一个背景音乐完毕之后的结束事件
GAMEOVER = USEREVENT
pygame.mixer.music.set_endevent(GAMEOVER) #当音乐播完后,发送一个GAMEOVER事件
#加载四个音效
hole_music = pygame.mixer.Sound(r"D:\Code\Python\Pygame\pygame10:游戏胜利\hole.wav")
laugh_music = pygame.mixer.Sound(r"D:\Code\Python\Pygame\pygame10:游戏胜利\laugh.wav")
winner_music = pygame.mixer.Sound(r"D:\Code\Python\Pygame\pygame10:游戏胜利\winner.wav")
loser_music = pygame.mixer.Sound(r"D:\Code\Python\Pygame\pygame10:游戏胜利\loser.wav")
#设置背景
bg_size = width , height = 1024 , 681 #背景大小
screen = pygame.display.set_mode(bg_size) # 设置背景大小
background = pygame.image.load(bg_image).convert_alpha() #画背景
#绘制用于摩擦的玻璃板,要花在小球前面,不然的话,后面小球会从玻璃板的下方划过
galss = pygame.image.load(galss_image).convert_alpha() #画玻璃板
galss_rect = galss.get_rect() #获得玻璃板的尺寸
galss_rect.left , galss_rect.top = ((width-galss_rect[2])/2 , (height-galss_rect[3])) #玻璃板的位置
balls = []
group = [] #会发生碰撞的小球
#加载鼠标图片
hand = pygame.image.load(hand_image).convert_alpha() #画玻璃版内鼠标
hand1 = pygame.image.load(hand1_image).convert_alpha() #画平常鼠标
flag = False #设置一个变量,用来表示鼠标是否进入玻璃板范围
#设置响应键盘连续输入
pygame.key.set_repeat(100,100)
#用来计数一秒钟内移动的次数
motion = 0
#设置一个自定义事件,用来检测鼠标移动的值是否符合控制小球的目标值
MYTIMER = USEREVENT + 1 #因为之前已经定义了一个自定义事件,所以根据之前说的这个自定义事件应该是之前的加1
pygame.time.set_timer(MYTIMER,1000) #计时器事件为1s
# 创建五个小球
BALL_NUM = 5
#地图上的黑洞的坐标,因为 100% 命中太难,所以只要在范围内即可
# 每个元素:(x1, x2, y1, y2)
hole = [(117, 119, 199, 201), (225, 227, 390, 392), \
(503, 505, 320, 322), (698, 700, 192, 194), \
(906, 908, 419, 421)]
for i in range (BALL_NUM) : #生成5个球
position = randint (0,width-100) , randint(0,height-100) #要减去100是因为球图片尺寸的大小为100,随机生成位置
#speed = [ randint (-10,10) , randint(-10,10) ] 因为这种方法会生成新bug有的时候速度生成值为0所以使用新的方法
speed = [choice([-1,1])*randint(1,10),choice([-1,1])*randint(1,10)] #随机生成速度
ball = Ball(grayball_image,greenball_image,position,speed,bg_size,5*(i+1)) #生成球的对象
while collide_check(ball,balls): #如果生成的小球和之前的球发生碰撞,那么重新在随机位置生成小球
ball.rect.left , ball.rect.top = randint (0,width-100) , randint(0,height-100)
balls.append(ball) #将所有的球对象添加到列表中,方便管理
group.append(ball) #将所有的球对象添加到会碰撞的小球中,方便管理
clock = pygame.time.Clock() #生成刷新帧率控制器
while running :
for event in pygame.event.get():
if event.type == QUIT: #如果事件类型是退出
sys.exit()
elif event.type == GAMEOVER: #如果音乐结束事件类型为其返回的自定义事件游戏结束
loser_music.play() #播放失败的音乐
pygame.time.delay(2000) #延时2s
laugh_music.play() #播放大笑音效
running = False
elif event.type == MOUSEMOTION: #如果事件类型为鼠标移动
mouse_x , mouse_y = pygame.mouse.get_pos() #获取鼠标移动的当前位置
if (galss_rect.left <= mouse_x <= galss_rect.right) \
and (galss_rect.top <= mouse_y <= galss_rect.bottom): #如果鼠标在玻璃板内,那么flag=1
motion += 1 #如果鼠标在玻璃版内滑动,那么motion+1
flag = True
else : #不再范围内鼠标可见
flag = False
elif event.type == MYTIMER: #如果事件类型为自己的定义的定时器事件
for each in balls : #遍历所有的球
if each.check(motion): #调用他们各自的check()函数,看是否打到控制要求的目标,如果达到要求,即返回值为真
each.drawball() #调用球类中的画图方法将灰色球划成绿色球
each.speed = [0,0] #将他们的速度设为0,等待人类的控制
motion = 0 #等到所有的球都进行判断之后将motion重新设为0,进行下一秒的循环
elif event.type == KEYDOWN : #如果时间类型是键盘上的键按下
if event.key == K_UP or event.key == K_w : #如果按下的键是向上键或者w键
for each in balls : #遍历所有的小球 查看他们的control属性
if each.control : #如果control属性为真,即小球可控
each.speed[1] -= 1 #每按一下就减一 营造加速度的效果 下面的类似
if event.key == K_DOWN or event.key == K_s : #如果按下的键是向下键或者s键
for each in balls : #遍历所有的小球 查看他们的control属性
if each.control : #如果control属性为真,即小球可控
each.speed[1] += 1
if event.key == K_LEFT or event.key == K_a : #如果按下的键是向左键或者a键
for each in balls : #遍历所有的小球 查看他们的control属性
if each.control : #如果control属性为真,即小球可控
each.speed[0] -= 1
if event.key == K_RIGHT or event.key == K_d : #如果按下的键是向右键或者d键
for each in balls : #遍历所有的小球 查看他们的control属性
if each.control : #如果control属性为真,即小球可控
each.speed[0] += 1
if event.key == K_SPACE : #如果按下的是键盘上的空格键
for each in group :
if each.control : #遍历所有小球中被人为控制的球
for i in hole : #遍历所有黑洞
#如果被控制的小球位置在黑洞运行的范围内
if ((i[0] <= each.rect.left <= i[1]) and (i[2] <= each.rect.top <= i[3])) :
#播放黑洞音效
hole_music.play()
#小球速度变为0
each.speed = [0,0]
#进黑洞属性变为True
each.hole = True
#将这个小球从碰撞组中删除,这样其他球就不会撞他
group.remove(each)
#并将这个小球从之前的balls列表中删除,然后插到列表的最前面,这样其他球就会在他的上面飘过
temp = balls.pop(balls.index(each))
balls.insert(0,temp)
# 一个坑一个球
hole.remove(i)
# 坑都补完了,游戏结束
if not hole:
pygame.mixer.music.stop()
winner_music.play()
pygame.time.delay(3000)
# 打印“然并卵”
msg = pygame.image.load(r"D:\Code\Python\Pygame\pygame10:游戏胜利\win.png").convert_alpha()
msg_pos = (width - msg.get_width()) // 2, \
(height - msg.get_height()) // 2
msgs.append((msg, msg_pos))
laugh_music.play()
screen.blit(background, (0, 0)) #将背景画到screen上
screen.blit(galss,(galss_rect.left , galss_rect.top)) #将玻璃板绘制在screen上
if flag : #如果鼠标进入玻璃板
#设置鼠标不可见
mouse_x , mouse_y = pygame.mouse.get_pos() #获取鼠标移动的当前位置
pygame.mouse.set_visible(False) #原鼠标不可见
screen.blit(hand,(mouse_x,mouse_y)) #画上我们玻璃版内鼠标
else : #如果鼠标没进入玻璃板
pygame.mouse.set_visible(False) #原鼠标不可见
screen.blit(hand1,(mouse_x,mouse_y)) #画上我们自己的平常鼠标
for each in balls: #每个球进行移动并重新绘制
each.move()
if each.colide : #如果这个小球刚发生过碰撞
each.speed = [randint(3,10),randint(3,10)] #改变速度
each.colide = False
screen.blit(each.image, each.rect)
for i in range (len(group)) : #循环5个小球,分别判断这个小球有没有和另外四个小球发生碰撞
item = group.pop(i) #因为是判断和其他四个小球,所以需要先将这个小球取出
if collide_check( item , group ): #调用碰撞检测的函数,如果结果为真,也就是有发生碰撞的小球
item.colide = True #小球碰撞属性设为真
item.direction[0] = - item.direction[0] #小球的运动方向反向,然后速度随机
item.direction[1] = - item.direction[1]
if item.control:
item.direction[0] = -1
item.direction[1] = -1
item.control = False
item.drawball() #调用小球的绘制函数,重新把小球画成灰色
group.insert(i, item) #最后不要忘记把这个小球放回原位
#画上文字
for msg in msgs:
screen.blit(msg[0], msg[1])
#延时5秒关闭
pygame.time.delay(5000)
pygame.display.flip()
clock.tick(30)
if __name__ == "__main__":
# 这样做的好处是双击打开时如果出现异常可以报告异常,而不是一闪而过!
try:
main()
except SystemExit: #这是按下 × 的异常,直接忽略
pass
except:
traceback.print_exc()
pygame.quit()
input()
因为我本人是在VScode或者pycharm中运行了,所以我遇不到这种困扰,但是平时在IDLE里面执行,但是我们点 关闭(×)没有反应,有没有什么好的办法?
有的,你只需要在响应 QUIT 事件这里,加一条 pygame.quit() 即可。
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
还有一种情况就是:用户双击打开游戏文件,如果有逻辑错误或者代码错误,程序就不会给你报错,而是一打开就闪退。
我们也有解决方案:
就是添加捕获异常的模块 import traceback
然后:
if __name__ == "__main__":
# 这样做的好处是双击打开时如果出现异常可以报告异常,而不是一闪而过!
try:
main()
except SystemExit: #这是按下 × 的异常,直接忽略
pass
except:
traceback.print_exc()
pygame.quit()
input()
另外在这个的基础上,昨天对代码进行了更新,在最前加入了故事情节,然后附带机械键盘的敲击音效,故事情节结束后,弹出新的窗口进行难度等级的选择,
之后与原来没有什么区别:
代码这里就不贴了,太长占篇幅,感兴趣的请点击→ https://blog.csdn.net/qq_38970783/article/details/89318673
import pygame
import sys
import traceback
from pygame.locals import *
from random import *
# 球类继承自Spirte类
class Ball(pygame.sprite.Sprite):
def __init__(self, grayball_image, greenball_image, position, speed, bg_size, target):
# 初始化动画精灵
pygame.sprite.Sprite.__init__(self)
self.grayball_image = pygame.image.load(grayball_image).convert_alpha()
self.greenball_image = pygame.image.load(greenball_image).convert_alpha()
self.rect = self.grayball_image.get_rect()
# 将小球放在指定位置
self.rect.left, self.rect.top = position
self.side = [choice([-1, 1]), choice([-1, 1])]
self.speed = speed
self.collide = False
self.target = target
self.control = False
self.width, self.height = bg_size[0], bg_size[1]
self.radius = self.rect.width / 2
def move(self):
if self.control:
self.rect = self.rect.move(self.speed)
else:
self.rect = self.rect.move((self.side[0] * self.speed[0], \
self.side[1] * self.speed[1]))
# 如果小球的左侧出了边界,那么将小球左侧的位置改为右侧的边界
# 这样便实现了从左边进入,右边出来的效果
if self.rect.right <= 0:
self.rect.left = self.width
elif self.rect.left >= self.width:
self.rect.right = 0
elif self.rect.bottom <= 0:
self.rect.top = self.height
elif self.rect.top >= self.height:
self.rect.bottom = 0
def check(self, motion):
if self.target < motion < self.target + 5:
return True
else:
return False
class Glass(pygame.sprite.Sprite):
def __init__(self, glass_image, mouse_image, bg_size):
# 初始化动画精灵
pygame.sprite.Sprite.__init__(self)
self.glass_image = pygame.image.load(glass_image).convert_alpha()
self.glass_rect = self.glass_image.get_rect()
self.glass_rect.left, self.glass_rect.top = \
(bg_size[0] - self.glass_rect.width) // 2, \
bg_size[1] - self.glass_rect.height
self.mouse_image = pygame.image.load(mouse_image).convert_alpha()
self.mouse_rect = self.mouse_image.get_rect()
self.mouse_rect.left, self.mouse_rect.top = \
self.glass_rect.left, self.glass_rect.top
pygame.mouse.set_visible(False)
def main():
pygame.init()
grayball_image = r"D:\Code\Python\Pygame\pygame10:游戏胜利\gray_ball.png"
greenball_image = r"D:\Code\Python\Pygame\pygame10:游戏胜利\green_ball.png"
glass_image = r"D:\Code\Python\Pygame\pygame10:游戏胜利\glass.png"
mouse_image = r"D:\Code\Python\Pygame\pygame10:游戏胜利\hand.png"
bg_image = r"D:\Code\Python\Pygame\pygame10:游戏胜利\background.png"
running = True
# 添加魔性的背景音乐
pygame.mixer.music.load(r"D:\Code\Python\Pygame\pygame10:游戏胜利\bg_music.ogg")
pygame.mixer.music.play()
# 添加音效
loser_sound = pygame.mixer.Sound(r"D:\Code\Python\Pygame\pygame10:游戏胜利\loser.wav")
laugh_sound = pygame.mixer.Sound(r"D:\Code\Python\Pygame\pygame10:游戏胜利\laugh.wav")
winner_sound = pygame.mixer.Sound(r"D:\Code\Python\Pygame\pygame10:游戏胜利\winner.wav")
hole_sound = pygame.mixer.Sound(r"D:\Code\Python\Pygame\pygame10:游戏胜利\hole.wav")
# 音乐播放完时游戏结束
GAMEOVER = USEREVENT
pygame.mixer.music.set_endevent(GAMEOVER)
# 根据背景图片指定游戏界面尺寸
bg_size = width, height = 1024, 681
screen = pygame.display.set_mode(bg_size)
pygame.display.set_caption("Play the ball - FishC Demo")
background = pygame.image.load(bg_image).convert_alpha()
# 5 个坑的范围,因为 100% 命中太难,所以只要在范围内即可
# 每个元素:(x1, x2, y1, y2)
hole = [(117, 119, 199, 201), (225, 227, 390, 392), \
(503, 505, 320, 322), (698, 700, 192, 194), \
(906, 908, 419, 421)]
# 存放要打印的消息
msgs = []
# 用来存放小球对象的列表
balls = []
group = pygame.sprite.Group()
# 创建 5 个小球
for i in range(5):
# 位置随机,速度随机
position = randint(0, width-100), randint(0, height-100)
speed = [randint(1, 10), randint(1, 10)]
ball = Ball(grayball_image, greenball_image, position, speed, bg_size, 5 * (i+1))
# 检测新诞生的球是否会卡住其他球
while pygame.sprite.spritecollide(ball, group, False, pygame.sprite.collide_circle):
ball.rect.left, ball.rect.top = randint(0, width-100), randint(0, height-100)
balls.append(ball)
group.add(ball)
# 生成摩擦摩擦的玻璃面板
glass = Glass(glass_image, mouse_image, bg_size)
# motion 记录鼠标在玻璃面板产生的事件数量
motion = 0
# 1 秒检查 1 次鼠标摩擦摩擦产生的事件数量
MYTIMER = USEREVENT + 1
pygame.time.set_timer(MYTIMER, 1000)
# 设置持续按下键盘的重复响应
pygame.key.set_repeat(100, 100)
clock = pygame.time.Clock()
print(balls)
print(group)
while running:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
# 游戏失败
elif event.type == GAMEOVER:
loser_sound.play()
pygame.time.delay(2000)
laugh_sound.play()
running = False
# 1 秒检查 1 次鼠标摩擦摩擦产生的事件数量
elif event.type == MYTIMER:
if motion:
for each in group:
if each.check(motion):
each.speed = [0, 0]
each.control = True
motion = 0
elif event.type == MOUSEMOTION:
motion += 1
# 当小球的 control 属性为 True 时
# 可是使用按键 w、s、a、d 分别上、下、左、右移动小球
# 带加速度的哦^_^
elif event.type == KEYDOWN:
if event.key == K_w:
for each in group:
if each.control:
each.speed[1] -= 1
if event.key == K_s:
for each in group:
if each.control:
each.speed[1] += 1
if event.key == K_a:
for each in group:
if each.control:
each.speed[0] -= 1
if event.key == K_d:
for each in group:
if each.control:
each.speed[0] += 1
if event.key == K_SPACE:
# 判断小球是否在坑内
for each in group:
if each.control:
for i in hole:
if i[0] <= each.rect.left <= i[1] and \
i[2] <= each.rect.top <= i[3]:
# 播放音效
hole_sound.play()
each.speed = [0, 0]
# 从 group 中移出,这样其他球就会忽视它
group.remove(each)
# 放到 balls 列表中的最前,也就是第一个绘制的球
# 这样当球在坑里时,其它球会从它上边过去,而不是下边
temp = balls.pop(balls.index(each))
balls.insert(0, temp)
# 一个坑一个球
hole.remove(i)
# 坑都补完了,游戏结束
if not hole:
pygame.mixer.music.stop()
winner_sound.play()
pygame.time.delay(3000)
# 打印“然并卵”
msg = pygame.image.load("win.png").convert_alpha()
msg_pos = (width - msg.get_width()) // 2, \
(height - msg.get_height()) // 2
msgs.append((msg, msg_pos))
laugh_sound.play()
screen.blit(background, (0, 0))
screen.blit(glass.glass_image, glass.glass_rect)
# 限制鼠标只能在玻璃内摩擦摩擦
glass.mouse_rect.left, glass.mouse_rect.top = pygame.mouse.get_pos()
if glass.mouse_rect.left < glass.glass_rect.left:
glass.mouse_rect.left = glass.glass_rect.left
if glass.mouse_rect.left > glass.glass_rect.right - glass.mouse_rect.width:
glass.mouse_rect.left = glass.glass_rect.right - glass.mouse_rect.width
if glass.mouse_rect.top < glass.glass_rect.top:
glass.mouse_rect.top = glass.glass_rect.top
if glass.mouse_rect.top > glass.glass_rect.bottom - glass.mouse_rect.height:
glass.mouse_rect.top = glass.glass_rect.bottom - glass.mouse_rect.height
screen.blit(glass.mouse_image, glass.mouse_rect)
for each in balls:
each.move()
if each.collide:
each.speed = [randint(1, 10), randint(1, 10)]
each.collide = False
if each.control:
screen.blit(each.greenball_image, each.rect)
else:
screen.blit(each.grayball_image, each.rect)
for each in group:
# 先从组中移出当前球
group.remove(each)
# 判断当前球与其他球是否相撞
if pygame.sprite.spritecollide(each, group, False, pygame.sprite.collide_circle):
each.side[0] = -each.side[0]
each.side[1] = -each.side[1]
each.collide = True
if each.control:
each.side[0] = -1
each.side[1] = -1
each.control = False
# 将当前球添加回组中
group.add(each)
for msg in msgs:
screen.blit(msg[0], msg[1])
pygame.display.flip()
clock.tick(30)
if __name__ == "__main__":
# 这样做的好处是双击打开时如果出现异常可以报告异常,而不是一闪而过!
try:
main()
except SystemExit:
pass
except:
traceback.print_exc()
pygame.quit()
input()