用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个像素为间隔,检查每个像素是否在目标形状内,也就是图中的蓝色圆形。

Matchsquare

回过头来在看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动态的加载图像,并创建对应形状的刚体!

点击下载源文件

联系作者

公众号:拉小登 | 微博:拉登Dony | B站:拉小登Excel

文章分类 > Nape

12 Replies to “用MarchingSquares实现自定义刚体形状”

  1. 在Node里,liveBodies是活动的Body才会在列表里。那KINEMATIC类型,怎么才能去激活它呢?让它也在liveBodies的列表里。ladeng大叔的QQ能不能告诉我,我有好多问题。

回复 RainYuTian

您的电子邮箱地址不会被公开。 必填项已用*标注