开始

游戏内有需求做多边形碰撞功能,但是接入box2d相对游戏的需求来说太重度了。所以准备自己实现碰撞。

确定多边形,必然要用到凸包的算法。在github上也找到了一些lua实现,但是这里的算法没有考虑多点共线的问题。所以准备自己实现

准备

这里提到的所有凸包,都指的平面上的。

  • 思路

凸包的具体定义,这里不赘述。一种通俗的说法,在木板上钉钉子,我们用一根麻绳绑住一个外面的钉子然后拉着麻绳绕所有钉子一圈这个麻绳围成的圈最后也构成了钉子组成的点集的凸包。

其实这种说法,和GiftWrapping算法的实现也有点异曲同工的感觉。 也就是先确定一个最边缘的点A,然后逆时针顺序寻找下一个与A构成的向量在逆时针方向上旋转角度最小的点。如果寻找到下一个点B,则继续用B点做基础点,寻找下一个点。直到寻找到的点为A,才会完成。

这里借用一下booble的图,画图好麻烦

了解更多GiftWrapping点这里,只不过这里的图示显示的是顺时针确定点的

  • 寻找下一个逆时针方向旋转角度最小的点

虽然可以直接通过两点计算出构成的向量的角度,然后进行排序比较,但是性能太差了。这里主要是用到向量叉积,因为不需要知道确定的旋转角度,只要能比较大小就可以了

向量叉积的特性:向量A乘以向量B,如果结果为正,则A逆时针旋转向B,则A向量相对于B逆时针方向旋转角度更小;否则为B更小; 如果为0,则共线。这里必须保证的是A旋转到B的角度必须小于180,否则会出错。这也就是为什么一开始要选一个最边缘的点做基础点的原因。

  • 共线的问题

如果两个向量叉积的结果为0,则两个向量共线了。因为这里只是确定凸多边形,而距离近的点必然是在基础点与较远点组成的边上,所以舍弃近距离点

实现

  • 获取第一个基础点

这里获取最左点,即X最小的点。如果又多个,取这些点中,y最小的点


  1. local function getLeftIndex(points)
  2. local size = #points
  3. local leftIndex = 1
  4. for i=2,size do
  5. if points[i].x < points[leftIndex].x
  6. then
  7. leftIndex = i
  8. end
  9. if points[i].x == points[leftIndex].x
  10. and points[i].y < points[leftIndex].y
  11. then
  12. leftIndex = i
  13. end
  14. end
  15. return leftIndex
  16. end
  • 比较一个哪个点与基础点形成的向量逆时针旋转更小

这里不多说,注释写的很明确了


  1. --叉积
  2. local function cross(p1,p2,p3)
  3. return (p2.x - p1.x)*(p3.y - p1.y)
  4. - (p2.y - p1.y)*(p3.x - p1.x)
  5. end
  6. --距离
  7. local function longer(startP,endP1,endP2)
  8. return ((endP2.x - startP.x)^2 + (endP2.y - startP.y)^2)
  9. - ((endP1.x - startP.x)^2 + (endP1.y - startP.y)^2)
  10. > Deviation
  11. end
  12. --判断(startP,midP)组成向量 a 是否顺时针旋转一个在 [0,180)区间的角度 β 后能与(startP,checkP)组成的向量 b 共线
  13. --如果β 在(0,180)范围,则返回true
  14. --如果β == 0 若,|b| > |a| 则,返回true 注意:如果midp checkP是在误差范围内的相同点,返回的是false
  15. local function isBetterVertice(startP,midP,checkP)
  16. local crossResult = cross(startP,midP,checkP)
  17. --crossResult0 则,逆时针
  18. -- =0 则,共线
  19. if crossResult < 0 then
  20. return true
  21. end
  22. if crossResult < Deviation
  23. and longer(startP,midP,checkP)
  24. then
  25. return true
  26. end
  27. return false
  28. end
  • 完成GiftWrapping算法

  1. local function giftWrapping(points)
  2. local pointCount = #points
  3. if pointCount < 3 then return nil end
  4. local leftIndex = getLeftIndex(points)
  5. local preIndex = leftIndex
  6. local hull = {}
  7. repeat
  8. table.insert(hull,#hull+1,points[preIndex])
  9. local bestIndex = 1
  10. for i=2,pointCount do
  11. if bestIndex == preIndex
  12. or isBetterVertice(points[preIndex],points[bestIndex],points[i])
  13. then
  14. bestIndex = i
  15. end
  16. end
  17. preIndex = bestIndex
  18. until (preIndex == leftIndex)
  19. return hull
  20. end

这个代码里顺带实现了GrahamScan,因为这个算法最麻烦的是确定一最左基础点后的剩余点按逆时针角度排序。但是排序要用的compare 方法,之前用到的isBetterVertice 完全可以胜任这个功能,就顺手写出来了。 GrahamScan相关

  • 其他

如有错误多包涵,轻喷

之后开始记录一下碰撞的东西,主要是多边形碰撞以及圆与多边形碰撞

转载请注明出处

http://www.cnblogs.com/boliu/p/4109120.html

凸包GiftWrapping GrahamScan 算法实现的更多相关文章

  1. 计算几何 二维凸包问题 Andrew算法

    凸包:把给定点包围在内部的.面积最小的凸多边形. Andrew算法是Graham算法的变种,速度更快稳定性也更好. 首先把全部点排序.依照第一keywordx第二keywordy从小到大排序,删除反复 ...

  2. 二维凸包 Graham扫描算法

    题目链接: http://poj.org/problem?id=1113 求下列点的凸包 求得凸包如下: Graham扫描算法: 找出最左下的点,设为一号点,将其它点对一号点连线,按照与x轴的夹角大小 ...

  3. 凸包Graham Scan算法实现

    凸包算法实现点集合中搜索凸包顶点的功能,可以处理共线情况,可以输出共线点也可以不输出而只输出凸包顶点.经典的Graham Scan算法,点排序使用极角排序方式,并对共线情况做特殊处理.一般算法是将共线 ...

  4. 计算几何--求凸包模板--Graham算法--poj 1113

    Wall Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 28157   Accepted: 9401 Description ...

  5. poj 2187 凸包加旋转卡壳算法

    题目链接:http://poj.org/problem?id=2187 旋转卡壳算法:http://www.cppblog.com/staryjy/archive/2009/11/19/101412. ...

  6. 《Algorithms算法》笔记:元素排序(4)——凸包问题

    <Algorithms算法>笔记:元素排序(4)——凸包问题 Algorithms算法笔记元素排序4凸包问题 凸包问题 凸包问题的应用 凸包的几何性质 Graham 扫描算法 代码 凸包问 ...

  7. Andrew算法求二维凸包-学习笔记

    凸包的概念 首先,引入凸包的概念: (有点窄的时候...图片右边可能会被吞,拉开图片看就可以了) 大概长这个样子: 那么,给定一些散点,如何快速地求出凸包呢(用在凸包上的点来表示凸包) Andrew算 ...

  8. POJ 1113 Wall 求凸包的两种方法

    Wall Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 31199   Accepted: 10521 Descriptio ...

  9. POJ 1113 Wall(计算几何の凸包)

    Description Once upon a time there was a greedy King who ordered his chief Architect to build a wall ...

随机推荐

  1. 【Shell Basic】Shell脚本编写规范

    shell脚本需要有较高的实用性.可维护.可阅读.方便他人阅读,因而需要建立一定的规范来操作 dream361@master:~$ cat test2.sh #!/bin/bash 所使用的bash程 ...

  2. WeQuant交易策略—简单均线

    简单双均线策略(Simple Moving Average) 策略介绍简单双均线策略,通过一短一长(一快一慢)两个回看时间窗口收盘价的简单移动平均绘制两条均线,利用均线的交叉来跟踪价格的趋势.这里说的 ...

  3. 语音激活检测(VAD)--前向神经网络方法(Alex)

    这是学习时的笔记,包含相关资料链接,有的当时没有细看,记录下来在需要的时候回顾. 有些较混乱的部分,后续会再更新. 欢迎感兴趣的小伙伴一起讨论,跪求大神指点~ VAD(ffnn神经网络)-Alex t ...

  4. Nuget安装nupkg文件

    问题描述: VS出现故障不能使用Manage NuGet Package自动下载安装DLL 解决方案: 直接官网下载相关需要的DLL 在VS中使用console加载本地下载的包安装 控制台code参考 ...

  5. 学习js的点点滴滴记录

    从安装完node.js后(里面自带了npm), 每个模块下都有个 package.json文件,在这个目录下打开cmd后 输入npm install 就是按照package.json里面的内容进行安装 ...

  6. jenkins 每个月1号到7号 一天执行一次

    在线Crontab表达式执行时间验证 / crontab执行时间计算 - aTool在线工具验证 http://www.atool.org/crontab.php 1.Build periodic a ...

  7. C++类静态成员与类静态成员函数

       当将类的某个数据成员声明为static时,该静态数据成员只能被定义一次,而且要被同类的所有对象共享.各个对象都拥有类中每一个普通数据成员的副本,但静态数据成员只有一个实例存在,与定义了多少类对象 ...

  8. 工具类:将其他编码类型转换成UTF-8或者其他类型的工具类

    将其他编码类型转换成UTF-8或者其他类型的工具类 public static String changeUTF(String str) { String newStr = null; try { n ...

  9. JS实现60s倒计时(亲测有效),及span标签如何使用和禁用onclick事件

    效果如下图:点击按钮出现60秒倒计时,60s内按钮不可用,倒计时到了时间方可再次点击获取. 另外还有一个知识点,只有input 及button这样的表单元素有disbale属性,如何设置是否可用属性的 ...

  10. 基于Vue全家桶制作的的高仿美团APP

    鸣谢:该项目核心部分参考了慕课网精英讲师ustbhuangyi的课程,其余部分高仿美团APP构建. 前端菜鸟项目,大佬们轻喷~ 美团外卖APP