Cocos2dx3.2 CrazyTetris 单线裁剪 对于判断消除的思考(一)

由于不是规则的俄罗斯方块,在消除时,很可能产生不规则的图形,因此,如何判断是否达到消除条件,以及进行方块的裁剪将是本游戏的一个关键问题。

我在做这个游戏时,采用的是最直接的方法,也就是最笨的方法,直接进行裁剪判定。如果有比较好的算法,希望大家可以和我交流。

首先,接上一篇,由于我们创建的刚体模型,需要是凸多边形,因此,每个初始方块都由四个正方形小方块构成。如图:

而本游戏中,方块的旋转应该是任意的(而不是原版游戏中每次旋转都是90度)。因此,当上面的方块停落时,很有可能是这样的方向:

因此,该方块在消除时,可能会发生这样的消除:

这是就需要进行裁剪,同时要满足裁剪后内部刚体还是凸多边形。其实很明显,我们只要分别裁剪里面的几个小方块即可。显而易见,一个凸多边形在经过一条直线切割后,生成的图形还是凸多边形。

接下来就是这个裁剪该如何做的问题了。

虽然每次消除是消除两条直线中间的部分,但是无论是两条直线分割图形和一条直线分割图形其实并无本质区别。因此,这种情况下,只考虑单线裁剪。

现在,我们开始考虑单线情况下的裁剪,如图:

裁剪线与图形整体可能存在相交和不想交问题,即使相交,也可能只与部分内部方块相交,而对于其中的一个方块,还要考虑其和哪一个边相交,故而去裁剪那一个边。

由于最终是按边裁剪,因此我们不考虑整体、不考虑构成整体的小方块,而是考虑构成小方块的几条边。

因此可能会出现以下几种情况,其中蓝点代表起始点,绿点代表终止点(一个边是一条向量):

这种情况是两点在裁剪线上部,不需要进行裁剪。

这种情况是两点在裁剪线下部,也不需要进行裁剪。

这种情况是裁剪线与边相交,两点分列裁剪线两侧,但是起始点(蓝点)在下,终止点(绿点)在上。

此时需要进行裁剪,起始点(蓝点)和裁剪点(裁剪线与边相交的点)成为裁剪完成后下边图形的边,裁剪点(裁剪线与边相交的点)和终止点(绿点)为裁剪完成后上边图形的边。

这种情况是裁剪线与边相交,两点分列裁剪线两侧,但是起始点(蓝点)在上,终止点(绿点)在下。

此时需要进行裁剪,起始点(蓝点)和裁剪点(裁剪线与边相交的点)成为裁剪完成后上边图形的边,裁剪点(裁剪线与边相交的点)和终止点(绿点)为裁剪完成后下边图形的边。

我们没有必要逐一去计算交点,然后判断。因此可以使用上面的方法,先判断情况,然后再根据情况适时进行计算裁剪。

这里使用的思想是,裁剪完成后,生成上部图形和下部图形。裁剪线上部编码为0,下部编码为1。以顺时针方向取方块中的两点,起始点和终止点。直接根据两点的y值,判断两点的编码值。

如果编码值相同,说明两点在裁剪线的同一侧,此时边必定没有与裁剪线相交。因此,不进行裁剪。但是需要根据编码值,将这两点按顺序归入上部图形或者下部图形。

如果编码值不同,进行裁剪,得到裁剪点。然后根据起始点位置,如果起始点编码为0,在裁剪线上部,则将起始点和裁剪点顺次归入上部图形中,将裁剪点和终止点顺次归入下部图形中;反之,则将起始点和裁剪点顺次归入下部图形中,将裁剪点和终止点顺次归入上部图形中。

算法思想大致如此,代码实现如下:

Vector<BaseBlock *> * BaseBlock::bottomLineCutting(float y)
{
	//定义上下多边形集
	std::vector<Vec2 *> * topBlock;
	std::vector<Vec2 *> * bottomBlock;
	std::vector<int> * topVecsNumber;
	std::vector<int> * bottomVecsNumber;

	topBlock = new std::vector<Vec2 *>();
	bottomBlock = new std::vector<Vec2 *>();
	topVecsNumber = new std::vector<int>();
	bottomVecsNumber = new std::vector<int>();

	bool isCutting = false;

	for(int i=0; i<shapeAmount; i++)
	{
		//定义上、下二段裁剪点集
		std::vector<Vec2> * topShape;
		std::vector<Vec2> * bottomShape;

		topShape = new std::vector<Vec2>();
		bottomShape = new std::vector<Vec2>();

		//逐边裁剪
		for(int j=0; j<shapeVecAmount->at(i); j++)
		{
			Vec2 startPoint = this->coordinateSpin(shapeVecs->at(i)[j]);
			Vec2 endPoint = this->coordinateSpin(shapeVecs->at(i)[(j+1)%shapeVecAmount->at(i)]);

			int cStart = 0;
			int cEnd = 0;

			if((fabs(startPoint.y - y) < 1e-6) && (fabs(endPoint.y - y) < 1e-6))
			{
				if(topShape->size() != 0) cStart = cEnd = 0;
				else cStart = cEnd = 1;
			}
			else if(fabs(startPoint.y - y) < 1e-6)
			{
				if(endPoint.y - y < 1e-6) cStart = cEnd = 1;
			}
			else if(fabs(endPoint.y - y) < 1e-6)
			{
				if(startPoint.y - y < 1e-6) cStart = cEnd = 1;
			}
			else
			{
				if(startPoint.y - y < 1e-6) cStart = 1;
				if(endPoint.y - y < 1e-6) cEnd = 1;
			}

			if(cStart == cEnd)
			{
				//两顶点在同一边,无需裁剪
				if(cStart == 0)
				{
					//顶点在上边,记录到上边顶点集
					topShape->push_back(coordinateGoBack(startPoint));
//					topShape->push_back(coordinateGoBack(endPoint));
				}
				else
				{
					//顶点在下边,记录到下边顶点集
					bottomShape->push_back(coordinateGoBack(startPoint));
//					bottomShape->push_back(coordinateGoBack(endPoint));
				}
			}
			else
			{
				//两顶点在不同边,需要进行裁剪
				float cutting_x = startPoint.x + (endPoint.x - startPoint.x) * (y - startPoint.y) / (endPoint.y - startPoint.y);
				isCutting = true;
				if(cStart == 0)
				{
					//起点在上,终点在下时
					topShape->push_back(coordinateGoBack(startPoint));
					topShape->push_back(coordinateGoBack(Vec2(cutting_x, y)));

//					bottomShape->push_back(coordinateGoBack(endPoint));
					bottomShape->push_back(coordinateGoBack(Vec2(cutting_x, y)));
				}
				else
				{
					//起点在下,终点在上时
					bottomShape->push_back(coordinateGoBack(startPoint));
					bottomShape->push_back(coordinateGoBack(Vec2(cutting_x, y)));

//					topShape->push_back(coordinateGoBack(startPoint));
					topShape->push_back(coordinateGoBack(Vec2(cutting_x, y)));
				}
			}
		}

		//添加至方块集
		Vec2 * topTempShape = new Vec2[topShape->size()];
		Vec2 * bottomTempShape = new Vec2[bottomShape->size()];

		for(int index = 0; index < topShape->size(); index++)
		{
			topTempShape[index] = topShape->at(index);
		}
		for(int index = 0; index < bottomShape->size(); index++)
		{
			bottomTempShape[index] =  bottomShape->at(index);
		}

		if(topShape->size() != 0)
		{
			topBlock->push_back(topTempShape);
			topVecsNumber->push_back(topShape->size());
		}

		if(bottomShape->size() != 0)
		{
			bottomBlock->push_back(bottomTempShape);
			bottomVecsNumber->push_back(bottomShape->size());
		}
	}

	if(isCutting)
	{
		Vector<BaseBlock *> * baseBlockSet = new Vector<BaseBlock *>();

		DrawNode * draw = DrawNode::create();

		Texture2D * texture = this->getTexture();

		RenderTexture * texture1 = RenderTexture::create(this->getContentSize().width, this->getContentSize().height);

		auto base1 = BaseBlock::createWithTexture(this->getTexture());
		auto base2 = BaseBlock::createWithTexture(this->getTexture());
		base1->initForm(topBlock, topVecsNumber, topBlock->size());
		base2->initForm(bottomBlock, bottomVecsNumber, bottomBlock->size());
		base1->setPosition(this->getPosition());
		base2->setPosition(this->getPosition());
		base1->setRotation(this->getRotation());
		base2->setRotation(this->getRotation());
		baseBlockSet->pushBack(base1);
		baseBlockSet->pushBack(base2);

		return baseBlockSet;
	}
	else
	{
		if(bottomBlock->size() != 0)
		{
			Vector<BaseBlock *> * baseBlockSet = new Vector<BaseBlock *>();
			auto base = BaseBlock::createWithTexture(this->getTexture());

			baseBlockSet->pushBack(base);

			return baseBlockSet;
		}
		else
		{
			return NULL;
		}
	}

}

关于制作游戏相关其他博客的目录,我放在利用Cocos2dx3.2制作重力版俄罗斯方块(Crazy
Tetris)

时间: 2024-12-29 11:39:52

Cocos2dx3.2 CrazyTetris 单线裁剪 对于判断消除的思考(一)的相关文章

Cocos2dx CrazyTetris 双线伪裁剪算面积 对于判断消除的思考(二)

上一篇主要讲了我对裁剪消除算法的思考,这一篇的主题是计算单行覆盖面积,以此来确定是否达到了裁剪条件. 就像之前所说的,在该游戏中,基本方块都由四个小方块构成,四个小方块的尺寸均是25*25.因此游戏区域是宽可容纳10个方块,高可容纳20个方块.即250*500.每行的间距均是25. 因此,现在的问题就是,如何判定在这个宽250,高25的区域内,方块所占的面积.如果能够计算出其面积,而这个区域的总面积为250 * 25 = 6250,那么就可以据此来判断是否满足消除条件.例如:面积 > 6000.

SQL优化之列裁剪和投影消除

列裁剪 对于没用到的列,则没有必要读取它们的数据去浪费无谓的IO 比如我们有一张表table1,它含有四列数据(a,b,c,d).当我们执行查询select a from table1 where c 10时,我们可以清晰的看到,table1中只有a,c两列被用到了.分别是Selection算子用到c列和Projection算子用到a列.那么DataSource读取数据时,b,d两列则不需要读取,可以裁剪掉. 那么都有哪些算子与列有关系呢?综合我们多年来使用SQL的经验来看,Selection(

拍照/从相册读取图片后进行裁剪的方法

本范例实现的是用户可以通过拍照.相册获取图片,然后进行裁剪,最后将结果保存在IamgeView中.当然你可以选择将结果同时存放在sd卡中,作为以后的缓存. 思路: 1.通过拍照获取图片 进入系统自带的相机界面——>拍照——>保存在sd卡中——>读取sd卡的文件进行裁减.PS:裁剪前先判断是否获取到图片了 2.通过系统相册获取图片 进入系统相册——>找到图片——>进行裁减.PS:裁剪前线判断是否获取到系统的图片了 接下来贴上实现方法: 1.进入拍照界面或者相册的方法,获取信息后

图形学复习3——观察和裁剪

图形学复习 CH5 二维观察 5.1 窗口.视口和二维观察流水线 窗口是指世界坐标系中要显式的区域称,窗口定义了显式的内容 视口是指窗口映射到显示器的区域,视口定义了在什么位置显示 通常将世界坐标系中一部分区域映射到设备坐标系的操作称为观察变换,二维观察变换流水线如下: 模型局部坐标→ 世界坐标 → 观察坐标 -(通过窗口视口描述)→ 规范化观察坐标 → 设备坐标 5.2 裁剪窗口到规范化视口的映射 通常所说的裁剪窗口就是窗口,即世界坐标系中要显示的区域,我们可以选择裁剪窗口的不同形状.大小以及

1020. Tree Traversals (25) PAT甲级真题

之前我看了这道题,实在是看不懂网上的解题答案,他们的具体思路基本上就是通过后续遍历和中序遍历,直接推出层次遍历. 我苦思冥想了半天,是在没看懂这种思路,于是想了一个笨点的但是也比较好理解的思路,通过后续和中序,先推出整个二叉树,再考虑 对二叉树层次遍历. 本题还有一点要注意的时在输出结果的末尾,如果使用了类似 pirntf("%d ",data); 这样的格式是不对的,一定要对末尾进行判断消除最尾端的空格. 首先最核心的部分是通过两次遍历反推回二叉树:这里的思路是,后续遍历的最末尾,一

Cocos2d-x 3.4 之 消灭星星 &gt; 第三篇(终) &lt;

***************************************转载请注明出处:http://blog.csdn.net/lttree******************************************** 满满的泪啊, 从5月22日写的第一篇,于6月6日结束. 中间各种课程.上机.大作业穿插,焦头烂额的, 最后终于做出来差不多的样子了... PS:写博客这几天,宿舍一直停电状态...真是醉了.. 本篇实现的功能: > 粒子特效 > 音乐音效 > 漂浮文字

cocos2d-x 3.2 之 三消类游戏——万圣大作战 (第五篇)

***************************************转载请注明出处:http://blog.csdn.net/lttree******************************************** 第五篇 前面已经把游戏主体逻辑搞定,已经可以玩了. 就是可玩性略差,所以本文就是来 修饰 游戏的. 本文主要内容: > 四消的处理 > 消除的特效 > 最高分的记录 > 音乐.音效的添加 好的,废话不多说,let's go! 1. 四消精灵的出现

Cesium原理篇:3最长的一帧之地形(3:STK)

有了之前高度图的基础,再介绍STK的地形相对轻松一些.STK的地形是TIN三角网的,基于特征值,坦白说,相比STK而言,高度图属于淘汰技术,但高度图对数据的要求相对简单,而且支持实时构建网格,STK具有诸多好处,但确实有一个不足,计算量比较大,所以必须预先生成.当然,Cesium也提供了一个Online的免费服务,不过因为是国外服务器,所以性能和不稳定因素都不小.好的东西自然得来不易,所以不同的层次,根据具体的情况选择不同的方案,技术并不是唯一决定因素,甚至不是主要因素. CesiumTerra

Qt-俄罗斯方块

声明: 仅个人小记 整个有效项目的文件已经上传csdn: http://download.csdn.net/detail/qq_25847123/9718822 目录: 1.前言 2.效果展示 3.主要代码 4.开发日志 5.小结 1.前言 整个程序的完成花了我不少时间, 有许多知识细节不够清楚,边学边做,断断续续完成的.之前有用C++直接做过一次俄罗斯方块,界面简陋,是在控制台运行的.这次用Qt实现,沿用了之前的总体思想,技术细节有所改动. 2.效果展示 刚开始: 开始游戏: 背景界面随着分数