先秀一個遊戲貼圖:
用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類有熟悉的瞭解,以及在腦中有對個控件佈局的想象地圖。