编程游戏,做蛇。

3.关键点分析

蛇的活动区域由几个小方块组成。当这些小方块是灰色时,它们代表蛇的身体。设整个区域由m×n个正方形组成,左上角正方形的坐标为(0,0)。一条蛇由几个相邻的方块组成,这些方块的坐标按照从蛇头到蛇尾的顺序存储在一个数组中,代表蛇。当蛇在游戏区域“游动”时,对应的数组操作应该是将新的位置坐标插入数组头,同时删除数组中的最后一个元素。这个工作用一般的数组就可以,但是插入的时候需要自己移动数组中的元素;也可以使用CArray来完成这些任务,CArray的成员函数提供了所需的操作,更加简单。

解决问题

(1)创建一个新项目Snake。选择“单个文档”作为MFC向导的第一步,然后按“完成”结束。

(2)选择ResourceView窗口,打开菜单编辑器,在顶部菜单添加一个名为“游戏”的弹出菜单,在该菜单下添加一个名为“开始”的子菜单,其ID为ID_GAME_START,如图1所示。

图1菜单设计

(3)给刚刚在ClassWizard中编辑的菜单添加一个消息处理函数。打开ClassWizard并选择消息映射页。在类名中选择CSnakeView,在对象ID中选择ID_GAME_ START,在Messages中选择COMMAND,添加一个消息处理程序。

(4)在ClassWizard中增加键盘消息处理功能。打开ClassWizard并选择消息映射页。在类名中选择CSnakeView,在对象ID中选择CSnakeView,在Messages中选择WM_KeyDown,增加一个消息处理函数。

(5)在ClassWizard中为计时器消息添加一个处理程序。打开ClassWizard并选择消息映射页。在类名中选择CSnakeView,在对象ID中选择CSnakeView,在Messages中选择WM_Timer,添加一个消息处理程序。

(6)编辑生成的代码,完成程序。

4.源程序列表

(1)选择“类视图”窗口,双击CSnakeView类,并添加以下成员变量。并添加一个头文件:

# include & ltAfxtempl.h & gt

类CSnakeView:公共CView

{

//这里省略了系统生成的几行代码。

私人:

void重新显示(c point p point);

void IniGame();

void inia im();

int m_nLeft,m_nTop,m_nWidth,m_nHeight,m _ nSize//起始坐标、宽度/高度(方块数)和每个方块的大小。

int m _ nDirect//当前方向

// 1-右,2-左,3-下,4-上

CPoint m _ pAim//当前目标坐标

卡雷& ltCPoint,CPoint & gtm _ aBody//蛇身

int m _ nGameStatus//游戏状态:0-未开始,1-开始。

int m _ nCount//被吃掉的目标数

int m_nTime,m _ nTime 1;//使用时间

};

(2)在CSnakeView。Cpp文件,添加三个成员函数:IniAim、IniGame和ReDiaplay:

//这个函数随机生成一个目标给蛇吃。如果目标恰好与蛇身重合,就会再生。

void CSnakeView::IniAim()

{

int uX,uY;//目标位置

while (1)

{

uX = rand()% m _ n height;

uY = rand()% m _ nWidth;

int uTag = 0;// 0-与蛇身不重合,1-重合。

for(int I = 0;我& lt= m_aBody。GetUpperBound();i++)

{

CPoint uPoint = m_aBody。GetAt(I);

if(upoint . x = = UX | | upoint . y = = uy)//目标与蛇重合。

{

uTag = 1;

打破;

}

}

if (uTag == 0)

打破;

}

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 _ n direct = 1;

m _ nCount = 0;

//初始化蛇体

m_aBody。remove all();

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 _ ntime 1 = 0;

//初始化随机数生成器

srand((无符号)时间(空));

//生成目标

inia im();

}

//刷新游戏区pPoint处的一个小方块。

void CSnakeView::re display(c point pPoint)

{

invalidate repertiser(CRect(m _ nlef+pPoint。y * m_nSize,m_nTop + pPoint。x * m_nSize,

m _ nlef+(p点。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 = get document();

ASSERT _ VALID(pDoc);

// TODO:在此添加本机数据的绘制代码

//绘制游戏区域

pDC->;SelectStockObject(WHITE _ BRUSH);

pDC->;rectangle(CRect(m _ nlef-1,m_nTop - 1,m _ nlef+m _ nWidth * m _ nSize+1,

m _ nTop+m _ n height * m _ nSize+1));

//显示当前经过的时间。

CString uStr

UStr。Format("当前时间:% d ",m _ nTime);

pDC->;TextOut(m _ nlef+m _ nWidth * m _ nSize+30,40,uStr);

//显示当前分数。

UStr。格式("当前分数:% d ",m _ nCount);

pDC->;TextOut(m _ nlef+m _ nWidth * m _ nSize+30,140,uStr);

//显示目标

pDC->;SelectStockObject(lt gray _ BRUSH);

pDC->;rectangle(CRect(m _ nlef+m _ pAim。y * m_nSize,m_nTop + m_pAim。x * m_nSize,

m _ nlef+(m _ pAim。y + 1) * m_nSize,m_nTop + (m_pAim。x+1)* m _ nSize));

//画一条蛇

for(int I = 0;我& lt= m_aBody。GetUpperBound();i++)

{

CPoint uPoint = m_aBody。GetAt(I);

pDC->;rectangle(CRect(m _ nlef+uppoint。y * m_nSize,m _ nTop+uppoint。x * m_nSize,

m _ nlef+(up point。y + 1) * m_nSize,m _ nTop+(uppoint。x+1)* m _ nSize));

}

}

(5)为游戏菜单下开始项的消息映射功能添加代码。

void CSnakeView::OnGameStart()

{

//开始游戏,启动计时器。

IniGame();

m _ nGameStatus = 1;

SetTimer (1,100,空);

invalidate();

}

(6)为键盘按键消息处理功能添加代码。

//根据按下的箭头键设置代表不同方向的值。

void CSnakeView::OnKeyDown(UINT nChar,UINT nRepCnt,UINT nFlags)

{

开关(nChar)

{

案例38:

m _ n direct = 4;

打破;

案例40:

m _ n direct = 3;

打破;

案例37:

m _ n direct = 2;

打破;

案例39:

m _ n direct = 1;

打破;

}

CView::OnKeyDown(nChar,nRepCnt,nFlags);

}

(7)添加定时器消息处理函数的代码。

void CSnakeView::on timer(UINT nIDEvent)

{

m _ ntime 1++;//计时

If (m_nTime1 == 10) //达到1秒。

{

m _ ntime++;

m _ ntime 1 = 0;

invalidate();

}

CPoint uPoint = m_aBody。GetAt(0);//黑鱼的位置

int uTag = 0;//失败了吗?

Switch (m_nDirect) //判断蛇接下来是否出界。

{

案例1: //右

向上点。y++;

如果(uPoint。y & gt= m_nWidth)

uTag = 1;

打破;

案例2: //左

向上点。y-;

如果(uPoint。y & lt0)

uTag = 1;

打破;

情况3: //向下

向上点。x++;

如果(uPoint。x & gt= m_nHeight)

uTag = 1;

打破;

案例4: //上

向上点。x-;

如果(uPoint。x & lt0)

uTag = 1;

打破;

}

If (uTag ==0) //判断蛇是否碰到了自己。

{

for(int I = 0;我& lt= m_aBody。GetUpperBound();i++)

{

CPoint uPoint1 = m_aBody。GetAt(I);

if (uPoint1。x = = uPoint。x & amp& ampuPoint1.y == uPoint。y)

{

uTag = 1;

打破;

}

}

}

if (uTag == 0)

{

m_aBody。InsertAt (0,uppoint);//新蛇头的位置

重新显示(uppoint);

如果(uPoint。x == m_pAim。x & amp& amp向上点。y == m_pAim。y) //击中目标。

{

m _ ncount++;

inia im();

invalidate();

}

其他

{

CPoint uPoint1 = m_aBody。GetAt (m_aBody。GetUpperBound());

m_aBody。RemoveAt (m_aBody。GetUpperBound());

重新显示(upoint 1);

}

}

否则//游戏结束

{

kill timer(1);

AfxMessageBox("失败!");

}

CView::on timer(nIDEvent);

}