(十)背景声音的播放
游戏大多都是有背景声音的,因此我们也不能免俗。
播放声音需要头文件,还需要导入声音文件库:
#include "mmsystem.h"
#pragma comment(lib,"winmm.lib")//导入声音头文件库
总之,这两行连用就可以了(更多的我也不知道)。
这两行放在需要使用下面代码的源代码文件中的开始处。
其次,需要使用mciSendString()函数。
打开一个媒体文件需要使用命令:
mciSendString("openres\\我在人民广场吃炸鸡.mp3 alias bgMusic", NULL,0, NULL);
open表示打开,后面是路径。alias bgMusic表示将该媒体文件别名命名为bgMusic(也可以起名为别的)。后面三个参数作用暂时不管(我也不知道)。
播放一个媒体文件需要使用命令:
mciSendString("playbgMusic repeat", NULL, 0, NULL);
play表示播放,bgMusic表示被播放的媒体文件(可以用别名也可以用路径)(这里是上面打开的媒体文件的别名),repeat表示重复播放(单曲循环)。
这两行例如可以放在PreCreateWindow()函数中。
(十一)如何输出坐标(输出文字)
涉及到格式化输出的问题。代码如下:
CString x_;
x_.Format(_T("x:%d,y:%d"), myHero.xy.left, myHero.xy.top);
m_cacheDC.TextOut(50, 50,x_);
第一行命令的意思是,设置一个CString类型的变量x_;
第二行命令的意思是,格式化字符串(被双引号括起来的那些),其中%d表示是int或者double类型的变量,2个%d表示有2个,这两个变量分别为(myHero.xy.left,和myHero.xy.top),因此,输出的文字则为:
第三行则是普通的TextOut()函数,参照使用即可。
(十二)障碍物
游戏里一般都是有障碍物的,而障碍物,一般是不能被碰到的。
检测障碍物的原理很简单,检测将要移动到的位置,是否有障碍物(简单版的检测方法,是只检测某一边,例如往上移动,则只检测top那一边移动后的那一排坐标,是否有障碍物。复杂点的,则是检测全部图像中,非透明的地方)。
障碍物的标识,可以用地图的障碍物版(即,将地图中是障碍物的地方,标记为单一颜色,例如黑色。这样,有障碍物的地方就是黑色的,没有障碍物的地方为其他颜色,例如统一为白色)(需要注意,这个障碍物版的地图,其像素应该和大地图的实际大小统一。就是在Draw()函数中,绘画的大地图大小)。
这个障碍物版的地图,被一个CImage类型变量所加载,但不被绘画到缓冲DC之上,只是单纯用于检测而已。
但由于我之前做的Hero类(用于加载人物和怪物的类),因此,为了修改代码方便,则先移动,然后判断是否能移动,如果不能,则取消移动(位移更改回去)
其函数为:
bool Hero::CanMove(CImage&bg, int X, int Y) //移动后判断是否应该取消移动,第一个参数为背景(黑白图),第二三个参数为该次移动的坐标变化量,有正负变化 { //判断是否能移动,根据面向判断(移动首先改变面向,然后才判断是否能移动) if (direct == 0) //向下移动,变化量为Y { for (int i = xy.left; i < xy.right; i++) if (bg.GetPixel(i, xy.bottom) == RGB(0, 0, 0))return false; //移动后下边那一边如果检测到黑色,说明不能移动 return true; //如果没检测到,说明可以移动 } else if (direct == 3) //向上 { for (int i = xy.left; i < xy.right;i++) if (bg.GetPixel(i, xy.top) == RGB(0, 0, 0))return false; //移动后上边那一边如果检测到黑色,说明不能移动 return true; //如果没检测到,说明可以移动 } else if (direct == 1) //往左移动 { for (int i = xy.top; i < xy.bottom; i++) if (bg.GetPixel(xy.left, i) == RGB(0, 0, 0))return false; return true; } else if (direct == 2) //往右移动 { for (int i = xy.top; i < xy.bottom; i++) if (bg.GetPixel(xy.right, i) == RGB(0, 0, 0))return false; return true; } }
键盘响应函数OnKeyDown()的代码为(注:较为繁琐,实际上是可以优化的,暂放弃优化):
void CChildView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) { // TODO: 在此添加消息处理程序代码和/或调用默认值 switch (nChar) { case 'D': myHero.addX(10); if (!myHero.CanMove(m_bgblack, 10, 0))myHero.addX(-10); myHero.GetDirect() = 2; break; case 'A': myHero.addX(-10); if (!myHero.CanMove(m_bgblack, -10, 0))myHero.addX(10); myHero.GetDirect() = 1; break; case 'W': myHero.addY(-10); if (!myHero.CanMove(m_bgblack, 0,-10))myHero.addY(10); myHero.GetDirect() = 3; break; case 'S': myHero.addY(10); if (!myHero.CanMove(m_bgblack, 0, 10))myHero.addY(-10); myHero.GetDirect() = 0; break; case 'T': //创建定时器 SetTimer(TIMER_HEROMOVE, 100, NULL); break; case 'I': //撤销定时器 KillTimer(TIMER_HEROMOVE); } myHero.SetXY(); myHero.addFrame(); //修改帧数 myHero.SetClient(m_client); //人物移动,设置背景图新的坐标 }
备注:
个人亲身感受而言,障碍物为矩形比较好用,假如用PS的画笔工具,似乎效果差很多(实际上,发生了不明问题,即明明是不是障碍物的地方,却过不去,但并不知道为什么)。
(十三)弹窗
先上代码:
void CChildView::OnLButtonDown(UINT nFlags, CPoint point) { //TODO: 在此添加消息处理程序代码和/或调用默认值 charbufPos[50]; sprintf_s(bufPos,"你单击了点X:%d,Y:%d", point.x, point.y); AfxMessageBox(bufPos); }
代码效果:
点击左键后,弹出一个窗口,其标题为“练习”(即程序名),内容为:“你单击了X:370,Y:63”(具体XY后面的值根据点击的地方而变化,范围为800x600内)。
注意,前两行可以用以下代码替换,效果是一样的(这种代码貌似是C风格):
CString bufpos;
bufpos.Format("你单击了点X:%d,Y:%d", point.x, point.y);
简单来说,AfxMessageBox()函数的作用是弹出一个弹窗。弹窗显示的内容为第一个参数(字符串类型)的内容,默认只有“确认”按钮。
更多关于AfxMessageBox()函数的功能,请参阅《MFC的一些函数》。