第088讲: Pygame:摩擦摩擦 | 学习记录(小甲鱼零基础入门学习Python)

现在 Play The Ball 这个小游戏现在已经有了背景音乐,有了小球,有了碰撞检测,接下来我们要做的就是摩擦摩擦。

我们有一块玻璃面板的图片,如下图所示:这些是我的图片素材。
在这里插入图片描述 在这里插入图片描述 green
在这里,因为再看了一点之后就自己动手去做了,所以没有使用小甲鱼的方法——建一个glass类来实现,而是直接绘画glass来实现,就和绘制背景一样。

现在我们的要求就是把这块玻璃面板加载到程序底部的中央位置,这个还是很好实现的。

from pygame.locals import *
from random import *
import pygame
import math
import time
import sysclass Ball(pygame.sprite.Sprite) :  #继承动画精灵基类def __init__ (self,grayball_image,position,speed,bg_size) :pygame.sprite.Sprite.__init__(self)self.image = pygame.image.load(grayball_image).convert_alpha()   self.rect = self.image.get_rect()   #获得球的尺寸self.rect.left , self.rect.top = position   #将出现的位置赋给球self.speed = speed  #设置速度self.width , self.height = bg_size[0] , bg_size[1]  #获得活动边界,就是背景的边界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 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_ballsdef main() :pygame.init()   #初始化#将所有图片的路径写入bg_image = r"D:\Code\Python\Pygame\pygame9:摩擦摩擦\background.png"    #背景图grayball_image = r"D:\Code\Python\Pygame\pygame9:摩擦摩擦\gray_ball.png"   #灰小球的图片galss_image = r"D:\Code\Python\Pygame\pygame9:摩擦摩擦\glass.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 = USEREVENTpygame.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 = []# 创建五个小球BALL_NUM = 5for 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,position,speed,bg_size)  #生成球的对象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()  #播放失败的音乐time.sleep(2)    #延时2slaugh_music.play()  #播放大笑音效running = False  screen.blit(background, (0, 0)) #将背景画到screen上screen.blit(galss,(galss_rect.left , galss_rect.top))    #将玻璃板绘制在screen上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 默认的鼠标非常难看,又小又黑又土,而作为一个游戏的话,漂亮的光标往往就是决胜的关键,游戏开发注重的就是细节,现在我们就用自定义的光标图片替换原来又小又黑又土的光标。如图·所示:
在这里插入图片描述 在这里插入图片描述
前者为在窗口其他位置的样子,后者为在玻璃板上的样子

关于自定义鼠标的光标:

我们的做法就是使用 set_visible() 方法来将光标设为不可见,然后使用 get_pos() 方法获取鼠标的实时位置,获取之后,我们实时的把 hand.png 图片画到这个位置上去,这样子就相当于实现了自定义光标。

pygame.mouse.set_visible() —— 隐藏或显示鼠标光标
pygame.mouse.get_pos() —— 获取鼠标光标的位置

代码实现:

from pygame.locals import *
from random import *
import pygame
import math
import time
import sysclass Ball(pygame.sprite.Sprite) :  #继承动画精灵基类def __init__ (self,grayball_image,position,speed,bg_size,target) :pygame.sprite.Sprite.__init__(self)self.image = pygame.image.load(grayball_image).convert_alpha()   self.rect = self.image.get_rect()   #获得球的尺寸self.rect.left , self.rect.top = position   #将出现的位置赋给球self.speed = speed  #设置速度self.width , self.height = bg_size[0] , bg_size[1]  #获得活动边界,就是背景的边界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 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_ballsdef main() :pygame.init()   #初始化#将所有图片的路径写入bg_image = r"D:\Code\Python\Pygame\pygame9:摩擦摩擦\background.png"    #背景图grayball_image = r"D:\Code\Python\Pygame\pygame9:摩擦摩擦\gray_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 = USEREVENTpygame.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    #设置一个变量,用来表示鼠标是否进入玻璃板范围# 创建五个小球BALL_NUM = 5for 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,position,speed,bg_size,6*(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()  #播放失败的音乐time.sleep(2)    #延时2slaugh_music.play()  #播放大笑音效running = Falseelif 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=1flag = True else :  #不再范围内鼠标可见flag = Falsescreen.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()

在这里插入图片描述 在这里插入图片描述
下一步,让小球响应光标的移动频率。

我们需要让小球响应光标一定频率的摩擦摩擦而停下来变成绿色,就是说,鼠标在玻璃面板上摩擦,达到一定频率的时候,某一个匹配的小球会停下来,而不是所有小球,停下来的小球就接受你的控制啦。

我们知道,鼠标的一定会产生事件,我们可以利用这一点。假设单位时间是一秒,我们检测一秒内鼠标在玻璃面板内产生多少事件,从而判定每一秒产生的事件数是否能够匹配到某一个小球需要的要求。

步骤如下:

1、为每个小球设定一个不同的目标;

2、创建一个motion 变量来记录每一秒钟产生事件数量;

3、为小球添加一个 check() 方法,用于判断鼠标在1秒钟内产生的事件数量是否匹配此目标;

4、添加一个自定义事件,每一秒钟触发一次。调用每个小球的 check() 检测 motion 的值是否匹配某一个小球的目标,并将motion重新初始化,以便记录下1秒鼠标事件数量;

5、小球应该添加一个 control 属性,用于记录当前的状态(绿色 -> 玩家控制 or 灰色 -> 随机移动)。

6、通过检查 control 属性决定绘制什么颜色的小球。

代码实现:

from pygame.locals import *
from random import *
import pygame
import math
import time
import sysclass 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.rect = self.image.get_rect()   #获得球的尺寸self.rect.left , self.rect.top = position   #将出现的位置赋给球self.speed = speed  #设置速度self.target = target    #设置一个使小球变为可控的目标self.control = False    #小球是否人为可控的标志self.width , self.height = bg_size[0] , bg_size[1]  #获得活动边界,就是背景的边界def drawball(self,greenball_image):if self.control :   #如果小球可用,control=1self.image = pygame.image.load(greenball_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 - 2 <= motion <= self.target + 2 :self.control = True #小球可控标志设为1return Trueelse :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_ballsdef 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 = USEREVENTpygame.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    #设置一个变量,用来表示鼠标是否进入玻璃板范围#用来计数一秒钟内移动的次数motion = 0  #设置一个自定义事件,用来检测鼠标移动的值是否符合控制小球的目标值MYTIMER = USEREVENT + 1     #因为之前已经定义了一个自定义事件,所以根据之前说的这个自定义事件应该是之前的加1pygame.time.set_timer(MYTIMER,1000)     #计时器事件为1s  # 创建五个小球BALL_NUM = 5for 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()  #播放失败的音乐time.sleep(2)    #延时2slaugh_music.play()  #播放大笑音效running = Falseelif 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=1motion += 1     #如果鼠标在玻璃版内滑动,那么motion+1flag = True else :  #不再范围内鼠标可见flag = Falseelif event.type == MYTIMER:     #如果事件类型为自己的定义的定时器事件for each in balls : #遍历所有的球if each.check(motion):  #调用他们各自的check()函数,看是否打到控制要求的目标,如果达到要求,即返回值为真each.drawball(greenball_image)    #调用球类中的画图方法将灰色球划成绿色球each.speed = [0,0]  #将他们的速度设为0,等待人类的控制motion = 0    #等到所有的球都进行判断之后将motion重新设为0,进行下一秒的循环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()

在这里插入图片描述


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部