MFC自制的掃雷小遊戲所得心得

首先亮一下自己做出的掃雷遊戲界面效果:

掃雷

還沒來得急改exe文件的圖標,剛下好ico文件,不過沒有合適的,所以就還沒換。

 

我做這個掃雷遊戲的第一件事當然是想解決怎麼讓這個界面看起來像掃雷那個樣子。我首先想到了按鈕控件可以響應鼠標點擊事件,所以就決定了使用MFC的對話框,用緊密排列的按鈕來當做雷區,索性就拉了幾個按鈕在界面隨意放着,然後Ctrl+c和Ctrl+v的湊夠100個按鈕。這樣一陣隨意操作之後,我急後悔了!太亂了,密密麻麻地,再怎麼也要弄個10*10的方陣出來纔像樣嘛~所以乾脆把按鈕全Delete掉,規規矩矩地先放5個按鈕進來,把按鈕的文字都刪掉,然後調整按鈕大小成一個方形,將5個按鈕選定佈局一橫排,之後再複製這5個到後面緊挨着放好,這樣才弄好第一行雷區,接下來就10個10個的複製,對齊了。我想要是這個佈局有網格的就好了,就不用我這樣一排一排的弄了……

第二步就是做上圖右邊那一列的遊戲信息,簡單的就不用贅述了。

第三步嘛自然是在雷區安排雷的位置了。這就需要爲這100個按鈕安排序號以作區別,然後隨機數序號的按鈕下對應着一個雷。而我又打開該對話框的資源文件頭文件Resource.h發現關於這100個按鈕的ID設置是這樣的:

#define IDC_BUTTON1                     1000
#define IDC_BUTTON2                     1001
#define IDC_BUTTON3                     1002

……………………………………………………

……………………………………………………
#define IDC_BUTTON98                    1097
#define IDC_BUTTON99                    1098
#define IDC_BUTTON100                   1099

所以我就乾脆以1000-1099爲序號,當然雷的標記是定義一個整數數組flg[100],flg[index]=0則不是雷,flg[index]=1則是雷,其中index+1000對應按鈕的ID。

第四步了,需要爲這100個按鈕添加響應鼠標左鍵單擊事件和右鍵單擊事件,不想做得太複雜了,就沒做鼠標左鍵雙擊和鼠標左右鍵同時點擊的響應。當然只算鼠標左鍵和右鍵就已經是麻煩事了,比較要爲100個按鈕添加呢!百度了一下多個控件響應同一個事件的添加方法,其中左鍵單擊有比較詳細的方法說明,就是先添加一個按鈕的左鍵單擊事件,再改Mine_SweepDlg.cpp如下幾句代碼:

BEGIN_MESSAGE_MAP(CChusDlg, CDialog)
 //{{AFX_MSG_MAP(CChusDlg)
 …………
 ON_BN_CLICKED(IDC_BUTTON1, OnButton1)
 //}}AFX_MSG_MAP
END_MESSAGE_MAP()

將其中的 ON_BN_CLICKED(IDC_BUTTON1, OnButton1)  改爲  ON_CONTROL_RANGE(BN_CLICKED,IDC_BUTTON1,IDC_BUTTON100,OnButton1)  即可實現100個按鈕響應同一個鼠標左鍵單擊事件函數OnButton1()  這隻適用於ID連續的控件。

當然鼠標左鍵單擊事件函數需要知道是哪一個ID的控件發生的,所以還需要傳遞一個ID參數,做下面幾個位置的修改:

Mine_SweepDlg.h中afx_msg void OnButton1(UINT uID);加入括號的參數,再在Mine_SweepDlg.cpp的事件響應函數中加入參數void CMine_SweepDlg::OnButton1(UINT uID)其中的uID就是100個按鈕的序號,而uID-1000就得到雷區標識的index了。

然後我將響應事件作出的效果如圖:

左鍵響應

即點擊某一個方格,判斷周邊8個方格是否有雷標識,若沒有則隱藏該按鈕,若有則顯示周邊雷標識個數。若點擊的這個方格就是累標識,則彈出消息框提示遊戲結束。以下附上事件響應函數的代碼:

void CMine_SweepDlg::OnButton1(UINT uID)
{
 // TODO: Add your control notification handler code here
 int index=(int)uID-1000;
 int chack[9];
 int i,j;
 if(flg[index]==0)//如果該位置不是雷
 {
  chack_mine(index,chack);//獲取試探方向
  for(i=0,j=0;i<chack[0];i++)//判斷周邊雷數量
  {
   if(flg[chack[i+1]]==1)
    j++;
  }
  if(j==0)//周邊無雷則隱藏相關按鈕
  {
   if(flg[index]!=2)
   {
    GetDlgItem(index+1000)->ShowWindow(FALSE);//隱藏該按鈕
    flg[index]=2;
    m_score+=10;
   }
   for(i=0,j=0;i<chack[0];i++)//隱藏周邊按鈕
   {
    int chac[9];
    chack_mine(chack[i+1],chac);
    for(int m=0,n=0;m<chac[0];m++)
    {
     if(flg[chac[m+1]]==1)
      n++;
    }
    if(n==0&&flg[chack[i+1]]!=2)
    {
     GetDlgItem(chack[i+1]+1000)->ShowWindow(FALSE);
     flg[chack[i+1]]=2;
     m_score+=10;
    }
   }
  }
  else//否則顯示周邊雷數量
  {
   CString bu;
   bu.Format("%d",j);
   SetDlgItemText(index+1000,bu);
   m_score+=10;
  }
 }
 else//左鍵單擊了一個雷
 {
  SetDlgItemText(uID,"*");
  m_minenum--;
  mine[0]--;
  m_score-=20;
  if(MessageBox("你觸雷了!遊戲結束~","提示",MB_OKCANCEL)==IDOK)
  {
   KillTimer(1);//取消定時器
   CDialog::OnOK();
  }
 }
}

以上是完成了左鍵單擊的響應,至於右鍵單擊的響應,比較麻煩了一點,需要重載一個虛函數PreTranslateMessage(MSG* pMsg)。說是該函數可以獲取界面上的鼠標事件,包括了左鍵單擊,左鍵雙擊,左鍵按下放開,右鍵單擊等消息,然後可以根據消息來完成想要的功能。具體的重載方法是在類視圖裏右鍵單擊Mine_SweepDlg,選擇Add Virtual Function ,在彈出對話框中左邊選擇PreTranslateMessage,然後確定。然後就可以在CMine_SweepDlg.cpp裏編輯使得響應控件的右鍵。以下是我的函數代碼:

BOOL CMine_SweepDlg::PreTranslateMessage(MSG* pMsg)
{
 // TODO: Add your specialized code here and/or call the base class
 if(pMsg->message==WM_RBUTTONDOWN)//鼠標點擊右鍵
 {
  CRect rc;
  CPoint ptCursor;
  GetCursorPos(&ptCursor);//獲取鼠標的位置信息
  int pos;
  for(pos=1000;pos<1100;pos++)//IDC_BOUTTON1(1000)--IDC_BOUTTON100(1099)
  {
   GetDlgItem(pos)-> GetWindowRect(&rc);//獲取控件的位置信息
   if(PtInRect(&rc,ptCursor))//右鍵點擊位置在某一個控件上
   {
    SetDlgItemText(pos,"*");
    m_minenum--;
    if(flg[pos-1000]==1)
     mine[0]--;
   }
  }
  GetDlgItem(1100)-> GetWindowRect(&rc);//獲取開始按鈕的位置信息
  if(PtInRect(&rc,ptCursor))//右鍵點擊位置在某一個控件上
  {
   for(int i=1;i<=mine[0];i++)
   {
    SetDlgItemText(mine[i]+1000,"*");
   }
  }
 }
 return CDialog::PreTranslateMessage(pMsg);
}

到此,按鈕的鼠標左鍵響應,右鍵響應已經完成,接下來就是設置定時器刷新遊戲用時和遊戲得分。

第五步,設置定時器。具體方法如下:

1.雙擊資源對話框,對着對話框空白處右鍵,選擇事件;

2.在左側的新建windows消息/事件下選擇WM_TIMER雙擊,然後確定;

3.在出現的void CMine_SweepDlg::OnTimer(UINT nIDEvent)中添加需要做的事

4.爲開始按鈕添加鼠標左鍵單擊響應事件,添加SetTimer(1,1000,NULL);//啓動定時器,1秒調用一次OnTimer函數。

 

以上就是我做這個掃雷遊戲的大致過程了。其中遇到的各種問題,也讓我學到不少東西,首先就是做一件事之前需要有一個科學合理有效的計劃,以免中途出錯重做;其次就是對於多個控件響應同一個事件的處理;再有就是處理鼠標消息的響應::PreTranslateMessage(MSG* pMsg)的使用。當然這個掃雷遊戲還有許多需要改進的地方,比如隱藏周邊無累的方格可以繼續擴展隱藏,還有顯示周邊雷標識個數的形式可以用靜態文本來代替該位置的按鈕顯示,等等……

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