用MarchingSquares实现自定义刚体形状
今天我们来学习一个叫做MatchingSquares的新东东。它的功能是在运行时,通过一个名为run的静态函数,动态生成自定义形状的刚体。
是不是有点胡涂?run什么啊?别急,听我细细向你道来。
我们知道,在Nape中,处理标准圆形Circle的形状都被归为多边形Polygon,而要创建一个Polygon,必须指定多边形的每一个顶点。Nape这样做肯定是以偏概全了,因为还有很多形状是规则的、简单的,例如半圆形和椭圆形。我们可以利用高中时学习的几何公式计算出他们的轮廓,但是如果要指定这些形状的顶点,就不是一件容易的事情了。
今天我要将的MarchingSquares类就可以帮我们解决这个问题,结合这些规则的、简单的形状公式,在运行时创建自定义的形状。
实际上MarchingSquare是一个边缘检测算法,不过我没有安下心来,把这个算法研究明白,如果有兴趣的话,可以参考天地会译林军兄弟翻译的emanueleferonato的这篇文章http://bbs.9ria.com/thread-176310-1-1.html,当然,不看也没关系。
Nape中的MarchingSquares类很简单,只有一个静态函数run,不过这对绘制那些简单的形状来说,已经足够了(当然MarchingSquares能做的不只这些)。下面是这个run方法的结构:
static function run( iso:IsoFunctionDef, bounds:AABB, cellsize:Vec2, quality:Int = 2, subgrid:Vec2 = null, combine:Bool = true, output:GeomPolyList = null ):GeomPolyList
“哇!一大堆的参数,这些单词都不认识!”是不是感觉刚才的窃喜有点太早了。没事儿,我们来一起看看这些参数。
- iso:IsoFunctionDef:这是run函数的核心部份,也是我们定义刚体形状的地方。在官方API中这个参数是IsoFunctionDef类型,不过这是针对Haxe语言的,针对Flash中这个iso是IsoFunction类型的。
这里的IsoFunction并不是一个具体的类,而是接口(注意到没,他是”I”开头的,Flash中接口类都是以”I”开头的)。它的结构如下:
interface IsoFunction{ public iso(x:Number, y:Number):Number; }
我们需要新建一个类,应用这个IsoFunction接口,在iso函数中编写刚体形状公式。然将这个类的实例化对象赋值给iso参数。
- bounds:AABB:表示舞台中的某个区域,类似于AS3中的Rectangle类。这个区域和iso函数中遍历的区域相同
- cellsize:Vec2:分解bounds区域的单元格尺寸,这个尺寸越小,iso函数仿真出来的形状越逼真,相应的CPU消耗也越大
- quality:Int = 2:在单元格边缘应用递归进行插值运算的次数,当iso遍历的单元格尺寸较大时,可以通过增加整个值来提高边缘的精确度。
- subgrid:Vec2 = null: 设置此参数后,bounds区域首先以subgrid的尺寸分割,然后分割后的单元格再以cellssize的尺寸进行分割。所以subgrid的尺寸一定要比cellsize的尺寸大。
- combine:Bool = true: 当这个参数为true时,每个单元格里分割出来的多边形,会组合成一个大的多边形。
- output:GeomPolyList = null:设置整个参数后,生成的GeomPolyList会自动添加到(通过GeomPolyList的add函数)output中,而不会新建一个GeomPolyList对象。得到这个GeomPolyList之后,根据我们学过的GeomPoly方法创建多边形。http://www.ladeng6666.com/blog/2013/01/28/draw-nape-polygons-with-geompoly/
还是一头雾水吧,别急,跟上!
首先从第2个AABB参数开始,我们需要为run方法指定一个要计算的区域(如下图中的黑色方框),就好像我们要分析这一块的每个像素一样,不过run方法不会一个一个,一行一行的去分析每个像素,这个距离由第3个参数cellsize指定。
cellsize指定run方法在AABB区域遍历计算的间距,如下图,每个单元格的尺寸是10×10,那么run方法会以10个像素为间隔,检查每个像素是否在目标形状内,也就是图中的蓝色圆形。
回过头来在看iso参数。iso的参数必须是一个应用IsoFunction的类,同时这类中也有一个名为iso的函数,这个函数有两个参数x和y,这里的x、y即run函数在AABB中遍历的坐标。如上图举例,那么x的取值会是30、40、50、60、70、90、100,y值也是类似的。
另外iso函数还会返回一个Number值,这个返回值小于0,表示该点在形状内部,属于刚体一部份,大于0表示该点在形状外部,不在刚体范围内。
明白了前3个参数,后面的看参数说明就能明白了,如果还是不明白的话,就看看下面的示例吧。
下面的示例中,试着调整右上角对应的相关选项,看看每个参数的作用,以及不同的设置实现的不同效果。
[swfobject]1043[/swfobject]
完整的代码如下:
package learnNape { import flash.events.MouseEvent; import nape.phys.BodyType; import nape.phys.Body; import nape.shape.Polygon; import nape.geom.GeomPolyList; import nape.geom.Vec2; import nape.geom.AABB; import nape.geom.MarchingSquares; import learnNape.AbstractNapeTest; /** * @author yangfei */ public class T34_MarchingSuqare extends AbstractNapeTest { public var aabb:AABB; public var cellsize:Vec2; public var quality:Number; public var combine:Boolean; public var r:Number = 70; private var myISO:SemiCircleIso; private var control:ControlPanel; private var b:Body; override protected function onNapeWorldReady() : void { myISO = new SemiCircleIso(); cellsize = new Vec2(); control = new ControlPanel(this); createMarchingSquareBody(); } public function createMarchingSquareBody(e:MouseEvent=null) : void { myISO.setUP(275, 200, r); var geomList: GeomPolyList; geomList = MarchingSquares.run(myISO, aabb, cellsize,quality,null,combine); if(b !=null){ b.space = null; } b = new Body(BodyType.DYNAMIC); geomList.foreach(function (s:*):void{ b.shapes.push(new Polygon(s)); }); b.align(); b.space = napeWorld; } } }
掌握了MarchingSquares的用法之后,你可以在iso函数添加任意形状的公式,如椭圆、五角星等等。当然MarchingSquares的功能不只这么简单,下一节,我要教你用MarchingSquares动态的加载图像,并创建对应形状的刚体!
联系作者
哎呦不错哦!~又有新东西学了啊!~
百忙之中也要来捧拉登的场啊,哈哈
谢谢兄弟捧场!
不太明白那个内部形状,
拉登大叔,我想问一下怎么样才能固定body不让它旋转呢?
嘛!刚刚找到方法了,谢谢你的教程,十分有用!
感谢支持,我会努力为大家写出更多物理游戏开发相关的教程!
在Node里,liveBodies是活动的Body才会在列表里。那KINEMATIC类型,怎么才能去激活它呢?让它也在liveBodies的列表里。ladeng大叔的QQ能不能告诉我,我有好多问题。
我的QQ:328800655,欢迎和我交流
太厉害啦!值得我们学习
我来留下脚印