网络游戏客户端编程第15章Word文档下载推荐.docx
《网络游戏客户端编程第15章Word文档下载推荐.docx》由会员分享,可在线阅读,更多相关《网络游戏客户端编程第15章Word文档下载推荐.docx(32页珍藏版)》请在冰豆网上搜索。
ppvOut用于返回DirectInput实例的地址;
punkOuter是用于返回IUnKnown接口,一般情况设置为NULL。
当我们需要从什么设备上获取输入信息的时候,我们需要创建设备接口。
通过设备接口,我们可以通过设备接口来获取输入信息。
下面就是使用DirectInput实例的CreateDevice方法创建设备接口的代码。
if(FAILED(g_pDI->
CreateDevice(GUID_SysMouse,&
g_pMouse,NULL)))
{
MessageBox(NULL,TEXT("
Mousenotfound.Thesamplewillnowexit."
),
TEXT("
DirectInputSample"
MB_ICONERROR|MB_OK);
EndDialog(hDlg,0);
returnS_OK;
}
其中CreateDevice函数原型是:
HRESULTCreateDevice(
REFGUIDrguid,
LPDIRECTINPUTDEVICE*lplpDirectInputDevice,
LPUNKNOWNpUnkOuter
);
rguid是用来指定需要创建的设备的类型ID,如GUID_SysMouse;
lplpDirectInputDevice用于返回设备接口的地址;
pUnkOuter是用于返回IUnKnown接口,一般情况设置为NULL。
从设备获取数据之前,必须先设置其数据格式。
这只是一个在设备的状态改变后获取数据包并与已知结构进行匹配的问题。
例如,从游戏杆得到的数据包显然和从鼠标那里得来的不一样:
格式的大小不同,并且按照按钮和轴组合的不同有不同格式大小的值。
设置数据格式可以使用下面代码。
if(FAILED(hr=g_pMouse->
SetDataFormat(&
g_dfMouse)))
returnhr;
这里g_dfMouse是我们预先定义的DIDATAFORMAT类型变量。
变量定义如下:
DIDATAFORMATg_dfMouse=
sizeof(DIDATAFORMAT),
sizeof(DIOBJECTDATAFORMAT),
DIDF_ABSAXIS,
sizeof(MouseState),
numMouseObjects,
g_aObjectFormats
};
上述的MouseState结构体是我们自定义的结构体,它用来存放输入设备的输入信息。
MouseState的定义如下:
structMouseState
LONGlAxisX;
LONGlAxisY;
BYTEabButtons[3];
BYTEbPadding;
上述的DIOBJECTDATAFORMAT结构体是定义设备中的输入信息与我们自己定义的输入信息结构体中相应变量相对应的映射表。
其定义如下:
DIOBJECTDATAFORMATg_aObjectFormats[]=
{&
GUID_XAxis,FIELD_OFFSET(MouseState,lAxisX),//Xaxis
DIDFT_AXIS|DIDFT_ANYINSTANCE,0},
GUID_YAxis,FIELD_OFFSET(MouseState,lAxisY),//Yaxis
{0,FIELD_OFFSET(MouseState,abButtons[0]),//Button0
DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
{0,FIELD_OFFSET(MouseState,abButtons[1]),//Button1(optional)
DIDFT_BUTTON|DIDFT_ANYINSTANCE|DIDFT_OPTIONAL,0},
{0,FIELD_OFFSET(MouseState,abButtons[2]),//Button2(optional)
DIDFT_BUTTON|DIDFT_ANYINSTANCE|DIDFT_OPTIONAL,0}
上述的numMouseObjects用来标识输入信息的个数,其定义如下:
#definenumMouseObjects(sizeof(g_aObjectFormats)/sizeof(DIOBJECTDATAFORMAT))
通常设备会被其他程序占有,这时本程序是否能访问输入设备,需要通过设置协作级别来标识,设置协作级别可以使用函数IDirectInputDevice8:
:
SetCooperativeLevel。
如下面语句。
SetCooperativeLevel(hDlg,DISCL_NONEXCLUSIVE|
DISCL_FOREGROUND)))
第一个参数是用来指定将要接受输入的设备;
第二个参数是指定协作级别,这里的协作级别是表示其他设备可以获得输入设备,当前程序只有在位于前台的时候才可以访问输入数据。
接下来就是读取输入信息的时候了。
当我们需要获取信息的时候,首先需要调用IDirectInputDevice8接口的Poll函数来使设备接口读取当前输入设备的信息。
然后我们就可以使用下列语句读取输入设备信息了。
GetDeviceState(sizeof(MouseState),&
ms)))
GetDeviceState是IDirectInputDevice8的函数,用来获取输入信息,其中第一个参数是设备状态信息结构体的大小,第二个参数是用来存放设备状态信息的结构体变量的地址。
15.2DirectXAudio入门
15.2.1声音播放概述
在游戏中添加声音是必不可少的,通常声音文件的格式有很多,不同的声音文件格式需要用不同的方法来播放,在程序中播放声音有很多种方式,DirectX提供了一套与播放声音相关的接口,其中包括DirectSound、DirectMusic等,当前新的DirectX版本中新增了XACT、XAudio2、X3DAudio、XAPO、XAPOFX。
这里我们主要讨论当前游戏中用得非常成熟并且比较容易学习的DirectSound编程。
下面我们看看DirectSound到底能帮我们做些什么。
1.播放WAVE格式的音频文件或者资源。
2.可以同时播放多个音频。
3.给硬件缓冲送数据
4.播放3D立体声音
5.在声音中添加特技效果,比如回声,动态的改变特技的参数等
6.将麦克风或者其他音频输入设备的声音录制成wave格式的文件
15.2.2实现播放声音的过程
要使用DirectSound首先需要包含相关的头文件。
下面就是需要包含的头文件。
#include<
windows.h>
mmsystem.h>
mmreg.h>
dsound.h>
当然添加的dsound.lib库文件是必须的。
下面我们简单的来学习一下如果通过Directsound的API播放声音
第一步,创建一个设备对象。
在你的代码中你可以通过调用DirectSoundCreat8函数来创建一个支持IDirectSound8接口的对象,这个对象通常代表缺省的播放设备。
当然你可以枚举可用的设备,然后将设备的GUID传递给DirectSoundCreat8函数。
注意,Directsound虽然基于COM,但是你并不需要初始化com库,这些Directsound都帮你做好了,当然,如果你使用DMOs特技,你就要自己初始化com库了,切记。
第二步,创建一个辅助Buffer,也叫后备缓冲区你可以通过IDirectS-ound8:
CreateSoundBuffer来创建buffer对象,这个对象主要用来获取处理数据,这种buffer称作辅助缓冲区,以和主缓冲区区别开来,Direcsound通过把几个后备缓冲区的声音混合到主缓冲区中,然后输出到声音输出设备上,达到混音的效果。
第三步,获取PCM类型的数据将WAV文件或者其他资源的数据读取到缓冲区中。
第四步,将数据读取到缓冲区你可以通过IDirectSoundBuffer8:
Lock.方法来准备一个辅助缓冲区来进行写操作,通常这个方法返回一个内存地址,见数据从你的私人buffer中复制到这个地址中,然后调用IDirectSoundBuffer8:
Unlock。
第五步,播放缓冲区中的数据你可以通过IDirectSoundBuffer8:
Play方法来播放缓冲区中的音频数据,你可以通过IDirectSoundBuffer8:
Stop来暂停播放数据,你可以反复的莱停止,播放,音频数据,如果你同时创建了几个buffer,那么你就可以同时来播放这些数据,这些声音会自动进行混音的。
15.2.3创建DirectSound
在进行这部分之前,我们首先学习一下Directsound中常用的几个对象。
见下表:
表15-1
对象
数量
作用
主要接口
设备
每个应用程序只有一个设备对象
用来管理设备,创建辅助缓冲区
IDirectSound8
辅助缓冲区
每一个声音对应一个辅助缓冲区
用来管理一个静态的或者动态的声音流,然后在主缓冲区中混音
IDirectSoundBuffer8,IDirectSound3DBuffer8,IDirectSoundNotify8
主缓冲区
一个应用程序只有一个主缓冲区
将辅助缓冲区的数据进行混音,并且控制3D参数.
IDirectSoundBuffer,IDirectSound3DListener8
特技
可以没有,也可以有很多
来辅助缓冲的声音数据进行处理
IDirectSoundFXChorus8等
接下来我们来介绍如何使用DirectSound。
首先,要创建一个设备对象,然后通过设备对象创建buffer对象。
辅助缓冲区由应用程序创建和管理,DirectSound会自动地创建和管理主缓冲区,一般来说,应用程序即使没有获取这个对象的接口也可以播放音频数据,但是,如果应用程序要想得到IDirectSound3DListener8接口,就必须要自己创建一个主缓冲区。
我们可以将短小的声音文件全部读取到辅助缓冲区中,然后通过一个简单的命令来播放。
如果声音文件很长,就必须采用数据流了。
(1)枚举系统声音输出设备
当我们要创建声音设备的时候,首先我们需要查看系统的声音设备,这就是枚举系统输出声音设备,我们可以使用下面语句实现。
//定义一个回调函数,用来处理枚举到的信息
BOOLCALLBACKDSEnumProc(LPGUIDlpGUID,
LPCTSTRlpszDesc,
LPCTSTRlpszDrvName,
LPVOIDlpContext)
HWNDhCombo=(HWND)lpContext;
LPGUIDlpTemp=NULL;
if(lpGUID!
=NULL)//NULLonlyfor"
PrimarySoundDriver"
.
{
if((lpTemp=(LPGUID)malloc(sizeof(GUID)))==NULL)
{
return(TRUE);
}
memcpy(lpTemp,lpGUID,sizeof(GUID));
}
ComboBox_AddString(hCombo,lpszDesc);
ComboBox_SetItemData(hCombo,ComboBox_FindString(hCombo,0,lpszDesc),lpTemp);
free(lpTemp);
return(TRUE);
//注册枚举函数和combox句柄
if(FAILED(DirectSoundEnumerate((LPDSENUMCALLBACK)DSEnumProc,
(VOID*)&
hCombo)))
EndDialog(hDlg,TRUE);
枚举设备之后我再看如何真正的创建设备对象
(2)创建设备对象
创建设备对象最简单的方法就是通过DirectSoundCreate8函数,这个函数的第一个参数指定了和这个对象邦定的设备的GUID,你可以通过枚举设备来获取这个设备的GUID,你可以传递一个下面的参数来指定一个缺省的设备
DSDEVID_DefaultPlayback缺省的系统的声音输出设备,这个参数也可以为NULL
SDEVID_DefaultVoicePlayback,缺省的声音输出设备,通常指第二缺省设备,例如USB耳机麦克风
如果没有声音输出设备,这个函数就返回error,或者,在VXD驱动程序下,如果声音输出设备正被某个应用程序通过waveform格式的api函数所控制,该函数也返回error,下面是创建对象的代码:
LPDIRECTSOUND8lpds;
HRESULThr=DirectSoundCreate8(NULL,&
lpds,NULL));
(3)设置协作级别
因为Windows是一个多任务操作环境,在同一个时刻有可能多个应用程序共用同一个设备,通过协作水平,DirectX就可以保证这些应用程序在访问设备的时候不会冲突,每个Directsound应用程序都有一个协作度,用来确定来接近设备的程度,当你创建完设备对象后,一定要调用IDirectSound8:
SetCooperativeLevel来设置协作度,否则,你不会听到声音的,设置协作度的代码如下:
HRESULThr=lpDirectSound->
SetCooperativeLevel(hwnd,DSSCL_PRIORITY);
if(FAILED(hr))
ErrorHandler(hr);
//Adderror-handlinghere.
上面的hwnd参数代表了应用程序的窗口。
DirectSound定义了三种水平,DSSCL_NORMAL,DSSCL_PRIORITY,DSSCL_WRITEPRIMARY。
在NORMAL水平的协作度下,应用程序不能设置主缓冲区的格式,也不能对主缓冲区进行写操作,所有在这个层次的应用程序使用的媒体格式都是22kHZ,立体声,采样精度8位,所以,设备可以在各个应用程序中任意的切换。
在Priority层次的协作度下,应用程序可以有优先权使用硬件资源,比如使用硬件进行混音,当然也可以设置主缓冲区的媒体格式,游戏程序应该采用这个层次的协作度,这个层次的协作度在允许应用程序控制采用频率和位深度的同时,也给应用程序很大的权力,这个层次的协作度允许其他应用程序的声音和游戏的音频同时被听到,不影响。
最高层次的协作度就是Write_primary,当采用这个层次的协作度来使用一个dsound设备时,在非WDM模式驱动下,你的应用程序可以直接操作主缓冲区,在这个模式下,应用程序必须直接的操作主缓冲区。
如果你想直接把音频数据直接写入到主缓冲区,你就要将你的协作度模式设置为writeprimary,如果你的应用程序没有设置到这个层次,那么所有的对主缓冲区的IDirectSoundBuffer:
Lock都会失败。
当你的应用程序的协作度设置为wirte_primary水平时,并且你的应用程序位于前台,那么其他应用程序的辅助缓冲区就会停止,并且标示为lost(丢失设备),当你的应用程序转到后台,那么它的主缓冲区就会标示为lost(丢失设备),当它再次回到前台的时候会恢复到原来的状态,如果你的设备没有出现在你的系统中,那么你就没法设置wirte_primary协作度了,所以,在设置协作度之前,可以通过IDirectSound8:
GetCaps方法来查询一下设备是否可用。
(4)设置声音设备
在windowos98或者2000系统中,用户可以通过控制面板来设置扬声器的属性,应用程序可以通过IDirectSound8:
GetSpeakerConfig.函数来获取这些属性,我们不建议应用程序通过IDirectSound8:
SetSpeakerConfig函授来设置扬声器的属性,因为这些设置的改动会影响到其他用户或者应用程序。
(5)查询输出设备的性能
当你调用DirectSoundCreate8创建一个设备对象后,你的应用程序就可以通过IDirectSound8:
GetCaps函数来获取设备的性能属性。
下面的代码的功能就是查询设备性能:
DSCAPSdscaps;
dscaps.dwSize=sizeof(DSCAPS);
GetCaps(&
dscaps);
if(FAILED(hr))
通过DSCAPS结构,该函数就给我返回硬件设备的一些性能,但是在调用这个函数之前一定要初始化这个结构的dwSize成员。
如果你的应用程序能够操作硬件设备,比如你的协作度是Write_primory级别的,那么你在硬件设备上分配内存的时候,就要调用IDirectSoudn8:
GetCaps来查看一下硬件的资源是否满足你的要求。
15.2.4DirectSound声音缓冲区
在存储和播放几个音频流的时候,你的应用程序要给每一个音频流都要创建一个辅助缓冲区(buffer)对象。
辅助缓冲区可以和应用程的生命期一样的长,也可以在不需要的时候销毁。
辅助缓冲区可以是一个包含了整个声音数据的静态缓冲区,也是可以只包含声音数据的一部份,然后再播放时不断更新数据的流缓冲区。
为了限制内存开销,在播放比较长的声音文件时要采用流缓冲区,这些缓冲区只包含几秒钟的数据量。
我们可以通过同时播放几个辅助缓冲区中的声音来对他们进行混音,至于同时可以播放几个辅助缓冲区则有硬件设备的性能决定。
辅助缓冲区的格式并不完全一样,一般用来描述缓冲格式的参数如下
Format,缓冲区的format必须要所播放音频的waveformat一致。
Controls,不同的缓冲区的控制参数的值可以不一样,比如音量,频率,以及在不同方向的移动,当创建buffer时,你就要指定你需要的控制参数的值,例如,不要为一个不支持3D的音频创建一个3D缓冲区
Location,你创建的缓冲区可以在硬件管理的内存中,也可以在软件管理的内存中,当然,硬件缓冲区比软件缓冲区速度要快。
你可以调用IDirectSound8:
CreateSoundBuffer函数来创建一个缓冲区(buffer)对象。
这个函数返回一个指向IDrectSoundBuffer接口的指针,通过这个接口,应用程序可以获取IDirectSoundBuffer8interface。
下面的一段代码演示了如何创建一个辅助缓冲区,并且返回一个IDirectSoundBuffer8接口。
HRESULTCreateBasicBuffer(LPDIRECTSOUND8lpDirectSound,LPDIRECTSOUNDBUFFER8*ppDsb8)
WAVEFORMATEXwfx;
DSBUFFERDESCdsbdesc;
LPDIRECTSOUNDBUFFERpDsb=NULL;
HRESULThr;
//SetupWAVformatstructure.
memset(&
wfx,0,sizeof(WAVEFORMATEX));
wfx.wFormatTag=WAVE_FORMAT_PCM;
wfx.nChannels=2;
wfx.nSamplesPerSec=22050;
wfx.nBlockAlign=4;
wfx.nAvgBytesPerSec=wfx.nSamplesPerSec*wfx.nBlockAlign;
wfx.wBitsPerSample=16;
//SetupDSBUFFERDESCstructure.
dsbdesc,0,sizeof(