这节课我们来谈谈 Pygame 中的 播放声音和音效,因为几乎没有任何游戏是一声不吭的,多重的感官体验更能刺激玩家的神经,没有声音的游戏就好比 不蘸番茄的薯条,尽管如此,Pygame 对于声音的处理并不是太理想,我说的是如果你想用 Pygame 做一个炫酷的音乐播放器那可能会让你失望,因为Pygame对于声音格式的支持十分有限,不过对于游戏开发来说,这完全是足够的,我们需要的我们自己转换就可以了。
一般游戏来说,声音主要分为两种,一种是背景音乐,一种是音效。
背景音乐就是时刻伴随着游戏存在的,往往就是重复播放的一首曲子或者歌曲;音效就是在某种条件下被触发产生的,比如两个小球碰撞在一起就会发出 "啪啪啪" 的声音。
刚才我们也说了,Pygame 支持的声音格式十分有限,一般情况下我们使用 .ogg 的格式来做背景音乐,用无压缩的 .wvb 来作为音效。那么你拿到一个 .mp3 格式该怎么办呢?
你可以使用 格式工厂 这类的软件把它转为 .ogg 或者 .wvb 格式。
注意:music 模块虽然写了支持 .mp3 格式,但是它对 .mp3 格式的支持十分有限,经常你会在网上找到一段很好的 .mp3 的曲子,但是载入之后压根没有声音。你把它转为 .ogg 格式就可以很好的支持了。
•播放音效:
–pygame.mixer.Sound()
播放音效我们使用 mixer 模块,在使用之前,需要先生成一个 Sound 对象,对这个 Sound 对象进行控制,Sound 对象 的 play() 方法就是播放音效,稍候我们来讲这些方法。
•播放背景音乐:
–pygame.mixer.music
播放背景音乐我们使用 music 模块,music 模块和 mixer 模块是紧密关联的。
Sound 对象方法如下:
方法 | 含义 |
play() | 播放音效 |
stop() | 停止播放 |
fadeout() | 淡出 |
set_volume() | 设置音量 |
get_volume() | 获取音量 |
get_num_channels() | 计算该音效播放了多少次 |
get_length() | 获得该音效的长度 |
get_raw() | 将该音效以二进制格式的字符串返回 |
music 模块 方法如下:
方法 | 含义 |
load() | 载入音乐 |
play() | 播放音乐 |
rewind() | 重新播放 |
stop() | 停止播放 |
pause() | 暂停播放 |
unpause() | 恢复播放 |
fadeout() | 淡出 |
set_volume() | 设置音量 |
get_volume() | 获取音量 |
get_busy() | 检测音乐流是否正在播放 |
set_pos() | 设置开始播放的位置 |
get_pos() | 获取已经播放的时间 |
queue() | 将音乐文件放入待播放列表中 |
set_endevent() | 在音乐播放完毕时发送事件 |
get_endevent() | 获取音乐播放完毕时发送的事件类型 |
我们来举个例子:
要求:写一个程序,打开程序就会自动播放背景音乐(bg_music.ogg),当你在窗口中点击鼠标左键时,就会播放 winner.wav 音效;点击鼠标右键,就会播放 loser.wav 音效;点击空格键就暂停背景音乐,再次点击就继续播放。(需要这几个声音文件的的可以在下面评论区留下邮箱,我看到后就把免费发送给你们)
import pygame
import sys
from pygame.locals import *
pygame.init()
pygame.mixer.init()#初始化混音器模块(可以不写,上面初始化已经包含了,但是最好写上)
#加载背景音乐
pygame.mixer.music.load('bg_music.ogg')
pygame.mixer.music.set_volume(0.2)#设置音量
pygame.mixer.music.play()#播放
#加载音效
winner_sound = pygame.mixer.Sound("winner.wav")
winner_sound.set_volume(0.2)
loser_sound = pygame.mixer.Sound("loser.wav")
loser_sound.set_volume(0.2)
#设置窗口
bg_size = width, height = 300, 200
screen = pygame.display.set_mode(bg_size)
pygame.display.set_caption("Music - Python Demo")
#设置 暂停 标记
pause = False
#显示播放和暂停图片
pause_image = pygame.image.load("pause.png").convert_alpha()
unpause_image = pygame.image.load("unpause.png").convert_alpha()
pause_rect = pause_image.get_rect()
pause_rect.left, pause_rect.top = (width - pause_rect.width) // 2, (height - pause_rect.height) // 2#居窗口正中央
clock = pygame.time.Clock() #帧率
while True:
for event in pygame.event.get():
if event.type == QUIT:
sys.exit()
if event.type == MOUSEBUTTONDOWN:
if event.button == 1: #鼠标左键
winner_sound.play()
if event.button == 3: #鼠标右键
loser_sound.play()
if event.type == KEYDOWN:
if event.key == K_SPACE: #空格键
pause = not pause
screen.fill((255, 255, 255)) #窗口背景白色
if pause: #暂停
screen.blit(pause_image, pause_rect)
pygame.mixer.music.pause()
else: #播放
screen.blit(unpause_image, pause_rect)
pygame.mixer.music.unpause()
pygame.display.flip() #刷新窗口
clock.tick(30)#帧率
上面就是我们实现 music 模块的 Demo。
现在我们就来把我们学到的新东西加到 Play TheBall 小游戏中。我们继续完善我们的代码:
代码如下:(需要素材的可以在评论区留下邮箱)
import pygame
import sys
from pygame.locals import *
from random import *
# 球类继承自Spirte类
class Ball(pygame.sprite.Sprite):
def __init__(self, image, position, speed, bg_size):
# 初始化动画精灵
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load(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]
self.radius = self.rect.width / 2 #增加半径属性
def move(self):
self.rect = self.rect.move(self.speed)
# 如果小球的左侧出了边界,那么将小球左侧的位置改为右侧的边界
# 这样便实现了从左边进入,右边出来的效果
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 main():
pygame.init()
ball_image = "gray_ball.png"
bg_image = "background.png"
running = True
##############################################################
#添加背景音乐
pygame.mixer.music.load('bg_music.ogg')
pygame.mixer.music.set_volume(0.2)#设置音量
pygame.mixer.music.play()#播放
#加载音效
winner_sound = pygame.mixer.Sound("winner.wav")
winner_sound.set_volume(0.2)
loser_sound = pygame.mixer.Sound("loser.wav")
loser_sound.set_volume(0.2)
laugh_sound = pygame.mixer.Sound("laugh.wav")
laugh_sound.set_volume(0.2)
hole_sound = pygame.mixer.Sound("hole.wav")
hole_sound.set_volume(0.2)
#背景音乐会贯穿游戏的始终,背景音乐完整播放一次我们视为游戏的时间,
#因此我们需要想办法让游戏在背景音乐停止时结束,我们应该有留意到:
#music 模块有一个 set_endevent() 方法,该方法的作用就是在音乐播放完时
#发送一条事件消息,发送什么消息是我们自定义的,USEREVENT 就是自定义消息,
#Pygame 给我们预定了很多事件,像我们熟悉的 键盘事件、鼠标事件等。这些预定义
#的事件都有一个标记符,例如:MOUSEBUTTONDOWN 、KEYDOWN等。这些都是
#一些数字的等值定义,其实在内部,2 就表示鼠标按下,但是人类难以记住,所以定义为
#MOUSEBUTTONDOWN。
#USEREVENT 就是数字24,24以上就是我们可以自定义的事件,我们可以像这样自定义事件:
# MY_EVENT = USEREVENT。
# MY_EVENT_1 = USEREVENT + 1........
#音乐播放完时,游戏结束
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 - Python Demo")
background = pygame.image.load(bg_image).convert_alpha()
# 用来存放小球对象的列表
balls = []
group = pygame.sprite.Group()
# 创建五个小球
for i in range(5):
# 位置随机,速度随机
position = randint(0, width-100), randint(0, height-100)
speed = [randint(-10, 10), randint(-10, 10)]
ball = Ball(ball_image, position, speed, bg_size)
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)
clock = pygame.time.Clock()
while running:
for event in pygame.event.get():
if event.type == QUIT:
sys.exit()
#####################################################################
elif event.type == GAMEOVER: #判断事件是否为我们自定义的GAMEOVER事件
loser_sound.play()
pygame.time.delay(2000)#暂停2秒
laugh_sound.play()
running = False
#####################################################################
screen.blit(background, (0, 0))
for each in balls:
each.move()
screen.blit(each.image, each.rect)
for each in group:
group.remove(each) #把自身拿出来
if pygame.sprite.spritecollide(each, group, False, pygame.sprite.collide_circle):#把自己和别的球进行碰撞检测
each.speed[0] = -each.speed[0]
each.speed[1] = -each.speed[1]
group.add(each)#还要把自己放进去
pygame.display.flip()
clock.tick(30)
if __name__ == "__main__":
main()
有一点需要讲解的:
背景音乐会贯穿游戏的始终,背景音乐完整播放一次我们视为游戏的时间,因此我们需要想办法让游戏在背景音乐停止时结束,我们应该有留意到:
music 模块有一个 set_endevent() 方法,该方法的作用就是在音乐播放完时发送一条事件消息,发送什么消息是我们自定义的,USEREVENT 就是自定义消息,Pygame 给我们预定了很多事件,像我们熟悉的 键盘事件、鼠标事件等。这些预定义的事件都有一个标记符,
例如:MOUSEBUTTONDOWN 、KEYDOWN等。这些都是一些数字的等值定义,其实在内部,2 就表示鼠标按下,但是人类难以记住,所以定义为 MOUSEBUTTONDOWN。
USEREVENT 就是数字24,24以上就是我们可以自定义的事件,我们可以像这样自定义事件:
MY_EVENT = USEREVENT。
MY_EVENT_1 = USEREVENT + 1........