1. 程式人生 > >用Python和Pygame寫遊戲-從入門到精通(22)

用Python和Pygame寫遊戲-從入門到精通(22)

辛苦啦~ 這次是我們系統的pygame理論學習的最後一章了,把這次的音樂播放講完了,pygame的基礎知識就全部OK了。不過作為完整的教程,只有理論講解太過枯燥了,我隨後還會加一個或更多的實踐篇系列,看需要可能也會追加真3D等額外的內容。


就像上次所說的,pygame.mixer並不適合播放長時間的音樂播放,我們要使用pygame.mixer.music。

pygame.mixer.music用來播放MP3和OGG音樂檔案,不過MP3並不是所有的系統都支援(Linux預設就不支援MP3播放),所以最好還是都用Ogg檔案,我們可以很容易把MP3轉換為Ogg檔案,自己搜一下吧。

我們使用pygame.mixer.music.load()來載入一個檔案,然後使用pygame.mixer.music.play()來播放,這裡並沒有一個類似Music這樣的類和物件,因為背景音樂一般般只要有一個在播放就好了不是麼~不放的時候就用stop()方法來停止就好了,當然很自然有類似錄影機上的pause()和unpause()方法。

音效和音樂方法總結

Sound物件

方法名 作用
fadeout 淡出聲音,可接受一個數字(毫秒)作為淡出時間
get_length 獲得聲音檔案長度,以秒計
get_num_channels 聲音要播放多少次
get_volume 獲取音量(0.0 ~ 1.0)
play 開始播放,返回一個Channel物件,失敗則返回None
set_volume 設定音量
stop 立刻停止播放

Channels物件

方法名 作用
fadeout 類似
get_busy 如果正在播放,返回true
get_endevent 獲取播放完畢時要做的event,沒有則為None
get_queue 獲取佇列中的聲音,沒有則為None
get_volume 類似
pause 暫停播放
play 類似
queue 將一個Sound物件加入佇列,在當前聲音播放完畢後播放
set_endevent 設定播放完畢時要做的event
set_volume 類似
stop 立刻停止播放
unpause 繼續播放

Music物件:

方法名 作用
fadeout 類似
get_endevent 類似
get_volume 類似
load 載入一個音樂檔案
pause 類似
play 類似
rewind 從頭開始重新播放
set_endevent 類似
set_volume 類似
stop 立刻停止播放
unpause 繼續播放
get_pos 獲得當前播放的位置,毫秒計

雖然很簡單,不過還是提供一個例程吧,這裡面音樂的播放很簡單,就是上面講過的,不過其中還有一點其他的東西,希望大家學習一下pygame中按鈕的實現方法。


介面如上,執行的時候,指令碼讀取./MUSIC下所有的OGG和MP3檔案(如果你不是Windows,可能要去掉MP3的判斷),顯示的也很簡單,幾個控制按鈕,下面顯示當前歌名(顯示中文總是不那麼方便的,如果你執行失敗,請具體參考程式碼內的註釋自己修改):

# -*- coding: utf-8 -*-
# 注意檔案編碼也必須是utf-8
SCREEN_SIZE = (800, 600)
# 存放音樂檔案的位置
MUSIC_PATH = "./MUSIC"

import pygame
from pygame.locals import *
from math import sqrt
import os
import os.path

def get_music(path):

    # 從資料夾來讀取所有的音樂檔案
    raw_filenames = os.listdir(path)

    music_files = []
    for filename in raw_filenames:
        # 不是Windows的話,還是去掉mp3吧
        if filename.lower().endswith('.ogg') or filename.lower().endswith('.mp3'):
            music_files.append(os.path.join(MUSIC_PATH, filename))

    return sorted(music_files)

class Button(object):
    """這個類是一個按鈕,具有自我渲染和判斷是否被按上的功能"""
    def __init__(self, image_filename, position):

        self.position = position
        self.image = pygame.image.load(image_filename)

    def render(self, surface):
        # 家常便飯的程式碼了
        x, y = self.position
        w, h = self.image.get_size()
        x -= w / 2
        y -= h / 2
        surface.blit(self.image, (x, y))

    def is_over(self, point):
        # 如果point在自身範圍內,返回True
        point_x, point_y = point
        x, y = self.position
        w, h = self.image.get_size()
        x -= w /2
        y -= h / 2

        in_x = point_x >= x and point_x < x + w
        in_y = point_y >= y and point_y < y + h
        return in_x and in_y

def run():

    pygame.mixer.pre_init(44100, 16, 2, 1024*4)
    pygame.init()
    screen = pygame.display.set_mode(SCREEN_SIZE, 0)     

    #font = pygame.font.SysFont("default_font", 50, False)
    # 為了顯示中文,我這裡使用了這個字型,具體自己機器上的中文字型請自己查詢
    # 詳見本系列第四部分:http://eyehere.net/2011/python-pygame-novice-professional-4/
    font = pygame.font.SysFont("simsunnsimsun", 50, False)    

    x = 100
    y = 240
    button_width = 150
    buttons = {}
    buttons["prev"] = Button("prev.png", (x, y))
    buttons["pause"] = Button("pause.png", (x+button_width*1, y))
    buttons["stop"] = Button("stop.png", (x+button_width*2, y))
    buttons["play"] = Button("play.png", (x+button_width*3, y))
    buttons["next"] = Button("next.png", (x+button_width*4, y))

    music_filenames = get_music(MUSIC_PATH)
    if len(music_filenames) == 0:
        print "No music files found in ", MUSIC_PATH
        return

    white = (255, 255, 255)
    label_surfaces = []
    # 一系列的檔名render
    for filename in music_filenames:
        txt = os.path.split(filename)[-1]
        print "Track:", txt
        # 這是簡體中文Windows下的檔案編碼,根據自己系統情況請酌情更改
        txt = txt.split('.')[0].decode('gb2312')
        surface = font.render(txt, True, (100, 0, 100))
        label_surfaces.append(surface)

    current_track = 0
    max_tracks = len(music_filenames)
    pygame.mixer.music.load( music_filenames[current_track] )  

    clock = pygame.time.Clock()
    playing = False
    paused = False

    # USEREVENT是什麼?請參考本系列第二部分:
    # http://eyehere.net/2011/python-pygame-novice-professional-2/
    TRACK_END = USEREVENT + 1
    pygame.mixer.music.set_endevent(TRACK_END)

    while True:

        button_pressed = None

        for event in pygame.event.get():

            if event.type == QUIT:
                return

            if event.type == MOUSEBUTTONDOWN:

                # 判斷哪個按鈕被按下
                for button_name, button in buttons.iteritems():
                    if button.is_over(event.pos):
                        print button_name, "pressed"
                        button_pressed = button_name
                        break

            if event.type == TRACK_END:
                # 如果一曲播放結束,就“模擬”按下"next"
                button_pressed = "next"

        if button_pressed is not None:

            if button_pressed == "next":
                current_track = (current_track + 1) % max_tracks
                pygame.mixer.music.load( music_filenames[current_track] )
                if playing:
                    pygame.mixer.music.play()

            elif button_pressed == "prev":

                # prev的處理方法:
                # 已經播放超過3秒,從頭開始,否則就播放上一曲
                if pygame.mixer.music.get_pos() > 3000:
                    pygame.mixer.music.stop()
                    pygame.mixer.music.play()
                else:
                    current_track = (current_track - 1) % max_tracks
                    pygame.mixer.music.load( music_filenames[current_track] )
                    if playing:
                        pygame.mixer.music.play()

            elif button_pressed == "pause":
                if paused:
                    pygame.mixer.music.unpause()
                    paused = False
                else:
                    pygame.mixer.music.pause()
                    paused = True

            elif button_pressed == "stop":
                pygame.mixer.music.stop()
                playing = False

            elif button_pressed == "play":
                if paused:
                    pygame.mixer.music.unpause()
                    paused = False
                else:
                    if not playing:
                        pygame.mixer.music.play()
                        playing = True

        screen.fill(white)

        # 寫一下當前歌名
        label = label_surfaces[current_track]
        w, h = label.get_size()
        screen_w = SCREEN_SIZE[0]
        screen.blit(label, ((screen_w - w)/2, 450))

        # 畫所有按鈕
        for button in buttons.values():
            button.render(screen)

        # 因為基本是不動的,這裡幀率設的很低
        clock.tick(5)
        pygame.display.update()

if __name__ == "__main__":

    run()


這個程式雖然可以執行,還是很簡陋,有興趣的可以改改,比如顯示播放時間/總長度,甚至更厲害一點,滑鼠移動到按鈕上班,按鈕會產生一點變化等等,我們現在已經什麼都學過了,唯一欠缺的就是實踐而已!

所以下一次,我將開始一個實戰篇,用pygame書寫一個真正可以玩的遊戲,敬請期待~~