MFC自制贪吃蛇游戏设计心得

先秀一个游戏贴图:

用MFC设计制作的小游戏——贪吃蛇!

通过利用MFC的对话框,计时器和静态文本实现。其中有几个重点:

其一:游戏边界的确定及控制;

其二:蛇身移动及位置变化;

其三:定时器的设置及利用。

其一,游戏边界问题。

界面分为左面游戏控制和右面游戏信息两个部分,如下代码可划分这两个部分的位置:

GetClientRect(&rect);//获取应用程序客户区的矩形位置信息

MaxLength=rect.right/25-4;

MaxHight=rect.bottom/25-2;

rectMax.left=25;//rectMax为游戏控制区域的矩形位置结构

rectMax.top=25;

rectMax.right=(MaxLength+1)*25;

rectMax.bottom=(MaxHight+1)*25;

//食物长宽为25的方块,所以游戏控制区域以25为步长GetDlgItem(IDC_STATICTOP)->MoveWindow(CRect(25,24,rectMax.right,25));

GetDlgItem(IDC_STATICBOTTOM)->MoveWindow(CRect(25,rectMax.bottom,rectMax.right,rectMax.bottom+1));

GetDlgItem(IDC_STATICLEFT)->MoveWindow(CRect(24,25,25,rectMax.bottom));

GetDlgItem(IDC_STATICRIGHT)->MoveWindow(CRect(rectMax.right,25,rectMax.right+1,rectMax.bottom));

//上面4条语句就是设置上下左右四个边界线

至于上面的MaxLength=rect.right/25-4; MaxHight=rect.bottom/25-2; 就是为了给右面的游戏信息留出一点位置。

其二,蛇身移动问题。

我在设计蛇身的时候,遇到了一点结构的应有问题。我最初是用vector容器存放蛇身节点:

struct Node{

CRect rect;//存放节点矩形位置信息

int NodeID;//存放节点资源ID

};

vector<Node> rectlist;

对于蛇身移动,我的想法是每次以将蛇尾节点移动到蛇头的前一个位置来达到一次整个蛇身移动的效果,而这对于vector容器来说就不好操作。介于我移动蛇身的想法,我想到了队列这样的数据结构,可以双向操作数据正好满足要求,所以我将vector改为deque结构定义:

deque<Node> rectlist;

如下代码为某个方向的移动:

case 1://

rect.left-=25;

rect.right-=25;

if(rect==rectFood)//蛇头碰到食物

{}

Else//否则做一次移动

{

struct Node node;//保存队列头结点

node.rect=rect;

node.NodeID=rectlist.begin()->NodeID;

rectlist.pop_front();//删除队列头结点

rectlist.push_back(node);//将之前的头结点加入队列尾

GetDlgItem((rectlist.end()-1)->NodeID)->MoveWindow(rect);

//蛇尾结点移动到蛇头之前

}

break;

由于蛇头碰见食物时,习惯压栈操作是向队列尾部添加,所以就将队列尾当做蛇头,而队列头当做蛇尾。这样移动一个结点达到整个蛇身移动的效果,避免了循环多次移动的出栈压榨操作消耗。

当然在做移动之前需要判断是否越出边界或碰到身体:

rect.left-=25;

rect.right-=25;

if(rect.left<rectMax.left)

{

KillTimer(2);

MessageBox("碰左壁了,游戏结束!");

KillTimer(1);

return;

}

if(rect==rectFood)//蛇头碰到食物

{}

else//否则做一次移动

{}

for(i=0;i<listlength-2;i++)//判断蛇头是否碰到身体

{

if(rectTop==rectlist[i].rect)

{

KillTimer(2);

MessageBox("你吃到自己了!游戏结束~");

KillTimer(1);

return;

}

}

由于这个判断是放在OnTimer()函数里,这里判断碰到需要先KillTimer();否则弹出的对话框会让你很麻烦,当然你不弹出对话框提示就不会担心这个问题了…

其三,定时器使用问题。

上面看到我用了两个KillTimer(),这是由于我设置有两个定时器:

SetTimer(1,1000,NULL);//游戏计时

SetTimer(2,400,NULL);//蛇身移动速度

这里是游戏开始时的定时器设置,由于贪吃蛇游戏需要根据得分高低来调整难度,所以需要根据判断得分来重新设置蛇身的移动速度:

if(m_score>=100)

{ KillTimer(2); KillTimer(1);

MessageBox("你很牛逼!恭喜你通关了!");

}

else if(m_score>=75)

SetTimer(2,80,NULL);

else if(m_score>=50)

SetTimer(2,100,NULL);

else if(m_score>=25)

SetTimer(2,150,NULL);

else if(m_score>=10)

SetTimer(2,200,NULL);

else if(m_score>=5)

SetTimer(2,250,NULL);

在这次小游戏的设计制作过程中,我也学到了不少东西。这次我基于MFC对话框设计的界面,基本摆脱了纯粹在对话框资源调节各个控件的布局,在函数中动态的创建和对控件布局,加深了我对MFC个性化的准确设计。当然动态创建可布局需要对CRect类有熟悉的了解,以及在脑中有对个控件布局的想象地图。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章