贪吃蛇的游戏开发
这个实验要求是实现贪吃蛇游戏的基本功能,屏幕上随机出现一个食物,键盘上下左右控制蛇的移动,吃到食物后蛇的身体长一点,得分增加,蛇碰到边界或者咬到自己的尾巴,蛇死亡,游戏结束。
实验准备
整个过程都是在Cview类中完成的,所以查阅了一些相关的内容。
本次实验主要涉及到的知识点有:
(1)CView类中的消息响应
(2)控件的消息响应
(3)基于CView类内的具体游戏实现
(4)游戏图形的实现用CDC类实现。
接下来我会介绍一下主要步骤
主要步骤
交互页面的设计
先添加“游戏”和“帮助两个标题”,再在两个标题下分别添加“开始”,“暂停”,“继续”,“结束”,“关于贪吃蛇”控件,如图所示:
接下来最关键的是画贪吃蛇游戏的页面,设计颜色,大小,形状
利用windows给我们提供的CDC来进行画图
用灰色将页面进行填充,然后画一个801*501的矩形,就是贪吃蛇活动的界面
分别对蛇和出现的事物进行颜色填充
这里我设计的是粉色的
初始化蛇有三个节点
要写出每个节点的坐标
这就是设计好的界面了
函数
首先为刚才添加的“开始”,“暂停”,“继续”,“退出”,“关于贪吃蛇”添加消息响应函数
这里使用的是自带的AfxMessageBox函数的默认格式,有一个文本和一个确定按钮还有一个警告标识符
比如这是设置好的“关于贪吃蛇”的效果
按“确定”或者“X”都是一样的可以关闭弹出的框
OnKeyDown函数对玩家的键盘输入进行判断,当蛇是向上移动时,你按“上”,“下”都是无效的
OnTimer函数是最主要的函数,因为我们判断蛇移动,暂停,继续都是根据时间来的
设置不同长度下蛇的速度
判断蛇是否死亡
判断是否具有吃掉食物的条件
判断食物是否被蛇吃掉,如果吃掉了就生成,注意食物的生成不能出界,也不能生成在蛇身上
判断蛇的行走方向
判断得分情况
最后是添加音效,这里我添加了一个音效,使得蛇吃掉食物的时候会发出声音
添加音乐主要就是用到了playSound函数
这样贪吃蛇的游戏开发就算完成了
完整代码
#include "stdafx.h"
#include "snake.h"
#include "snakeDoc.h"
#include "snakeView.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
#include "Mmsystem.h"
#pragma comment(lib,"Winmm.lib")
BOOL playSound(LPCSTR pasSound,HMODULE hmod,DWORD fdwSound);
// CSnakeView
struct Snake
{
int x;
int y;
int len;
int direct;
}Snake[50];
struct Food
{
int x;
int y;
int isfood;
}Food;
IMPLEMENT_DYNCREATE(CSnakeView, CView)
BEGIN_MESSAGE_MAP(CSnakeView, CView)
//{{AFX_MSG_MAP(CSnakeView)
ON_WM_KEYDOWN()
ON_WM_RBUTTONDOWN()
ON_WM_TIMER()
ON_COMMAND(IDM_START, OnStart)
ON_COMMAND(IDM_PAUSE, OnPause)
ON_COMMAND(IDM_EXIT, OnExit)
ON_COMMAND(IDM_CONTINUE, OnContinue)
ON_COMMAND(ID_APP_ABOUT, OnAppAbout)
//}}AFX_MSG_MAP
// Standard printing commands
ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview)
END_MESSAGE_MAP()
// CSnakeView construction/destruction
CSnakeView::CSnakeView()
{
// TODO: add construction code here
}
CSnakeView::~CSnakeView()
{
}
BOOL CSnakeView::PreCreateWindow(CREATESTRUCT& cs)
{
// TODO: Modify the Window class or styles here by modifying
// the CREATESTRUCT cs
return CView::PreCreateWindow(cs);
}
// CSnakeView drawing
void CSnakeView::OnDraw(CDC* pDC)
{
CSnakeDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: add draw code for native data here
CBrush backBrush(RGB(100,100,100));
CBrush* pOldBrush = pDC->SelectObject(&backBrush);
CRect rect;
pDC->GetClipBox(&rect);
pDC->PatBlt(rect.left, rect.top, rect.Width(), rect.Height(),PATCOPY);
pDC->SelectObject(pOldBrush);
pDC->Rectangle(100,100,801,501);
oninit();
}
// CSnakeView printing
BOOL CSnakeView::OnPreparePrinting(CPrintInfo* pInfo)
{
// default preparation
return DoPreparePrinting(pInfo);
}
void CSnakeView::OnBeginPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
{
// TODO: add extra initialization before printing
}
void CSnakeView::OnEndPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
{
// TODO: add cleanup after printing
}
// CSnakeView diagnostics
#ifdef _DEBUG
void CSnakeView::AssertValid() const
{
CView::AssertValid();
}
void CSnakeView::Dump(CDumpContext& dc) const
{
CView::Dump(dc);
}
CSnakeDoc* CSnakeView::GetDocument() // non-debug version is inline
{
ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CSnakeDoc)));
return (CSnakeDoc*)m_pDocument;
}
#endif //_DEBUG
// CSnakeView message handlers
void CSnakeView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
// TODO: Add your message handler code here and/or call default
switch(nChar)
{//对玩家的按键进行判断,你如蛇往左的时候,玩家按右或左是无效的
case VK_UP:if(Snake[0].direct!=2)Snake[0].direct=1;break;
case VK_DOWN:if(Snake[0].direct!=1)Snake[0].direct=2;break;
case VK_LEFT:if(Snake[0].direct!=4)Snake[0].direct=3;break;
case VK_RIGHT:if(Snake[0].direct!=3)Snake[0].direct=4;break;
}
CView::OnKeyDown(nChar, nRepCnt, nFlags);
}
void CSnakeView::OnRButtonDown(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
CString str;
str.Format("%d,%d",point.x,point.y);
AfxMessageBox(str);
CView::OnRButtonDown(nFlags, point);
}
void CSnakeView::OnTimer(UINT nIDEvent)
{
// TODO: Add your message handler code here and/or call default
CDC *pDC=GetDC();
//设置不同长度下蛇的速度
if(Snake[0].`len==3)SetTimer`(1,270,NULL);
if(Snake[0].len==9)SetTimer(1,200,NULL);
if(Snake[0].len==15)SetTimer(1,150,NULL);
if(Snake[0].len==30)SetTimer(1,100,NULL);
//判断得分
CString soure;
soure.Format(" 得分: %d! ",(Snake[0].len-3)*10);
//撞界判断
if(Snake[0].x*20<=118||Snake[0].y*20<=118||Snake[0].x*20>=762||Snake[0].y*20>=462)
{
KillTimer(1);
AfxMessageBox(soure);
// s=0;
}
//蛇身相撞判断
if(Snake[0].len>3)
for(int sn=Snake[0].len-1;sn>0;sn--)
{
if(Snake[0].x*20==Snake[sn].x*20&&Snake[0].y*20==Snake[sn].y*20)
{
KillTimer(1);
AfxMessageBox(soure);
// s=0;
}
}
pDC->SelectStockObject(WHITE_PEN);
pDC->Rectangle(Snake[Snake[0].len-1].x*20,Snake[Snake[0].len-1].y*20,(Snake[Snake[0].len-1].x+1)*20,(Snake[Snake[0].len-1].y+1)*20);
for(int i=Snake[0].len-1;i>0;i--)
{
Snake[i].x=Snake[i-1].x;
Snake[i].y=Snake[i-1].y;
}
//行走方向判断
if(Snake[0].direct==1)Snake[0].y--;
if(Snake[0].direct==2)Snake[0].y++;
if(Snake[0].direct==3)Snake[0].x--;
if(Snake[0].direct==4)Snake[0].x++;
pDC->SelectStockObject(BLACK_PEN);
CBrush DrawBrush=(RGB(255,217,255));
CBrush *Drawbrush=pDC->SelectObject(&DrawBrush);
pDC->Rectangle(Snake[0].x*20,Snake[0].y*20,(Snake[0].x+1)*20,(Snake[0].y+1)*20);
pDC->SelectObject(DrawBrush);
//判断吃食物的条件,撞到就吃
if(Snake[0].x*20==Food.x*20&&Snake[0].y*20==Food.y*20)
{
Snake[0].len++;
Food.isfood=1;
sndPlaySound("D:\\按钮类7.wav", SND_ASYNC);
Snake[Snake[0].len-1].x=Snake[Snake[0].len-2].x;
Snake[Snake[0].len-1].y=Snake[Snake[0].len-2].y;
}
//如果食物被吃了 就生成
if(Food.isfood==1)
{
srand((unsigned)time(NULL));
do
{
for(int isfo=Snake[0].len-1;isfo>=0;isfo--)
if(Snake[0].x*20==Snake[isfo].x*20&&Snake[0].y*20==Snake[isfo].y*20)
{
Food.x=rand()%25;
Food.y=rand()%25;
}
}
while(Food.x*20<150||Food.y*20<150||Food.x*20>730||Food.y*20>430);
pDC->Rectangle(Food.x*20,Food.y*20,(Food.x+1)*20,(Food.y+1)*20);
Food.isfood=0;
}
CView::OnTimer(nIDEvent);
}
void CSnakeView::OnInitialUpdate()
{ //初始化蛇有四个节点,每个节点的坐标
CView::OnInitialUpdate();
Snake[0].x=15;
Snake[0].y=10;
Snake[1].x=16;
Snake[1].y=10;
Snake[2].x=17;
Snake[2].y=10;
Snake[0].direct=3;
Snake[0].len=3;
Food.isfood=1;//食物默认1有
// TODO: Add your specialized code here and/or call the base class
}
CSnakeView::oninit()
{//函数画一个矩形,用当前的画笔画矩形轮廓,用当前画刷进行填充
CDC *pDC=GetDC();
CBrush DrawBrush=(RGB(255,217,255));
CBrush *Drawbrush=pDC->SelectObject(&DrawBrush);
for(int i=0;i<=Snake[0].len-1;i++)
pDC->Rectangle(Snake[i].x*20,Snake[i].y*20,(Snake[i].x+1)*20,(Snake[i].y+1)*20);
pDC->SelectObject(DrawBrush);
}
void CSnakeView::OnStart()
{
SetTimer(1,3000,NULL);
AfxMessageBox(" 开始游戏 \n\n 3秒后开始游戏 ");
}
void CSnakeView::OnPause()
{
KillTimer(1);
AfxMessageBox(" 休息一下吧 ");
}
void CSnakeView::OnExit()
{
AfxMessageBox(" 退出咯 ");
exit (0);
}
void CSnakeView::OnContinue()
{
SetTimer(1,150,NULL);
}
void CSnakeView::OnAppAbout()
{
AfxMessageBox(" 键盘控制蛇移动 \n\n 每吃到一个食物加10分 \n\n 吃得越多移动得越快 ");
}
最后
这个代码是我参考了一些网上的例子以及一些Cview编程的知识写出来的,可能会有一些bug,我会尽量的去完善它。
然后这个例子真的很有用,建议初学者都能自己动手尝试一下。