緊接著上一次,我們繼續來看如何在Pygame中使用聲音。
Sound物件
在初始化聲音系統之後,我們就可以讀取一個音樂檔案到一個Sound物件中了。pygame.mixer.Sound()接受一個檔名,或者也可以使一個檔案物件,不過這個檔案必須是WAV或者OGG,切記!
1 |
hello_sound = Pygame.mixer.Sound("hello.ogg") |
一旦這個Sound物件出來了,你可以使用play方法來播放它。play(loop, maxtime)可以接受兩個引數,loop自然就是重複的次數,-1意味著無限迴圈,1呢?是兩次,記住是重複的次數而不是播放的次數;maxtime是指多少毫秒後結束,這個很簡單。當你不使用任何引數呼叫的時候,意味著把這個聲音播放一次。一旦play方法呼叫成功,就會返回一個Channel物件,否則返回一個None。
Channel物件
Channel,也就是聲道,可以被音效卡混合(共同)播放的資料流。遊戲中可以同時播放的聲音應該是有限的,pygame中預設是8個,你可以通過pygame.mixer.get_num_channels()來得知當前系統可以同時播放的聲道數,而一旦超過,呼叫sound物件的play方法就會返回一個None,如果你確定自己要同時播放很多聲音,可以用set_num_channels()來設定一下,最好一開始就設,因為重新設定會停止當前播放的聲音。
那麼Channel物件有什麼用呢?如果你只是想簡單的播放一下聲音,那麼根本不用管這個東西,不過如果你想創造出一點有意境的聲音,比如說一輛火車從左到右呼嘯而過,那麼應該是一開始左聲道比較響,然後相當,最後右聲道比較響,直至慢慢消失。這就是Channel能做到的事情。Channel的set_volume(left, right)方法接受兩個引數,分別是代表左聲道和右聲道的音量的小數,從0.0~1.0。
豎起我們的耳朵
OK,來個例子試試吧~
這個例子裡我們通過釋放金屬小球並讓它們自由落體和彈跳,聽碰撞的聲音。這裡面不僅僅有這次學習的聲音,還有幾個一起沒有接觸到的技術,最重要的一個就是重力的模擬,我們可以設定一個重力因子來影響小球下落的速度,還有一個彈力系數,來決定每次彈跳損失的能量,雖然不難,但是遊戲中引入這個東西能讓我們的遊戲模擬度大大提高。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 |
SCREEN_SIZE = (640, 480) # 重力因子,實際上是單位 畫素/平方秒 GRAVITY = 250.0 # 彈力系數,不要超過1! BOUNCINESS = 0.7 import pygame from pygame.locals import * from random import randint from gameobjects.vector2 import Vector2 def stero_pan(x_coord, screen_width): """這個函式根據位置決定要播放聲音左右聲道的音量""" right_volume = float(x_coord) / screen_width left_volume = 1.0 - right_volume return (left_volume, right_volume) class Ball(): """小球類,實際上我們可以使用Sprite類來簡化""" def __init__(self, position, speed, image, bounce_sound): self.position = Vector2(position) self.speed = Vector2(speed) self.image = image self.bounce_sound = bounce_sound self.age = 0.0 def update(self, time_passed): w, h = self.image.get_size() screen_width, screen_height = SCREEN_SIZE x, y = self.position x -= w/2 y -= h/2 # 是不是要反彈了 bounce = False # 小球碰壁了麼? if y + h >= screen_height: self.speed.y = -self.speed.y * BOUNCINESS self.position.y = screen_height - h / 2.0 - 1.0 bounce = True if x <= 0: self.speed.x = -self.speed.x * BOUNCINESS self.position.x = w / 2.0 + 1 bounce = True elif x + w >= screen_width: self.speed.x = -self.speed.x * BOUNCINESS self.position.x = screen_width - w / 2.0 - 1 bounce = True # 根據時間計算現在的位置,物理好的立刻發現這其實不標準, # 正規的應該是“s = 1/2*g*t*t”,不過這樣省事省時一點,我們只是模擬~ self.position += self.speed * time_passed # 根據重力計算速度 self.speed.y += time_passed * GRAVITY if bounce: self.play_bounce_sound() self.age += time_passed def play_bounce_sound(self): """這個就是播放聲音的函式""" channel = self.bounce_sound.play() if channel is not None: # 設定左右聲道的音量 left, right = stero_pan(self.position.x, SCREEN_SIZE[0]) channel.set_volume(left, right) def render(self, surface): # 真有點麻煩了,有愛的,自己用Sprite改寫下吧…… w, h = self.image.get_size() x, y = self.position x -= w/2 y -= h/2 surface.blit(self.image, (x, y)) def run(): # 上一次的內容 pygame.mixer.pre_init(44100, 16, 2, 1024*4) pygame.init() pygame.mixer.set_num_channels(8) screen = pygame.display.set_mode(SCREEN_SIZE, 0) pygame.mouse.set_visible(False) clock = pygame.time.Clock() ball_image = pygame.image.load("ball.png").convert_alpha() mouse_image = pygame.image.load("mousecursor.png").convert_alpha() # 載入聲音檔案 bounce_sound = pygame.mixer.Sound("bounce.ogg") balls = [] while True: for event in pygame.event.get(): if event.type == QUIT: return if event.type == MOUSEBUTTONDOWN: # 剛剛出來的小球,給一個隨機的速度 random_speed = ( randint(-400, 400), randint(-300, 0) ) new_ball = Ball( event.pos, random_speed, ball_image, bounce_sound ) balls.append(new_ball) time_passed_seconds = clock.tick() / 1000. screen.fill((255, 255, 255)) # 為防止小球太多,把超過壽命的小球加入這個“死亡名單” dead_balls = [] for ball in balls: ball.update(time_passed_seconds) ball.render(screen) # 每個小球的的壽命是10秒 if ball.age > 10.0: dead_balls.append(ball) for ball in dead_balls: balls.remove(ball) mouse_pos = pygame.mouse.get_pos() screen.blit(mouse_image, mouse_pos) pygame.display.update() if __name__ == "__main__": run() |
這麼久了,我們們的遊戲終於有了聲音,太棒了!不過,是不是遊戲的背景音樂也是用mixer來播放的呢?這不是一個好主意,因為背景音樂往往很長,比較佔資源,pygame中另外提供了一個pygame.mixer.music類來控制背景音樂的播放,這也是我們下一次要講的內容。
小球影像:ball.png
游標影像:mousecursor.png
彈跳聲音:bounce.ogg