第087讲: Pygame:播放声音和音效 | 学习记录(小甲鱼零基础入门学习Python)

这节课我们来谈谈 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 音效;点击空格键就暂停背景音乐,再次点击就继续播放。

from pygame.locals import *
from random import *
import pygame
import math
import sys


def main() :
    pygame.init()
    pygame.mixer.init()
    pygame.mixer.init()#初始化混音器模块(可以不写,上面初始化已经包含了,最好写上)

    bg_music = r"D:\Code\Python\Pygame\pygame8:播放声音和音效\bg_music.ogg"
    hole_music = r"D:\Code\Python\Pygame\pygame8:播放声音和音效\hole.wav"
    laugh_music = r"D:\Code\Python\Pygame\pygame8:播放声音和音效\laugh.wav"
    
    pygame.mixer.music.load(bg_music)  #加载背景音乐  
    pygame.mixer.music.set_volume(0.2)  #设置背景音乐音量
    pygame.mixer.music.play()  

    pygame.mixer.Sound(hole_music)  #加载点击鼠标左键发生的音乐
    pygame.mixer.Sound(hole_music).set_volume(0.2)  #设置音量

    pygame.mixer.Sound(laugh_music)  #加载点击鼠标右键发生的音乐
    pygame.mixer.Sound(laugh_music).set_volume(0.2)  #设置音量

    bg_size = width , height = 500 , 450   #设置窗口
    screen = pygame.display.set_mode(bg_size)
    pygame.display.set_caption("music-demo")

    bg = pygame.image.load(r"D:\Code\Python\Pygame\pygame8:播放声音和音效\jieshao.png").convert_alpha()     #加载背景图片
    pause_image = pygame.image.load(r"D:\Code\Python\Pygame\pygame8:播放声音和音效\pause.png").convert_alpha() #加载暂停播放图片
    unpause_image = pygame.image.load(r"D:\Code\Python\Pygame\pygame8:播放声音和音效\unpause.png").convert_alpha() #加载继续播放图片
    pause_rect = pause_image.get_rect()   #得到图片的尺寸

    clock = pygame.time.Clock()

    position = ((width - pause_rect.width) // 2 , (height - pause_rect.height) // 2 + 150 )     #图片记载的位置

    flag = 0    #设置一个flag变量使其能够判断现在是暂停还是继续

    while True :
        for event in pygame.event.get():
            if event.type == QUIT:
                sys.exit()
            elif event.type == MOUSEBUTTONDOWN:   #如果鼠标按下
                if event.button == 1:    #如果按下的是鼠标左键
                    pygame.mixer.Sound(hole_music).play()   #播放hole_music
                elif event.button == 3:     #如果按下的是鼠标右键
                    pygame.mixer.Sound(laugh_music).play()  #播放laugh_music
            elif event.type == pygame.KEYDOWN: #如果键盘按下:
                if event.key == pygame.K_SPACE and flag == 0: #如果按下的是空格且 flag == 0
                    flag += 1
                elif event.key == pygame.K_SPACE and flag == 1:  #如果按下的是空格且 flag == 1
                    flag -= 1

        #窗口背景白色
        screen.fill((255, 255, 255))  

        # 更新图像
        screen.blit(bg, (0,0))

        if flag :
            screen.blit(pause_image,position)   #记载暂停播放图片
            pygame.mixer.music.pause()   #暂停播放背景音乐  
        else :
            screen.blit(unpause_image,position)     #记载继续播放图片
            pygame.mixer.music.unpause()    #继续播放背景音乐

        # 更新界面
        pygame.display.flip()
        clock.tick(30)

if __name__ == "__main__":
    main()

第087讲: Pygame:播放声音和音效 | 学习记录(小甲鱼零基础入门学习Python)_第1张图片 第087讲: Pygame:播放声音和音效 | 学习记录(小甲鱼零基础入门学习Python)_第2张图片
上面就是实现 music 模块的 Demo。

现在我们就来把我们学到的新东西加到 Play TheBall 小游戏中。我们继续完善我们的代码:

有一点需要讲解的:

背景音乐会贯穿游戏的始终,背景音乐完整播放一次我们视为游戏的时间,因此我们需要想办法让游戏在背景音乐停止时结束,我们应该有留意到:

music 模块有一个 set_endevent() 方法,该方法的作用就是在音乐播放完时发送一条事件消息,发送什么消息是我们自定义的,USEREVENT 就是自定义消息,Pygame 给我们预定了很多事件,像我们熟悉的 键盘事件、鼠标事件等。这些预定义的事件都有一个标记符,

例如:MOUSEBUTTONDOWN 、KEYDOWN等。这些都是一些数字的等值定义,其实在内部,2 就表示鼠标按下,但是人类难以记住,所以定义为 MOUSEBUTTONDOWN。

USEREVENT 就是数字24,24以上就是我们可以自定义的事件,我们可以像这样自定义事件:

 MY_EVENT = USEREVENT。

 MY_EVENT_1 = USEREVENT + 1........

代码如下:

from pygame.locals import *
from random import *
import pygame
import time
import math
import sys

class Ball(pygame.sprite.Sprite) :  #继承动画精灵基类
    def __init__ (self,imgae,position,speed,bg_size) :
        pygame.sprite.Sprite.__init__(self)

        self.image = pygame.image.load(imgae).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_balls


def main() :
    pygame.init()

    bg_image = r"D:\Code\Python\Pygame\pygame6:动画精灵\background.png"
    ball_image = r"D:\Code\Python\Pygame\pygame6:动画精灵\gray_ball.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\pygame8:播放声音和音效\loser.wav")
    laugh_music = pygame.mixer.Sound(r"D:\Code\Python\Pygame\pygame8:播放声音和音效\laugh.wav")    
    winner_music = pygame.mixer.Sound(r"D:\Code\Python\Pygame\pygame8:播放声音和音效\winner.wav")
    loser_music = pygame.mixer.Sound(r"D:\Code\Python\Pygame\pygame8:播放声音和音效\loser.wav")


    bg_size = width , height = 1024 , 681       #背景大小
    screen = pygame.display.set_mode(bg_size) # 设置背景大小
    background = pygame.image.load(bg_image).convert_alpha()       #画背景

    balls = []

    # 创建五个小球
    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(ball_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)    #延时2s
                laugh_music.play()  #播放大笑音效
                running = False

        screen.blit(background, (0, 0)) #将背景画到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()

你可能感兴趣的:(第087讲: Pygame:播放声音和音效 | 学习记录(小甲鱼零基础入门学习Python))