面向对象方法编一个简易的控制台版贪吃蛇(三)

之前关于贪吃蛇的内容参见:面向对象方法编一个简易的控制台版贪吃蛇(一)点击打开链接    和     面向对象方法编一个简易的控制台版贪吃蛇(二)点击打开链接

现在,我们开始写碰撞。

一说起增加什么功能,我首先想到的还是写一个新的方法。但是这个方法写在哪个类里面似乎永远都是一门学问,你可以写在一个现有的类里面,也可以再创建一个类。即使对于这样一款小游戏也需要考虑,因为如果写的不科学的话,可能后续实现起来就会显得很困难。

一开始,我采取的办法是是额外创建一个类,取名叫GameFunction,专门用来实现游戏当中的功能。但是,这样做的话。我想要调用GameFunction里的函数的话,就不能用现有主函数里的各个来调用GameFunction里面的方法了(也调用不到)。那怎么办,我就想把主函数改一下,直接new 一个GameFunction类的对象,同时在GameFunction的构造函数内部将地图、蛇和食物初始化。但是这样的话,刚刚创建的GameFunction对象又无法访Map、Food和Snake里各自的show函数,这样就无法将东西显示在控制台上。又该怎么办?个人能力有限,这个问题我没能解决,得想其他的办法,如果有哪位朋友能够给我提个意见,我将不胜感激。

那就只能在现有的类里面添加这些方法了。

我又选取了一个方法,那就是将用来判断是否碰撞的方法(isCollide)分别写在Map类和Food类里面。之所以分开写的原因,是因为当蛇碰到Map和Food时的操作不尽相同,分开写便于写主函数。

Map.cpp中的isCollide方法,其实也不难,就是把所有map结点都遍历一遍,看有没有结点和它重合。

bool Map::isCollide(BaseNode* node)
{
	for (auto Map : m_map)
	{
		if (Map->x == node->x&&Map->y == node->y)
		{
			return true;
		}
	}
	return false;
}
同理,Food.cpp当中的isCollide方法:

bool Food::isCollide(BaseNode* node)
{
	for (auto Food : m_food)
	{
		if (Food->x == node->x&&Food->y == node->y)
		{
			return true;
		}
	}
	return false;
}

现在需要处理的是当蛇碰撞到不同的东西时所产生的不同效果。当蛇碰到地图Map的时候,蛇就死了,这个比较好处理,只需当碰上之后,直接break掉跳出主函数中的while循环就行了;当蛇碰到食物的时候,蛇身子增加,同时当前食物消失并随机生成另外一个食物。

先考虑蛇身子增加的问题。我们可以在Snake类里面增加一个函数getLonger专门用来实现蛇身子增加的功能。

void Snake::getLonger()  //在蛇的尾部增加一个结点
{
	m_snake.push_back(new BaseNode(m_snake.back()->x, m_snake.back()->y, TYPE_SNAKE));
}

现在我们对主函数进行一下修改,然后测试一下,看看碰撞是否管用。写主函数的时候我们又发现一个问题:isCollide函数的参数是BaseNode*类型,主函数中的类型没有BaseNode*类型,因此传参的时候存在类型不匹配的问题。我们这样解决这个问题:在Snake类里面定义一个getSnake函数,专门用来获取蛇的结点。

vector<BaseNode*>*  getSnake() { return &m_snake;}

等我们在主函数调用isCollide函数的时候,我们就可以这么写:

1.当判断地图和蛇碰撞时:

if( map->isCollide(snake->getSnake()->front())

{

……

}

碰撞的时候,只需判断蛇头是否碰上就可以了(因为按照正常的玩法,蛇的其它部位也不可能碰到)

2.同理当判断食物和蛇是否碰撞时:

if(food->isCollide(snake->getSnake()->front()))

{

……

}

这样,主函数就被修改成了这样:

#include<iostream>
#include"Controller.h"
#include"baseNode.h"
#include"Map.h"
#include"Snake.h"
#include"Food.h"
#include<Windows.h>

void main()
{
	
	Snake* snake = new Snake(5, 6);
	Food* food = new Food(10, 9);
	Map* map = new Map();

	snake->showSnake();
	snake->setKey(VK_UP, VK_DOWN, VK_LEFT, VK_RIGHT);

	food->showFood();
	map->showMap();
	while (true)
	{
		Sleep(150);
		snake->controlSnake();

		if (map->isCollide(snake->getSnake()->front()))
		{
			break;
		}
		if(food->isCollide(snake->getSnak
利用面向对象方法,实现贪吃蛇。 1. 利用面向对象的思想实现——一个食物对象、一个蛇对象、一个游戏总控对象。 2. 在使用××.prototype= {}重写原型对象的时候,一定要加上一句constructor:该对象。不然会造成实例化出来的实例的constructor为object。 3. 在underscore中,使用_.random(a,b)即可获得a-b中的一个随机数。 4. 在求食物的随机位置的时候,用到了panel.clientHeight/this.height - 1) * this.height。 原理是使用盒子的高度/小球的高度,可以算得最多放多少个小球。因为要控制小球不能超过边界,所以总数量要减去1,数量×高度即为随机位置的最大值。 5. 在蛇对象中,用body数组存放蛇身体每一个部分对象。蛇的绘制过程就是遍历body,在面板上绘制。 6. 蛇的移动分为两部分。 ① 蛇节移动到前一个蛇节的位置。直到蛇头后一个蛇节移动到蛇头的位置。 ② 根据direction判断蛇头如何移动。 注意:在游戏绘制的过程中,界面的每一次绘制都要**删除**之前的绘制,不然会叠加到一起。 7. 在蛇的闭包中建一个局部数组,存储蛇对象,可以更加方便的删除操作。 8. 只有在原型对象中的方法和属性,外界是可以调用的。 9. 蛇的移动(动画)必然需要定时器协助。定时器的时间,即象征着刷新速度,也就是难度。 10. this所在的函数在哪一个对象中,this就指向谁。单独写一个函数的时候,如果调用之前对象的this,需要备份指针(将对象的this赋值给另一个变量)。 11. JavaScript原生的键盘按下事件(keydown) 中,事件有一个keyCode属性,其值代表按下的键。其中:37—left、38—top、39—right、40—bottom。 12. 边界控制。通过判断蛇头与最大X和Y的关系,判断是否碰到边界。 13. confirm()方法用于显示一个带有指定消息和确认及取消按钮的对话框。 14. window.location.reload(); 重新加载当前文档 15. window.close() 方法用于关闭浏览器窗口。 16. 与食物的碰撞检测:如果蛇头和食物坐标重叠,将蛇尾添加到body中。并重新绘制一个食物点,将之前的食物删掉。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值