游戏引擎开发之音乐播放(一)

首先,我只是把自己的经验写出来,有不足之处还望包涵,此外,我并不打算写教程,所以建议想学习游戏引擎开发的人还是找本书从浅入深学习。

很久之前我写过两篇关于多线程使用mciSendString方法的文章,为了便于开发,后面又将其扩展之后写成了动态链接库,由于跨度有些大,有些东西须要先从简说明一下。


首先是重载new和delete,用可自增的独立堆来存放同类对象,这个方法是从《Windows核心编程》上学来的,它能解决一些我遇到过的问题——线程栈空间不够,对象共享。

之前曾在线程中使用了new方法创建对象,因为开线程时线程栈是默认大小,空间很有限,运行一会儿就会报错,而且这样创建的对象,不利于线程间共享,所以那之后我在自己写的类里面都使用了这个重载操作符方法,另,子类如果不再重载一次的话,默认会使用父类的那个堆,基本代码如下:

头文件:


	//使用独立堆存储对象
protected:
	static HANDLE m_Heap;
	static volatile UINT m_ObjectNum;

public:
	void* operator new (size_t size);
	void operator delete (void* p);

源文件(Music是类名):


//使用独立堆存储对象
volatile UINT Music::m_ObjectNum=0;
HANDLE Music::m_Heap=NULL;

void* Music::operator new (size_t size)
{
	if(m_Heap==NULL)
	{
		//堆不存在,创建堆
		m_Heap=HeapCreate(0,0,0);
		//创建堆失败
		if(m_Heap==NULL)
			return(NULL);
	}

	//分配内存空间
	void* p=HeapAlloc(m_Heap,0,size);

	if(p!=NULL)
	{
		//成功创建对象后对象计数器+1
		InterlockedExchangeAdd(&m_ObjectNum,1);
	}

	return (p);
}

void Music::operator delete (void* p)
{
	if(HeapFree(m_Heap,0,p))
	{
		//成功释放内存后对象计数器-1
		InterlockedExchangeAdd(&m_ObjectNum,-1);
	}

	if(m_ObjectNum==0)
	{
		//没有对象存在时销毁堆
		if(HeapDestroy(m_Heap))
		{
			m_Heap=NULL;
		}
	}
}


其次是动态链接库的生成,我主要是想把类封装到dll中,方便在其它程序中使用。

首先是头文件中一些基本的宏定义:

#define DLLC extern "C"

#ifdef MYDLL

#define MYDLLAPI __declspec(dllexport)

#else

#define MYDLLAPI __declspec(dllimport)

#endif

之后在源文件中包含头文件前定义“MYDLL”就行。

须要导出自定义结构体,写成这样:

DLLC MYDLLAPI typedef struct tagPOINTEX{
	
}POINTEX;
须要导出自定义枚举型,写成这样:

MYDLLAPI typedef enum HPEN_STYLE{
	
};
须要导出类,写成这样:

DLLC class MYDLLAPI Music{

};

到此为止,准备工作告一段落,接下来进入正题,音乐播放。

与之前的相比,主要更改了两个地方:

一个是类别,从原来的一个Music类改为MusicBGM与MusicBGS两个类,MusicBGM与原来的Music类效果一样,MusicBGS主要是用来播放音效,区别在于一个MusicBGS对象会打开数次同一个音乐文件(默认为10次),而且不会响应循环播放等操作,主要是用于游戏中音效重叠播放效果。

其次是音乐管理类的循环机制,之前是每一次循环都会检测所有音乐对象的状态,在游戏开发中发现音乐对象多了之后,会非常占用CPU时间,即便它是摆在一个子线程中,所以改为一次循环只检测一个音乐对象的状态,当然,有消息会先响应消息,这么做之后发现确实能够缓解CPU压力,而且也不会影响游戏帧频了。

除此之外,对音乐管理类的消息队列增加了互斥锁,便于在多线程中使用,主要是为了应对多线程中游戏精灵发送音效播放消息的情况。

为了模拟多线程发送消息的情景,我写了一个DEMO,首先是动态链接库的导入:

头文件中:

#include "MusicLib\\MusicAPI.h"

源文件中:

#pragma	  comment(lib,"MusicLib\\MCIMusic.lib")

当然,DEMO中只有一个源文件,所以直接写成:

#include "MusicLib\\MusicAPI.h"
#pragma	  comment(lib,"MusicLib\\MCIMusic.lib")

然后是模拟多线程中游戏精灵发送音效播放消息,这些线程的代码如下:

//模拟线程数量
#define THREAD_NUM 10

volatile bool g_bExitFlag=false;	//通知线程退出的标志
HANDLE g_hThread[THREAD_NUM];

//用于模拟游戏精灵在多线程中发送音效消息
unsigned __stdcall ThreadForPlayMusic(LPVOID lpParameter)
{
	Sleep(1000);
	srand(timeGetTime());	// 初始化随机数种子
	while(true)
	{
		Sleep(rand()%1000);
		MUSIC_PARAM MP;
		MP.m_ID=g_pMusicBGS[rand()%MUSICBGS_NUM]->GetID();
		MP.m_Play=true;
		g_pMusicManager->AddMessage(MP);
		if(g_bExitFlag==true)
			break;
	}
	return 0;
}

因为音效不止一个,所以用了随机数,向随机音效对象发送播放消息,你也可以通过更改宏定义的值来改变模拟线程数量。

当然,DEMO中也载入了一个BGM,用户可以输入消息对BGM进行操作,这些操作包括play(播放),replay(重放),stop(暂停),loop(循环播放),normal(普通播放),kill(失去焦点),set(重获焦点),exit(退出程序),运行效果:


代码下载:

http://download.csdn.net/detail/daeba/8824935

代码中包含了DEMO项目和DLL项目。


PS:

话说之前游戏的帧数问题找到病根所在了,那是因为MCI对wav文件支持性不佳造成的,如果把用到的音乐文件全部改为mp3,就不会出现帧数问题,至于开启千千静听后帧数恢复正常的情况,应该是开启千千静听时会同时开启wav音频的加速插件,看来MCI还是有些老了,估计今后会去用DirectSound,现在的话就先将就着用吧。




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值