Direct2D处理几何图形之间的碰撞检测(下)
一、概述
上一篇文章中我们介绍了几何图形与点的碰撞检测。在几何图形与点的位置关系比较简单:点在几何图形内、点在几何图形外、点在几何图形边框上三种情况。
而几何图形之间的位置关系就比较复杂了,大概有4种位置关系。这几种关系大家应该都知道:不相交、相交、包含、属于(被包含)。
也就是今天本篇文章的重点内容,给大家介绍的关于几何图形之间的碰撞检测。
二、几何图形之间的碰撞检测
1.函数介绍
首先还是要先介绍ID2D1Geometry接口的一个成员函数CompareWithGeometry,这个函数是我们接下来要进行几何图形之间的碰撞检测的关键,(这个函数有4个重载,这里由于篇幅原因只分别介绍重载中的一个,其实原理都一样,其他的重载大家可以去msdn官网了解):
ID2D1Geometry::CompareWithGeometry函数介绍
功能:描述此几何对象与指定几何对象之间的交集。
参数
inputGeometry 要测试的几何对象。
inputGeometryTransform 要应用到 inputGeometry的转换。
relation 此方法返回时,relation表示此几何对象与inputGeometry的关系。
返回值:如果该方法成功,则返回 S_OK。 否则,将返回错误代码。
ID2D1Geometry::CompareWithGeometry函数的第3个参数用来返回此几何对象与inputGeometry的关系,这个参数是一个枚举类型D2D1_GEOMETRY_RELATION,我们简单看一下它的枚举成员都有哪些:
typedef enum { D2D1_GEOMETRY_RELATION_UNKNOWN = 0, // 无法确定 D2D1_GEOMETRY_RELATION_DISJOINT = 1, // 不相交 D2D1_GEOMETRY_RELATION_IS_CONTAINED = 2, // 属于(被包含) D2D1_GEOMETRY_RELATION_CONTAINS = 3, // 包含 D2D1_GEOMETRY_RELATION_OVERLAP = 4 // 相交 } D2D1_GEOMETRY_RELATION;
从上面的介绍就可以看出,几何图形之间的位置关系有4种:不相交、属于、包含和相交。枚举的第一个成员D2D1_GEOMETRY_RELATION_UNKNOWN在MSDN中是这样介绍的,需要注意的是任何D2D方法都不会返回该值。
介绍完我们需要用到的函数,我们接下来就可以使用这个函数进行几何图形之间的碰撞检测工作了。
2.首先解决一个小问题
我们先看一下下面的函数使用示例:
hr = pGeometry1->CompareWithGeometry( pGeometry2, transMatrix2, &relation );
在这段代码中,出现一个问题:在使用CompareWithGeometry函数判断几何图形之间的位置关系时,只允许参数中的几何对象pGeometry2传入对应的变换,而不允许自身的几何对象pGeometry1传入自己的变换。这样就出现了我们使用这个函数不能满足的情况:如果自身的几何对象也有变换,那么就不能直接使用CompareWithGeometry函数了。
但是,我们可以间接地弥补一下这个缺陷。
在ID2D1Geometry中,有一个子类接口ID2D1TransformedGeometry,它用来表示已转换的几何对象。有了这个接口,我们就可以先使用几何对象pGeometry1和它对应的变换创建一个已转换的几何对象transGeometry,之后再用transGeometry判断其与几何对象pGeometry2的位置关系,这样就可以完美解决这个问题了。
代码如下:
// 使用pGeometry1和transMatrix1创建transGeometry对象 HRESULT hr = pD2DFacytory->CreateTransformedGeometry( pGeometry1, transMatrix1, &transGeometry ); if (SUCCEEDED(hr)) { // 使用transGeometry和pGeometry2做位置判断 hr = transGeometry->CompareWithGeometry( pGeometry2, transMatrix2, &relation ); // 使用relation做接下去的工作 // ... }
3.代码实现
根据上面的函数介绍和问题分析,我在代码中封装了一个函数IntersectsWithGeometry,用来判断几何图形之间的位置关系。代码如下:
// 检测两个几何图形是否碰撞 D2D1_GEOMETRY_RELATION IntersectsWithGeometry(ID2D1Factory* pD2DFacytory, ID2D1Geometry* pGeometry1, D2D1_MATRIX_3X2_F& transMatrix1, ID2D1Geometry* pGeometry2, D2D1_MATRIX_3X2_F& transMatrix2) { D2D1_GEOMETRY_RELATION relation; ID2D1TransformedGeometry* transGeometry = NULL; HRESULT hr = pD2DFacytory->CreateTransformedGeometry( pGeometry1, transMatrix1, &transGeometry ); if (SUCCEEDED(hr)) { hr = transGeometry->CompareWithGeometry( pGeometry2, transMatrix2, &relation ); if (SUCCEEDED(hr)) { SafeRelease(&transGeometry); return relation; } SafeRelease(&transGeometry); } return D2D1_GEOMETRY_RELATION_UNKNOWN; }
此函数在使用时,传入D2D工厂,几何对象1,几何对象1的变换矩阵,几何对象2,几何对象2的变换矩阵 作为参数列表,返回几何对象1和几何对象2的位置关系。
现在开始我们的测试,在测试代码中,我还是使用了五角星作为几何对象比较的标准,分别创建了五角星、圆角矩形、矩形、圆形和三角形。原始状态即不做碰撞检测时,五个几何对象的初始状态如下图,五角星作为比较的标准,它的颜色为蓝色,其余四个几何对象的初始颜色均为白色。
这是不做碰撞检测时的效果图:
进行测试时,使用五角星分别去与这四个几何对象进行比较。为了让大家从绘制的结果图中清楚地看出这些几何对象的位置关系,在我的代码中,会根据比较之后返回的结果,来设置它们绘制的颜色,不同的位置关系的几何对象绘制颜色不同。
我使用的颜色标准为:
a.不相交 红色
b.属于 橙色
c.包含 黄色
d.相交 绿色
那么比较之后再进行绘制,我们最后的效果图是这样的:
上图的测试的结果表明:
(1)五角星 属于 圆角矩形
(2)五角星 相交于 矩形
(3)五角星 包含 圆形
(4)五角星 不相交于 三角形
在这里完整代码代码就不贴出了,有兴趣的朋友可以点击此处下载Demo源码,Demo源码是Direct2DTests目录下的D2DCollisionDetectionBetweenGeometrys文件。
三、结语
这样,我们所作的几何图形之间的碰撞检测工作就完成了。其实准确来说,我们做的不是几何图形之间的碰撞检测工作,而是几何图形之间的位置关系判断。因为碰撞检测绝大部分情况只是需要判断几何图形是不是相交,而我们这里不仅可以判断它们是不是相交,还可以更精确的判断出它们是不是包含或被包含(属于)的关系。只不过我最近在用D2D写引擎,说碰撞检测比较习惯了。
但是,通过这篇文章,我们可以知道,D2D对几何图形之间的关系判断足够可以满足我们的需求了。