游戲音樂(lè)與音效的播放
發(fā)表時(shí)間:2023-07-23 來(lái)源:明輝站整理相關(guān)軟件相關(guān)文章人氣:
[摘要]在Win32環(huán)境下,播放音樂(lè)音效的方法太多了,而且有一個(gè)共同點(diǎn)就是:你不需要花很大的心力就可以得到你需要的東西。延續(xù)主題式的探討,這一期我們著重在音樂(lè)與音效的播放! 游戲的配樂(lè)我相信很多人一定同意...
在Win32環(huán)境下,播放音樂(lè)音效的方法太多了,而且有一個(gè)共同點(diǎn)就是:你不需要花很大的心力就可以得到你需要的東西。延續(xù)主題式的探討,這一期我們著重在音樂(lè)與音效的播放。
□ 游戲的配樂(lè)
我相信很多人一定同意音樂(lè)在游戲里面所占的地位,回想一下國(guó)內(nèi)RPG的經(jīng)典「仙劍奇?zhèn)b傳」,剝掉音樂(lè)這一個(gè)層面,整個(gè)游戲?qū)?huì)遜色不少,尤其適當(dāng)?shù)膱?chǎng)景搭配適當(dāng)?shù)囊魳?lè),更能讓玩家融入劇情當(dāng)中。該哭的時(shí)候哭,該笑的時(shí)候笑,大概就很切中要領(lǐng)了。RPG剩下的音效部份,并不特別突出,大抵上知道砍人的時(shí)候有揮劍的聲音就可以了,所以在音效的表現(xiàn)方面,通常比較不那麼注重。而即時(shí)戰(zhàn)斗的游戲著重在廝殺的音效表現(xiàn)上,一大片人馬,一片混雜的聲音,這其中牽涉到混音的部份,我們底下也會(huì)探討到。讀完這篇文章,你會(huì)學(xué)習(xí)到什麼時(shí)候該用什麼樣的程式作法來(lái)表現(xiàn)游戲的另一個(gè)生命:音樂(lè)與音效。
□ 從MIDI開(kāi)始
早期DOS下的音樂(lè)部份,大多數(shù)采用聲霸卡的規(guī)格,副檔名為CMF者便是這種格式,當(dāng)然游戲通常不會(huì)讓你看到真正的作法,但是內(nèi)部采用這種格式居多是無(wú)庸置疑的。而WINDOW下的游戲以光碟發(fā)行者居多,為了充分達(dá)到空間利用的階段,游戲中會(huì)大量使用WAV格式的檔案,或是直接將音樂(lè)燒成音軌的格式。尤其很多游戲喜歡采用第一片資料片,第二片音樂(lè)片的作法,平常不玩游戲還可以當(dāng)成音樂(lè)CD來(lái)聽(tīng),算是滿(mǎn)有質(zhì)感的一件事。當(dāng)然,我的意思是這些音樂(lè)必須要聲聲入耳,如果音樂(lè)本身庸庸碌碌的,即使燒成音軌,一樣是庸庸碌碌,改變不了這個(gè)事實(shí)。
在WINDOW下,考量到空間的大小,MIDI格式的音樂(lè)檔絕對(duì)是最佳的選擇,一首五分鐘的MIDI了不起十萬(wàn)字元的大小,這跟WAV格式一分鐘占用量以MB計(jì),簡(jiǎn)直是小巫見(jiàn)大巫,所以網(wǎng)站上的音樂(lè),游戲的音樂(lè),都很適合用MIDI來(lái)表現(xiàn),而音樂(lè)部份我個(gè)人注重旋律,至於一首音樂(lè)本身使用到的樂(lè)器數(shù)量,我倒是很少去注意,人的耳朵聽(tīng)東西有一定的極限,只要不產(chǎn)生雜音,配合優(yōu)美的旋律,大致上都可以接受。
□ 播放MIDI的程式作法
游戲中播放音樂(lè)的要點(diǎn)就是循環(huán)播放,也就是播放完畢以後,要讓他從頭開(kāi)始播放,直到場(chǎng)景更換,或是游戲結(jié)束為止。所以當(dāng)MIDI檔案播放完畢以後,必須要能通知程式,讓程式做出適當(dāng)?shù)奶幚。播放MIDI的作法只要藉由WINDOW的多媒體的支援,馬上就搞定了,甚至直接從HELP的作法剪過(guò)來(lái),稍微修改一下,也能符合需要,因?yàn)檫@種東西相當(dāng)公式化,A君和B君寫(xiě)出來(lái)的程式碼也大致上會(huì)長(zhǎng)得差不多,廢話不多說(shuō),看看程式多麼簡(jiǎn)單便是:
class CMidi
{
public:
DWORD Play(HWND,char* FileName);
void Replay();
void Stop();
private:
UINT wDeviceID;//MCI裝置代號(hào)
DWORD dwReturn;
MCI_OPEN_PARMS mciOpenParms;
MCI_PLAY_PARMS mciPlayParms;
MCI_STATUS_PARMS mciStatusParms;
MCI_SEQ_SET_PARMS mciSeqSetParms;
};
將他包裝成一個(gè)類(lèi)別來(lái)使用也可以,而介面的部份需要單純化,從直覺(jué)上來(lái)說(shuō),第一個(gè)動(dòng)作就是播放(Play),接著是重播(Replay),最後當(dāng)然是善後的工作了(Stop),不多不少,剛好三個(gè),當(dāng)然你會(huì)想到,是不是需要一個(gè)暫停的介面,沒(méi)問(wèn)題,這不是什麼難事,花額外的三分鐘應(yīng)該可以勝任愉快。
了解類(lèi)別大致上的長(zhǎng)相以後,讓我們來(lái)看看實(shí)作的部份是怎麼一回事,先從CMidi::Play()開(kāi)始:
DWORD CMidi::Play(HWND hwnd,char* MidiFile)
{
// 開(kāi)啟Midi的硬體裝置,我們使用一般內(nèi)定值
mciOpenParms.lpstrDeviceType = "sequencer";
//這個(gè)叁數(shù)就是要播放的MIDI檔案名稱(chēng)
mciOpenParms.lpstrElementName = MidiFile;
// 使用Message的方式來(lái)播放MIDI而不是STRING的方式
if (dwReturn = mciSendCommand(NULL, MCI_OPEN,
MCI_OPEN_TYPE MCI_OPEN_ELEMENT,
(DWORD)(LPVOID) &mciOpenParms)
return (dwReturn);
// The device opened successfully; get the device ID.
wDeviceID = mciOpenParms.wDeviceID;
// Check if the output port is the MIDI mapper.
mciStatusParms.dwItem = MCI_SEQ_STATUS_PORT;
if (dwReturn = mciSendCommand(wDeviceID, MCI_STATUS,
MCI_STATUS_ITEM, (DWORD)(LPVOID) &mciStatusParms))
{
mciSendCommand(wDeviceID, MCI_CLOSE, 0, NULL);
return (dwReturn);
}
// 為了達(dá)成重復(fù)播放的目的,必須讓我們的程式能夠接收到
// MM_MCINOTIFY的訊息,這個(gè)函示呼叫的方式,就是傳遞
// WM_PLAY訊息給裝置,叫他開(kāi)始播放。
mciPlayParms.dwCallback = (DWORD) hwnd;
if (dwReturn = mciSendCommand(wDeviceID, MCI_PLAY, MCI_NOTIFY,
(DWORD)(LPVOID) &mciPlayParms))
{
mciSendCommand(wDeviceID, MCI_CLOSE, 0, NULL);
return (dwReturn);
}
return (0L);
};
播放MIDI的方式有兩種,第一種是利用字串命令硬體動(dòng)作,第二種是傳遞訊息的方式,我們采用第二種,原因很清楚了,必須透過(guò)訊息的傳遞,我們才能得知音樂(lè)是否播放完畢了。
接下來(lái)我們看看Cmidi::Replay是怎麼一回事:
void CMidi::Replay()
{
mciSendCommand(wDeviceID, MCI_SEEK,MCI_SEEK_TO_START, NULL);
mciSendCommand(wDeviceID, MCI_PLAY, MCI_NOTIFY, (DWORD)(LPVOID) &mciPlayParms);
}
真是不可思議地簡(jiǎn)單呀,函示里面只包含兩條呼叫,第一條呼叫送訊息給裝置,叫他把MIDI的播放指標(biāo)移到最開(kāi)頭的部份,也就是MCI_SEEK_TO_START,
作法就像移動(dòng)檔案指標(biāo)一樣。接著第二條指令光看也明白,就是叫他繼續(xù)播放就是了,而且別忘了MCI_NOTIFY,當(dāng)下次播放完畢,還是得用訊息通知我們的程式。
最後看一下Cmidi::Stop()的作法:
void CMidi::Stop()
{
mciSendCommand(wDeviceID, MCI_CLOSE, 0, NULL);
}
越來(lái)越單純了,里面只有包含一個(gè)函示呼叫,其中的訊息叁數(shù)MCI_CLOSE,就是結(jié)束整個(gè)音樂(lè)的播放。當(dāng)你結(jié)束播放以後,要播放另一首音樂(lè),很簡(jiǎn)單,再次呼叫Cmidi::Play()即可。
整個(gè)類(lèi)別的使用方法大致上是這樣的:首先配置一個(gè)實(shí)際的CMidi物件給程式,只要在全域的地方下條指令 CMidi midi;即可,爾後midi就是真實(shí)的物件了。在場(chǎng)景初始化的部份呼叫midi.Play(hwnd,"ff3celes.mid");,輸入正確的MIDI檔名即可。此處我播放的是太空戰(zhàn)士三代的音樂(lè),只是示范一下,當(dāng)然這首音樂(lè)確實(shí)很棒就是了。而在訊息回圈里面,我們必須定義一個(gè)訊息:
case MM_MCINOTIFY:
midi.Replay();
break;
在音樂(lè)播放完畢以後,我們的訊息回圈會(huì)收到MM_MCINOTIFY這個(gè)訊息,這時(shí)候如同我們前面所言,呼叫Cmidi::Replay()即可。而當(dāng)場(chǎng)景更換,要重新一首新的音樂(lè),或是程式結(jié)束的時(shí)候,就是呼叫Cmidi::Stop()的時(shí)機(jī)。因?yàn)橐粋(gè)場(chǎng)景同時(shí)間只會(huì)存在一首音樂(lè),所以我們的類(lèi)別表現(xiàn)良好,不用擔(dān)心。