DirectX游戏开发之一个API玩转音乐
当你闭上眼睛,打开一款游戏,如刺客信条,英雄联盟,DNF,或者是当有一个人坐在你背后玩一款新游戏,第一时间吸引你的是什么?
没错,就是各种游戏的音效,包括背景音乐,人物运动的声音(CF),人物攻击的声音(DNF),怪物的咆哮声……比比皆是。
要想游戏有“未见其人先闻其声”的效果,设计巧妙的音效是必不可少的。而且,各种音效营造游戏氛围的强大作用应该是每个看过恐怖片的人都可以充分体会到的。
没有好的音效绝对是玩家们吐槽的点,好的音效也绝对是加分点。
我们这里暂时不研究怎么选择合适的音效,我们先来通过一个函数简单玩转各种声音播放先。
我们起码要后台循环播放背景音乐,而且在不同状态下触发不同的音效。貌似弱弱的playsound是远远不能满足的,我们选择mciSendString。
【1】 加载音乐
//加载音乐
void LoadMusic()
{
mciSendString(L"open GameMedia\\背景音乐.mp3 alias bgm", NULL, 0, NULL);
mciSendString(L"open GameMedia\\子弹.mp3 alias bullet", NULL, 0, NULL);
mciSendString(L"open GameMedia\\金币.mp3 alias coin", NULL, 0, NULL);
mciSendString(L"open GameMedia\\跳.mp3 alias jump", NULL, 0, NULL);
mciSendString(L"open GameMedia\\子弹打到敌人.mp3 alias hit_enemy", NULL, 0, NULL);
mciSendString(L"open GameMedia\\子弹撞墙.mp3 alias hit_wall", NULL, 0, NULL);
mciSendString(L"open GameMedia\\撞击砖块.mp3 alias hit_brick", NULL, 0, NULL);
mciSendString(L"open GameMedia\\踩敌人.mp3 alias step", NULL, 0, NULL);
mciSendString(L"open GameMedia\\吃到武器.mp3 alias arm", NULL, 0, NULL);
mciSendString(L"open GameMedia\\死亡1.mp3 alias dead1", NULL, 0, NULL);
mciSendString(L"open GameMedia\\游戏结束.mp3 alias gameover", NULL, 0, NULL);
}
语法好简单的,open 音乐名 alias 用于代替长长不好记的音乐名的名字
【2】 各种简易操作
//格式:Play 名字 [from 起点] [to 终点]
mciSendString ("play musicname", NULL, 0, NULL);//播放
mciSendString ("play musicname from 0", NULL, 0, NULL);//从头播放
mciSendString ("pause musicname", NULL, 0, NULL);//暂停
mciSendString ("resume musicname", NULL, 0, NULL);//暂停后的继续
mciSendString ("stop musicname", NULL, 0, NULL);//停止
mciSendString ("close musicname",NULL, 0,NULL);//关闭
上个图吧。
只要合理运用以上方法,基本足够了。
我们已经能够捏住音乐玩儿啦!
main.cpp
//-----------------------------------【程序说明】----------------------------------------------
// 程序名称:003
// 2016年8月 by UltramanGaia520
// 描述:DirectX游戏开发之一个API玩转音乐
//------------------------------------------------------------------------------------------------
//-----------------------------------【头文件包含部分】---------------------------------------
// 描述:包含程序所依赖的头文件
//------------------------------------------------------------------------------------------------
#include <d3d9.h>
#include <d3dx9.h>
#include <tchar.h>
//-----------------------------------【库文件包含部分】---------------------------------------
// 描述:包含程序所依赖的库文件
//------------------------------------------------------------------------------------------------
#pragma comment(lib,"d3d9.lib")
#pragma comment(lib,"d3dx9.lib")
#pragma comment( lib,"winmm.lib" )
//-----------------------------------【宏定义部分】--------------------------------------------
// 描述:定义一些辅助宏
//------------------------------------------------------------------------------------------------
#define WINDOW_WIDTH 800 //窗口宽度宏
#define WINDOW_HEIGHT 600 //窗口高度宏
#define WINDOW_TITLE L"燃烧吧青春--DirectX游戏开发之一个API玩转音乐" //窗口标题宏
#define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } } //安全释放宏,便于后面COM接口指针的释放
//-----------------------------------【全局变量声明部分】-------------------------------------
// 描述:全局变量的声明
//------------------------------------------------------------------------------------------------
LPDIRECT3DDEVICE9 g_pd3dDevice = NULL; //Direct3D设备对象
ID3DXFont* g_pFont=NULL; //字体COM接口
float g_FPS = 0.0f; //代表帧速率
wchar_t g_strFPS[50]; //包含显示帧速率的字符数组
//-----------------------------------【全局函数声明部分】-------------------------------------
// 描述:全局函数声明,防止“未声明的标识”系列错误
//------------------------------------------------------------------------------------------------
LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );//窗口过程函数
HRESULT Direct3D_Init(HWND hwnd); //Direct3D的初始化
HRESULT Objects_Init(HWND hwnd); //要绘制的物体的资源初始化
VOID Direct3D_Render(HWND hwnd); //进行Direct3D渲染代码的书写
VOID Direct3D_CleanUp( ); //清理COM资源以及其他资源
float Get_FPS(); //获取帧速率
void LoadMusic(); //加载音乐资源
void TextRender(HWND hwnd); //渲染2D文字,放一起吧
//-----------------------------------【WinMain( )函数】--------------------------------------
// 描述:Windows应用程序的入口函数,我们的程序从这里开始
//------------------------------------------------------------------------------------------------
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, int nShowCmd)
{
//设计一个窗口类,像是私人定制一种自己喜欢的窗口的属性的组合,比喻为设计一种新的车型
WNDCLASSEX wndClass = { 0 }; //用WINDCLASSEX定义了一个窗口类
wndClass.cbSize = sizeof( WNDCLASSEX ) ; //设置结构体的字节数大小,无需修改
wndClass.style = CS_HREDRAW | CS_VREDRAW; //设置窗口类的样式,窗口类风格指定的是该类窗口所具有的风格
wndClass.lpfnWndProc = WndProc; //设置指向窗口过程函数的指针,就是说绑定该类型窗口发送的消息由哪个响应函数处理
wndClass.cbClsExtra = 0; //窗口类的附加内存,取0就可以了
wndClass.cbWndExtra = 0; //窗口的附加内存,取0就行了
wndClass.hInstance = hInstance; //指定包含窗口过程的程序的实例句柄。
wndClass.hIcon=(HICON)::LoadImage(NULL,L"icon.ico",IMAGE_ICON,0,0,LR_DEFAULTSIZE|LR_LOADFROMFILE); //本地加载自定义ico图标,开发游戏,图标也是展现风格的一面
wndClass.hCursor = LoadCursor( NULL, IDC_ARROW ); //指定窗口类的光标句柄。
wndClass.hbrBackground=(HBRUSH)GetStockObject(GRAY_BRUSH); //为hbrBackground成员指定一个白色画刷句柄
wndClass.lpszMenuName = NULL; //指定菜单资源的名字,因为游戏一般不会用到菜单资源,设为空就好
wndClass.lpszClassName = L"OurFantasticWindowsClass"; //指定窗口类的名字。
//注册窗口类,比喻为设计好图纸后,拿去什么机构那里登记备案
if( !RegisterClassEx( &wndClass ) ) //对窗口类进行注册,这样才能创建该类型的窗口
return -1;
//正式创建窗口,开始造车子了,一台新系列的法拉利出炉啦
HWND hwnd = CreateWindow( L"OurFantasticWindowsClass",WINDOW_TITLE, //创建窗口函数CreateWindow
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, WINDOW_WIDTH, //填WS_OVERLAPPEDWINDOW参数的位置选的是窗口的样式,
//不同于设计窗口类时的样式,前者指定的是某一个窗口的样式,
//后者是指定某一类窗口
WINDOW_HEIGHT, NULL, NULL, hInstance, NULL );
//Direct3D资源的初始化,调用失败用messagebox予以显示
if (!(S_OK==Direct3D_Init (hwnd)))
{
MessageBox(hwnd, _T("Direct3D初始化失败~!"), _T("错误"), 0); //使用MessageBox函数,创建一个消息窗口
}
mciSendString(L"play bgm repeat", NULL, 0, NULL);//重复播放背景音乐
//窗口的移动、显示与更新
MoveWindow(hwnd,250,80,WINDOW_WIDTH,WINDOW_HEIGHT,true); //调整窗口显示时左上角位置,不然是在(0,0)不好看
ShowWindow( hwnd, nShowCmd ); //调用ShowWindow函数来显示窗口
UpdateWindow(hwnd); //对窗口进行更新,就像我们买了新房子要装修一样
//【5】消息循环过程
MSG msg = { 0 }; //初始化msg
while( msg.message != WM_QUIT )
{
if( PeekMessage( &msg, 0, 0, 0, PM_REMOVE ) ) //查看应用程序消息队列,有消息时将队列中的消息派发出去。
{
TranslateMessage( &msg ); //将虚拟键消息转换为字符消息
DispatchMessage( &msg ); //该函数分发一个消息给窗口程序,就是给你之前绑定的那个窗口过程处理函数
}
else
{
Direct3D_Render(hwnd); //进行渲染
}
}
//窗口类的注销
UnregisterClass(L"OurFantasticWindowsClass", wndClass.hInstance); //程序准备结束,注销窗口类
return 0;
}
//-----------------------------------【WndProc( )函数】--------------------------------------
// 描述:窗口过程函数WndProc,对窗口消息进行处理
//------------------------------------------------------------------------------------------------
LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
{
switch( message ) //switch语句,分别处理不同的消息
{
case WM_PAINT: //若是客户区重绘消息
Direct3D_Render(hwnd); //调用Direct3D渲染函数
ValidateRect(hwnd, NULL); //更新客户区的显示
break; //跳出该switch语句,要记得这句,跳出去,不然继续往下,后果不堪设想
case WM_KEYDOWN: //若是键盘按下消息
if (wParam == VK_ESCAPE) //如果被按下的键是ESC
DestroyWindow(hwnd); //销毁窗口, 并发送一条WM_DESTROY消息
switch(wParam)
{
case VK_NUMPAD1:
mciSendString(L"play bullet from 0 ", NULL, 0, NULL);
break;
case VK_NUMPAD2:
mciSendString(L"play coin from 0 ", NULL, 0, NULL);
break;
case VK_NUMPAD3:
mciSendString(L"play jump from 0 ", NULL, 0, NULL);
break;
case VK_NUMPAD4:
mciSendString(L"play hit_enemy from 0 ", NULL, 0, NULL);
break;
case VK_NUMPAD5:
mciSendString(L"play hit_wall from 0 ", NULL, 0, NULL);
break;
case VK_NUMPAD6:
mciSendString(L"play hit_brick from 0 ", NULL, 0, NULL);
break;
case VK_NUMPAD7:
mciSendString(L"play step from 0 ", NULL, 0, NULL);
break;
case VK_NUMPAD8:
mciSendString(L"play arm from 0 ", NULL, 0, NULL);
break;
case VK_NUMPAD9:
mciSendString(L"play dead1 from 0 ", NULL, 0, NULL);
break;
case VK_NUMPAD0:
mciSendString(L"stop bgm ", NULL, 0, NULL);
mciSendString(L"play gameover from 0 ", NULL, 0, NULL);
break;
}
break; //跳出该switch语句
case WM_DESTROY: //若是窗口销毁消息
Direct3D_CleanUp(); //调用自定义的资源清理函数Game_CleanUp()进行退出前的资源清理
PostQuitMessage( 0 ); //向系统表明有个线程有终止请求。用来响应WM_DESTROY消息
break; //跳出该switch语句
default: //不属于上面的情况,调用默认的处理方式就好
return DefWindowProc( hwnd, message, wParam, lParam ); //调用缺省的窗口过程
}
return 0; //正常退出
}
//-----------------------------------【Direct3D_Init( )函数】--------------------------------------
// 描述:Direct3D初始化函数,进行Direct3D的初始化
//------------------------------------------------------------------------------------------------
HRESULT Direct3D_Init(HWND hwnd)
{
//--------------------------------------------------------------------------------------
// 创接口,创建Direct3D接口对象, 以便用该Direct3D对象创建Direct3D设备对象
//--------------------------------------------------------------------------------------
LPDIRECT3D9 pD3D = NULL; //Direct3D接口对象的创建
if( NULL == ( pD3D = Direct3DCreate9( D3D_SDK_VERSION ) ) ) //初始化Direct3D接口对象,并进行DirectX版本协商
return E_FAIL;
//--------------------------------------------------------------------------------------
// 取信息,获取硬件设备信息
//--------------------------------------------------------------------------------------
D3DCAPS9 caps; int vp = 0;
if( FAILED( pD3D->GetDeviceCaps( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &caps ) ) )
{
return E_FAIL;
}
if( caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT )
vp = D3DCREATE_HARDWARE_VERTEXPROCESSING; //支持硬件顶点运算,采用硬件顶点运算
else
vp = D3DCREATE_SOFTWARE_VERTEXPROCESSING; //不支持硬件顶点运算,无奈只好采用软件顶点运算
//--------------------------------------------------------------------------------------
// 填内容,填充D3DPRESENT_PARAMETERS结构体
//--------------------------------------------------------------------------------------
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(&d3dpp, sizeof(d3dpp));
d3dpp.BackBufferWidth = WINDOW_WIDTH;
d3dpp.BackBufferHeight = WINDOW_HEIGHT;
d3dpp.BackBufferFormat = D3DFMT_A8R8G8B8;
d3dpp.BackBufferCount = 1; //后台缓冲区的数量
d3dpp.MultiSampleType = D3DMULTISAMPLE_NONE;
d3dpp.MultiSampleQuality = 0;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.hDeviceWindow = hwnd;
d3dpp.Windowed = true; //是窗口化,还是全屏显示
d3dpp.EnableAutoDepthStencil = true;
d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8;
d3dpp.Flags = 0;
d3dpp.FullScreen_RefreshRateInHz = 0;
d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
//--------------------------------------------------------------------------------------
// 创设备,创建Direct3D设备接口
//--------------------------------------------------------------------------------------
if(FAILED(pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL,
hwnd, vp, &d3dpp, &g_pd3dDevice)))
return E_FAIL;
SAFE_RELEASE(pD3D) //LPDIRECT3D9接口对象的使命完成,我们将其释放掉
if(!(S_OK==Objects_Init(hwnd))) return E_FAIL; //调用一次Objects_Init,进行渲染资源的初始化
return S_OK;
}
//-----------------------------------【Object_Init( )函数】--------------------------------------
// 描述:渲染资源初始化函数,在此函数中进行要被渲染的物体的资源的初始化
//--------------------------------------------------------------------------------------------------
HRESULT Objects_Init(HWND hwnd)
{
//创建字体
if(FAILED(D3DXCreateFont(g_pd3dDevice, 25, 0, 0, 1, false, DEFAULT_CHARSET,
OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, 0, _T("微软雅黑"), &g_pFont)))
return E_FAIL;
//加载音乐
LoadMusic();
srand(timeGetTime()); //用系统时间初始化随机种子
return S_OK;
}
//-----------------------------------【Direct3D_Render( )函数】-------------------------------
// 描述:使用Direct3D进行渲染
//--------------------------------------------------------------------------------------------------
void Direct3D_Render(HWND hwnd)
{
//--------------------------------------------------------------------------------------
// 【Direct3D渲染五步曲之一】:清屏操作
//--------------------------------------------------------------------------------------
g_pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
//--------------------------------------------------------------------------------------
// 【Direct3D渲染五步曲之二】:开始绘制
//--------------------------------------------------------------------------------------
g_pd3dDevice->BeginScene(); // 开始绘制
//--------------------------------------------------------------------------------------
// 【Direct3D渲染五步曲之三】:正式绘制
//--------------------------------------------------------------------------------------
//渲染2D文字
TextRender(hwnd);
//--------------------------------------------------------------------------------------
// 【Direct3D渲染五步曲之四】:结束绘制
//--------------------------------------------------------------------------------------
g_pd3dDevice->EndScene(); // 结束绘制
//--------------------------------------------------------------------------------------
// 【Direct3D渲染五步曲之五】:显示翻转
//--------------------------------------------------------------------------------------
g_pd3dDevice->Present(NULL, NULL, NULL, NULL); // 翻转与显示
}
//-----------------------------------【Get_FPS( )函数】------------------------------------------
// 描述:用于计算每秒帧速率的一个函数
//--------------------------------------------------------------------------------------------------
float Get_FPS()
{
//定义四个静态变量
static float fps = 0; //我们需要计算的FPS值
static int frameCount = 0;//帧数
static float currentTime =0.0f;//当前时间
static float lastTime = 0.0f;//持续时间
frameCount++;//每调用一次Get_FPS()函数,帧数自增1
currentTime = timeGetTime()*0.001f;//获取系统时间,其中timeGetTime函数返回的是以毫秒为单位的系统时间,所以需要乘以0.001,得到单位为秒的时间
//如果当前时间减去持续时间大于了1秒钟,就进行一次FPS的计算和持续时间的更新,并将帧数值清零
if(currentTime - lastTime > 1.0f) //将时间控制在1秒钟
{
fps = (float)frameCount /(currentTime - lastTime);//计算这1秒钟的FPS值
lastTime = currentTime; //将当前时间currentTime赋给持续时间lastTime,作为下一秒的基准时间
frameCount = 0;//将本次帧数frameCount值清零
}
return fps;
}
//-----------------------------------【Direct3D_CleanUp( )函数】--------------------------------
// 描述:资源清理函数,在此函数中进行程序退出前资源的清理工作
//---------------------------------------------------------------------------------------------------
void Direct3D_CleanUp()
{
//释放COM接口对象
SAFE_RELEASE(g_pFont)
SAFE_RELEASE(g_pd3dDevice)
}
//加载音乐
void LoadMusic()
{
mciSendString(L"open GameMedia\\背景音乐.mp3 alias bgm", NULL, 0, NULL);
mciSendString(L"open GameMedia\\子弹.mp3 alias bullet", NULL, 0, NULL);
mciSendString(L"open GameMedia\\金币.mp3 alias coin", NULL, 0, NULL);
mciSendString(L"open GameMedia\\跳.mp3 alias jump", NULL, 0, NULL);
mciSendString(L"open GameMedia\\子弹打到敌人.mp3 alias hit_enemy", NULL, 0, NULL);
mciSendString(L"open GameMedia\\子弹撞墙.mp3 alias hit_wall", NULL, 0, NULL);
mciSendString(L"open GameMedia\\撞击砖块.mp3 alias hit_brick", NULL, 0, NULL);
mciSendString(L"open GameMedia\\踩敌人.mp3 alias step", NULL, 0, NULL);
mciSendString(L"open GameMedia\\吃到武器.mp3 alias arm", NULL, 0, NULL);
mciSendString(L"open GameMedia\\死亡1.mp3 alias dead1", NULL, 0, NULL);
mciSendString(L"open GameMedia\\游戏结束.mp3 alias gameover", NULL, 0, NULL);
}
//渲染2D文字,放一起吧
void TextRender(HWND hwnd)
{
//定义一个矩形,用于获取主窗口矩形
RECT formatRect;
GetClientRect(hwnd, &formatRect);
//在窗口右上角处,显示每秒帧数
int charCount = swprintf_s(g_strFPS, 20, _T("FPS:%0.3f"), Get_FPS() );
g_pFont->DrawText(NULL, g_strFPS, charCount , &formatRect, DT_TOP | DT_RIGHT, D3DCOLOR_XRGB(255,39,136));
//在纵坐标100处,写第一段文字
formatRect.top = 100;//指定文字的纵坐标
g_pFont->DrawText(0, _T("开始时循环播放背景音乐\n按数字键1,2,3....\n分别对应播放 发射子弹,吃到金币,跳,子弹打到敌人,\n子弹打到墙,撞击砖块,踩敌人,吃到花可以用武器,死亡,游戏结束"), -1, &formatRect, DT_LEFT|DT_BOTTOM,
D3DCOLOR_XRGB(68,139,256));
}
代码资源:优快云资源