3. 要点分析
蛇的活动区域是由若干个小方格组成的。当这些小方格呈现灰色时,便表示蛇身。设整个区域由m×n个方格组成,最左上角的方格坐标为(0, 0)。蛇是由若干个邻的方格组成的,将这些方格的坐标依蛇头至蛇尾的次序存入到一个数组中便代表了蛇身。当蛇在游戏区域中“游动”一格时,所对应的数组操作应该是,将新的位置坐标插入到数组头部,同时将数组中最后一个元素删除。这项工作可以用一个一般的数组来完成,但当进行插入操作时需要自己移动数组中的元素;也可以使用CArray来完成这些工作,CArray的成员函数提供了需要的操作,这样做简单一些。
4. 解题步骤
(1)新建工程Snake,在MFC的向导第一步选择Single Document,按Finish结束。
(2)选择ResourceView窗口,打开菜单编辑器,在顶层菜单上添加一个名为“游戏”的弹出式菜单,该菜单下再添加一个名为“开始”的子菜单,其ID为ID_GAME_START,如图1所示。
图1 菜单的设计
(3)在ClassWizard中为刚才编辑好的菜单添加消息处理函数。打开ClassWizard,选中Message Maps页。在Class Name中选择CSnakeView,在Object ID中选择ID_GAME_ START,在Messages中选择COMMAND,添加消息处理函数。
(4)在ClassWizard中添加键盘消息处理函数。打开ClassWizard,选中Message Maps页。在Class Name中选择CSnakeView,在Object ID中选择CSnakeView,在Messages中选择WM_KeyDown,添加消息处理函数。
(5)在ClassWizard中定时器消息添加处理函数。打开ClassWizard,选中Message Maps页。在Class Name中选择CSnakeView,在Object ID中选择CSnakeView,在Messages中选择WM_Timer,添加消息处理函数。
(6)编辑生成的代码,完成程序。
4. 源程序清单
(1) 选择ClassView窗口,双击CSnakeView类,添加如下成员变量。并添加头文件:
#include
class CSnakeView : public CView
{
//此处略去若干行由系统生成的代码
private:
void ReDisplay(CPoint pPoint);
void IniGame();
void IniAim();
int m_nLeft, m_nTop, m_nWidth, m_nHeight, m_nSize; // 起始坐标,宽/高度(格数),每格大小
int m_nDirect; // 当前方向
// 1-右,2-左,3-下,4-上
CPoint m_pAim; // 当前目标坐标
CArray < CPoint, CPoint > m_aBody; // 蛇身
int m_nGameStatus; // 游戏状态:0-未开始,1-开始
int m_nCount; // 吃掉目标数
int m_nTime, m_nTime1; // 用时间
};
(2)在CSnakeView. Cpp文件,添加3个成员函数IniAim、IniGame和ReDiaplay:
// 该函数随机产生一个供蛇吃的目标,如果该目标恰巧与蛇身重合,则重新产生一个
void CSnakeView::IniAim()
{
int uX, uY; // 目标位置
while (1)
{
uX=rand ( ) % m_nHeight;
uY=rand ( ) % m_nWidth;
int uTag = 0; // 0-不与蛇身重合,1-重合
for (int i=0;i <= m_aBody. GetUpperBound ( );i++)
{
CPoint uPoint = m_aBody. GetAt (i);
if (uPoint. x == uX ||uPoint. y == uY ) // 目标与蛇身重合
{
uTag = 1;
break;
}
}
if (uTag == 0)
break;
}
m_pAim = CPoint (uX, uY); // 产生的目标存放在成员变量中
}
// 该函数对游戏初始化,定义游戏的初始状态
void CSnakeView::IniGame()
{
// 游戏区域
m_nLeft = 20;
m_nTop = 20;
m_nWidth = 40;
m_nHeight = 30;
m_nSize = 10;
// 游初始状态
m_nGameStatus = 0;
m_nDirect = 1;
m_nCount = 0;
// 初始化蛇身
m_aBody. RemoveAll ( );
m_aBody. Add ( CPoint (2, 7) );
m_aBody. Add ( CPoint (2, 6) );
m_aBody. Add ( CPoint (2, 5) );
m_aBody. Add ( CPoint (2, 4) );
// 计时器清零
m_nTime = 0;
m_nTime1 = 0;
// 初始化随机数发生器
srand ( (unsigned) time (NULL) );
// 产生一个目标
IniAim ( );
}
// 刷新游戏区域中pPoint处的一个小方格
void CSnakeView::ReDisplay(CPoint pPoint)
{
InvalidateRect (CRect (m_nLeft + pPoint. y * m_nSize, m_nTop + pPoint. x * m_nSize,
m_nLeft + (pPoint. y + 1) * m_nSize, m_nTop + (pPoint. x + 1) * m_nSize) );
}
(3)修改CSnakeView的构造函数,完成游戏的初始化。
CSnakeView::CSnakeView()
{
IniGame();
}
(4)在OnDraw中加入代码,显示游戏界面。
void CSnakeView::OnDraw(CDC* pDC)
{
CSnakeDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: add draw code for native data here
// 画游戏区域
pDC -> SelectStockObject (WHITE_BRUSH);
pDC -> Rectangle (CRect (m_nLeft - 1, m_nTop - 1, m_nLeft + m_nWidth * m_nSize + 1,
m_nTop + m_nHeight * m_nSize + 1) );
// 显示当前用时
CString uStr;
uStr. Format ("当前用时:% d", m_nTime);
pDC -> TextOut (m_nLeft + m_nWidth * m_nSize + 30, 40, uStr);
// 显示当前得分
uStr. Format ("当前得分: % d", m_nCount);
pDC -> TextOut (m_nLeft + m_nWidth * m_nSize + 30, 140, uStr);
// 显示目标
pDC -> SelectStockObject (LTGRAY_BRUSH);
pDC -> Rectangle (CRect (m_nLeft + m_pAim. y * m_nSize, m_nTop + m_pAim. x * m_nSize,
m_nLeft + (m_pAim. y + 1) * m_nSize, m_nTop + (m_pAim. x + 1 ) *m_nSize ));
// 画蛇
for (int i=0; i<= m_aBody. GetUpperBound (); i++)
{
CPoint uPoint = m_aBody. GetAt (i);
pDC -> Rectangle (CRect (m_nLeft + uPoint. y * m_nSize, m_nTop + uPoint. x * m_nSize,
m_nLeft + (uPoint. y + 1) * m_nSize, m_nTop + (uPoint. x + 1) * m_nSize) );
}
}
(5)为游戏菜单下的开始项的消息映射函数添加代码。
void CSnakeView::OnGameStart()
{
// 启动游戏,启动定时器
IniGame ( );
m_nGameStatus = 1;
SetTimer (1, 100, NULL);
Invalidate ( );
}
(6)为键盘按键消息处理函数添加代码。
// 根据按下的方向键设置代表不同方向的值
void CSnakeView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
switch (nChar)
{
case 38:
m_nDirect = 4;
break;
case 40:
m_nDirect = 3;
break;
case 37:
m_nDirect = 2;
break;
case 39:
m_nDirect = 1;
break;
}
CView::OnKeyDown(nChar, nRepCnt, nFlags);
}
(7)为定时器消息处理函数添加代码。
void CSnakeView::OnTimer(UINT nIDEvent)
{
m_nTime1++; // 计时
if (m_nTime1 == 10) // 达到1秒
{
m_nTime++;
m_nTime1 = 0;
Invalidate ( );
}
CPoint uPoint = m_aBody. GetAt (0); // 蛇头的位置
int uTag = 0; //是否失败
switch (m_nDirect) // 判断下一步蛇是否出界
{
case 1: // right
uPoint. y++;
if (uPoint. y >= m_nWidth)
uTag = 1;
break;
case 2: // left
uPoint. y--;
if (uPoint. y < 0)
uTag = 1;
break;
case 3: // down
uPoint. x++;
if (uPoint. x >= m_nHeight)
uTag = 1;
break;
case 4: // up
uPoint. x--;
if (uPoint. x < 0)
uTag = 1;
break;
}
if (uTag ==0) // 判断蛇是否碰到了自身
{
for (int i=0; i <= m_aBody.GetUpperBound(); i++)
{
CPoint uPoint1 = m_aBody. GetAt (i);
if (uPoint1. x == uPoint. x && uPoint1.y == uPoint. y)
{
uTag = 1;
break;
}
}
}
if (uTag == 0)
{
m_aBody. InsertAt (0, uPoint); // 新的蛇头的位置
ReDisplay (uPoint);
if (uPoint. x == m_pAim. x && uPoint. y == m_pAim. y) // 碰上目标
{
m_nCount++;
IniAim ( );
Invalidate ( );
}
else
{
CPoint uPoint1 = m_aBody. GetAt (m_aBody.GetUpperBound ( ) );
m_aBody. RemoveAt (m_aBody.GetUpperBound ( ) );
ReDisplay (uPoint1);
}
}
else // 游戏结束
{
KillTimer(1);
AfxMessageBox("Fail!");
}
CView::OnTimer(nIDEvent);
}
已发,请查收,躲弹高手和连连看
你的串号我已经记下,采纳后我会帮你制作