从0开始如何开发贪吃蛇游戏

      作为一款经典游戏,贪吃蛇相信很多朋友都玩过,规则简单易上手。但您有没有想过自己制作一款贪吃蛇游戏呢?鄙人最近有意学习MFC,所以先拿贪吃蛇练了一下手。接下来就介绍一下这款最简陋的贪吃蛇的开发历程。

      首先是开发环境的搭建,我的选择是visual studio2013,在微软官网上下载。安装好以后,打开vs13,选择菜单栏中的文件 ->新建 ->项目,然后在弹出列表中选择 visual c++下的MFC应用程序,输入自己的工程名称,我的是自制贪吃蛇,点击下一步,选择 单文档和MFC标准,然后直接点击完成,至此我们就生成了一个MFC空框架。但这个空框架什么也干不了,如果我们想在它的基础上构建一款游戏,那么自然需要我们手动DIY点什么,所以接下来,是真正的步入正轨:

      1)首先找到【自制贪吃蛇】的资源管理器,在头文件里面,找到自制贪吃蛇view.h的视图类,在C自制贪吃蛇view类体声明中,添加几个private变量,如图所示:

源代码是:

private:
	int  exist; //用它来记录蛇是否还活着
	bool flag;  //用它来记录地图中的食物是否被吃掉,是则重新生成食物
	int  head;  //它表示蛇头位置
	int  tail;  //它表示蛇尾位置
	int  trend; //它表示当前蛇的走向
	int  map_arr[61 * 61]; //这是蛇所能活动的整个地图
	const int ROW = 61;    //它表示地图的宽度


      2)接着在源文件中,打开自制贪吃蛇view.cpp文件,在里面找到C自制贪吃蛇view类的构造函数的定义,最后在函数体中添加这几行代码:

for (int i = 0; i < 61 * 61; i++) map_arr[i] = 0; //先把地图清空
	score = 0;
	flag = false;
	exist = 1;
	head = 62; //游戏开始,我们先把蛇放在左上角的位置
	tail = 62; //同上
	trend = ROW; //开始时蛇的默认走向是向下走
	map_arr[head] = 1;

      3)贪吃蛇作为一款单机游戏,它是需要玩家与机器交互的,既然是交互,那么程序就需要接收并处理外部消息,比如鼠标按下、键盘被按下等,在这里我们只需要得到键盘被按下的消息就行了,所以我们在框架中增加一个消息相应函数OnChar,用它来处理键盘被按下时的消息,具体做法如下:在菜单栏中点击工程->类向导,在弹出的对话框中的类名一栏中选择C自制贪吃蛇view,然后在消息栏之下找到WM_CHAR,如图所示:

再然后双击它,点击编辑代码,最后我们在新生成的OnChar函数体中添加如下代码:

switch (nChar)
    {
    case 119: //按下w表示向上走
        if (trend == ROW)  break;
        trend = -ROW;
        break;
    case 97:  //a向左走
        if (trend == 1)  break;
        trend = -1;
        break;
    case 115: //s向下走
        if (trend == -ROW)  break;
        trend = ROW;
        break;
    case 100: //d向右走
        if (trend == -1)  break;
        trend = 1;
        break;
    }


      4)我最早接触贪吃蛇游戏,是在我四叔的诺基亚手机上,借完他的手机时,我想和他开个玩笑:冲击贪吃蛇的榜单top1,然后把玩家ID改成【我是大傻逼】。。。记得当时我使用了N中策略:先是使用快蛇得分王,无奈咱反应跟不上,然后又用慢蛇蜗牛爬,又没那耐心,所以最后未能如愿(笑)。但快蛇与慢蛇,它们的速度是怎么调节的呢?好吧,在游戏设置里面。但我想您肯定明白我问的不是这个,所以,蛇的速度在内部代码层里,究竟是在怎么调节的呢??有一种方法是使用定时器策略:在微软下,通过user32.dll中的SetTimer()函数告诉系统一个时间间隔,然后系统每隔一段时间,就会触发我们的程序一次,既相当于告诉我们的应用程序中的时间相应函数:“嗨,又轮到你了,该干嘛干嘛吧”。。。具体的做法如下,首先,就像第三步骤中创建OnChar函数一样:打开项目中的类向导,在消息栏下找到WM_CREATE,然后双击,点击编辑代码,在新生成的OnCreate函数体中添加如下一行代码:

SetTimer(1, 130, NULL);

这里我们主要注意第二个参数,就是那个130,它就是我们设置的时间间隔,单位是毫秒。这里的意思也就是说,每隔0.13秒,我们的贪吃蛇游戏会更新一次。


      5)设置了时间间隔,那么我们的MFC程序就要捕获并处理这个时间间隔,就像我们创建OnChar函数一样,在类向导里面创建一个消息响应函数OnTimer(),然后在里面写上我们自己的代码。所以接下来才是整个程序的重点:如何让我们的小蛇走位、转弯、吃东西、变长变粗。。。可以说它才是这个MFC程序的灵魂所在。关于贪吃蛇,每个程序猿都可能有自己的一套实现算法。但我个人喜欢质朴简洁,不想把本来很简单的游戏规则用太复杂的算法实现,比如又是结构体又是类什么的,这款MFC的简单不费力,才是我坐在这里写这篇博客的主要原因。但接下来的代码,可能读者朋友阅读起来有些困难,所以我尽量多添加注释,以方便您理解:

srand((unsigned)time(NULL));
	int newhead1 = head , newhead2=head;
	newhead1 += trend;
	newhead2 += (trend * 2); //蛇前进使用的是二连进方式

	if (!exist) goto gameover;
	if (newhead2 < 0 || newhead2 >= 3700 || (trend*trend < 2 && newhead2 / ROW != head / ROW) || map_arr[newhead2] == 1)
		exist = 0; //检测蛇是否撞到墙壁或自己,是的话蛇死
	
	else
	{
		/***********检测蛇是否吃到食物,**********/
		if (-1 == map_arr[newhead2])
		{
			map_arr[newhead1] = 1;
			map_arr[newhead2] = 1;
			head = newhead2;
			score++;
			flag = false;
		}
		/***********是则加分并更新蛇头位置*******/
		
		else 
		{
			map_arr[newhead1] = 1;
			map_arr[newhead2] = 1;
			head = newhead2; //以上三行代码用于更新蛇头位置

			int temp = (map_arr[tail - ROW] > 0)*(tail - ROW) + (map_arr[tail + 1] > 0)*(tail + 1) +
				(map_arr[tail + ROW] > 0)*(tail + ROW) + (map_arr[tail - 1] > 0)*(tail - 1) - tail;
			map_arr[tail] = 0;
			map_arr[tail + temp] = 0;
			tail = tail + temp * 2; //以上代码用于更新蛇尾位置
		}

		if (false == flag) //检测食物是否被吃到,是的话随机生成一个新的食物
		{
			flag = true;
			int i, j;
		crefood:
			i = (rand() % 30) * 2 + 1;
			j = (rand() % 30) * 2 + 1;

			if (map_arr[i*ROW + j] > 0) goto crefood;
			else map_arr[i*ROW + j] = -1;
		}
	}
	gameover:
	OnDraw((CDC*)this); //调用该函数绘画新局面

对了,将上面这些代码插入到OnTimer函数之前,您需要先在C自制贪吃蛇view.cpp文件的前面,加上一句【#include <time.h>】,因为里面要用到随机生成函数。


      6)游戏的主逻辑都已经完成,但现在只有电脑能理解我们游戏的状态。如果我们玩家想要实时了解蛇的动态该怎么办?这就需要在视图类中绘制游戏了。一般的绘制,都是先做好N个素材图片,然后保存在N个path下,最后在需要的时候露下脸。但我说过我是个简单的家伙,所以这款贪吃蛇,不光算法简单,连外观界面也丑陋到家了【自己不会美工,又懒得去网络上抠图,将就凑合吧】:蛇身子我用的是大写字母O,食物用的是大写F,空地用的是空格,两面墙壁用的是简单的两条黑线。。。但麻雀虽丑,五脏俱全,读者朋友能够通过这个游戏了解这个程序的真谛,才是我感到欣慰的。闲言打住,正文开始:我把所有的绘制代码都放在一个OnDraw函数中了,这个函数不需要我们新建,在一个MFC程序生成时,vs在view类中会自动帮我们创建一个OnDraw函数,只需在函数体的适当位置添加如下代码即可:

CClientDC dc(this);

	CString nul("   ");
	CString snake("O");
	CString food("F");
	
	dc.MoveTo(450, 0);
	dc.LineTo(450, 450);
	dc.MoveTo(0, 451); //如果这里是450,因为相同区域重绘的缘故,墙壁会忽隐忽现
	dc.LineTo(451, 451);
	for (int y = 0; y < 30;y++)
	for (int x = 0; x < 30; x++)
	{
		if (1 == map_arr[(y * 2 + 1)*ROW + (x * 2 + 1)]) //绘画蛇身子
			dc.TextOut(x * 15, y * 15, snake);
		else if (-1 == map_arr[(y * 2 + 1)*ROW + (x * 2 + 1)]) //绘画食物
			dc.TextOut(x * 15, y * 15, food);
		else dc.TextOut(x * 15, y * 15, nul); //绘画空地
	}
	
	wchar_t sco_str[30];
	if (!exist)   swprintf_s(sco_str, L"游戏结束,您分数是:%d", score);
	else swprintf_s(sco_str, L"您已得了%d分", score);
	dc.TextOutW(451, 0, sco_str);

      7)最后的最后,让我们来看一下游戏效果:


  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值