用GeomPoly绘制Nape多边形刚体

上一节我们学习了组合创建Nape多边形刚体,但是只是简单图形的拼拼凑凑是很难满足我们的需求的,不是吗?即便可以,但是要定义好每个图形的尺寸、位置和角度,也是一个很复杂的过程。

是不是想到Box2D的原生法创建刚体了?是的,今天我们要学习的就是Nape的原生法。

创建多边形刚体

和Box2D类似,Nape创建多边形刚体也是用Polygon对象实现,看过Polygon类的构造函数(我在简单的Nape刚体中有讲过)你应该可以猜到,这个过程并不难:

  1. 将要创建的多边形刚体顶点存储到一个变量中,这个变量可以是由Vec2对象组成的Array,也可以是Vector.<Vec2>或Vec2List类型。
  2. 将保存了顶点的数组作为第一个参数传入到Polygon的构造函数中,创建多边形刚体即可。

大致的代码如下:

		//创建存储多边形顶点的数组
		var vertice:Vector.<Vec2> = new Vector.<Vec2>();
		vertice.push(new Vec2(10, 10), new Vec2(10, 20), new Vec2(20, 30), new Vec2(20, 20));
		//将数组传递给polygon图形
		var polygon:Polygon = new nape.shape.Polygon(vertice);代码

把存储多边形顶点的变量,改成在鼠标移动过程中动态生成,就可以实现下面示例中动态绘制刚体的效果。

在下面的示例中点击并拖动鼠标开始绘制多边形,松开鼠标创建多边形刚体。

[swfobject]780[/swfobject]

测试一下,效果是不是很棒?但是多测试几次,问题就暴露出来了。当绘制的刚体是凹多边形时,Nape无法进行准确的碰撞模拟。怎么办呢?

解决凹多边形问题

在Box2D中我们用b2Separator类解决了引擎无法准确仿真凹多边形碰撞的问题。Nape也有一个类似功能的类(是不是很兴奋),叫做GeomPoly,怎么用呢?看到这里,不要急着用b2Separator的方法尝试,让我细细为你道来。

GeomPloy与b2Separator的用法不同,它的构造函数很简单,只有一个vertices数组,保存了多边形的顶点,vertices可以是由Vec2对象组成的Array,也可以是Vector.<Vec2>、Vec2List或GeomPoly类型。下面的代码用上面示例的顶点数组创建了一个GeomPoly对象:

			//用生成的顶点创建GeomPoly对象
			var geomPoly:GeomPoly=new GeomPoly(vertice);

GeomPoly中的monotoneDecomposition、simpleDecomposition和triangularDecomposition方法,可以将多边形分割成多个小的凹多边形,然后以GeomPolyList类型返回。这几个方法的用法如下:

  • monotoneDecomposition:就复杂的多边形分解成多个小多边形,但是分解后的多边形依然可能会是凹多边形
  • simpleDecomposition:将复杂的多边形分解成多个简单的凸多边形
  • triangularDecomposition:将复杂的多边形分解成多个三角形

这三个方法都会返回一个GeomPolyList对象,这个对象中包含了分解后的简单的多边形,然后我们可以遍历这个GeomPolyList对象,用组合方法将这个简单的多边形添加到刚体中。

说了这么多,有点晕了,下面我们来看看实际的代码吧!

			var body:Body=new Body();
			//用生成的顶点创建GeomPoly对象
			var geomPoly:GeomPoly=new GeomPoly(vertice);
			//用GeomPoly的convexDecomposition方法将多边形分解成多个小图形,并保存到polyShapeList对象中
			var polyShapeList:GeomPolyList=geomPoly.convexDecomposition();
			//遍历这些小的图形,将这些小图形用组合法添加到body.shapes属性中
			polyShapeList.foreach(function(shape:*):void{
				body.shapes.push(new Polygon(shape));
			});

下面是将上面示例中多边形用triangularDecomposition()分解后的效果,同样的,点击并拖动鼠标,开始绘制多边形,松开鼠标后,可以看到复杂的刚体被分解成了多个小三角形,而且凹多边形也可以准确的模拟碰撞了。

[swfobject]781[/swfobject]

完整的代码和注释如下:

package 
{
	import flash.display.Sprite;
	import flash.events.MouseEvent;
	import flash.geom.Point;

	import nape.geom.GeomPoly;
	import nape.geom.GeomPolyList;
	import nape.geom.Vec2;
	import nape.phys.Body;
	import nape.phys.BodyType;
	import nape.phys.Material;
	import nape.shape.Polygon;

	[SWF( width="550", height="400", frameRate="60")]
	public class T13_CreatePolygonBodyWIthGeomPoly extends AbstractNapeTest
	{
		private var vertice:Vector.<Vec2>;
		private var layer:Sprite;
		private var isDrawing:Boolean=false;
		private var curPoint:Point;
		private var prePoint:Point;

		public function T13_CreatePolygonBodyWIthGeomPoly()
		{
			//继承父类构造函数,创建Nape世界
		}
		override protected function onNapeWorldReady():void
		{
			//保存顶点的Vector数组
			vertice=new Vector.<Vec2>();
			//实例化绘制线条的图层
			layer=new Sprite();
			addChild(layer);
		}

		private function createPolygon():void
		{
			var body:Body=new Body();
			//用生成的顶点创建GeomPoly对象
			var geomPoly:GeomPoly=new GeomPoly(vertice);
			//用GeomPoly的convexDecomposition方法将多边形分解成多个小图形,并保存到polyShapeList对象中
			var polyShapeList:GeomPolyList=geomPoly.convexDecomposition();
			//遍历这些小的图形,将这些小图形用组合法添加到body.shapes属性中
			polyShapeList.foreach(function(shape:*):void{
				body.shapes.push(new Polygon(shape));
			});
			//纠正刚体的重心
			body.align();
			body.space=napeWorld;
		}
		override protected function mouseEventHanlder(event:MouseEvent):void
		{
			switch(event.type){
				case MouseEvent.MOUSE_DOWN:
				{
					isDrawing=true;
					//设置线条样式
					layer.graphics.lineStyle(2);
					layer.graphics.moveTo(mouseX, mouseY);
					//定义鼠标点为起点,并添加到Vector类型的verticesList数组中
					curPoint = new Point(mouseX, mouseY);
					prePoint = curPoint.clone();
					vertice.push(new Vec2(mouseX, mouseY));
					break;
				}
				case MouseEvent.MOUSE_UP:
				{
					isDrawing=false;
					layer.graphics.clear();
					//在鼠标位置随机创建一个圆形或矩形刚体
					createPolygon();
					//清空存储顶点的Vector数组
					vertice = new Vector.<Vec2>();
					break;
				}
				case MouseEvent.MOUSE_MOVE:
				{
					//如果鼠标没有按下,isDrawing为false,跳出
					if(!isDrawing) return;
					layer.graphics.lineTo(mouseX, mouseY);
					//记录鼠标坐标为当前curPoint,并计算curPoint与上一个点prePoint的距离
					curPoint = new Point(mouseX, mouseY);
					var distance:Number = Point.distance(prePoint, curPoint);
					//当前后两个点的距离大于线段距离时,添加顶点
					if (distance >= 30) {
						//记录顶点到verticesList数组中
						vertice.push(new Vec2(mouseX, mouseY ));
						prePoint = curPoint.clone();
					}
					break;
				}
			}
		}
	}
}

 源文件下载地址

联系作者

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

9 Replies to “用GeomPoly绘制Nape多边形刚体”

  1. 我有一个问题想问一下:就是在nape中如何绘制曲线或者说如何绘制很平滑的刚体呢?如果用GeomPoly的话,通过获得鼠标位置绘制得总不尽人意。不知道你有好的方法没有,拉登大叔。
    谢谢了。

  2. 谢谢拉登大叔的回复。其实我想做的很简单,flash中的curveTo方法可以画很流畅的贝茨曲线。我最近总是考虑,能不能通过鼠标滑动,动态绘制由曲线包围构成的刚体。现在可以做到,但我就是觉得效果很粗糙。
    也不知道说明白了没有。

  3. 谢谢了拉登。那我把重点放在曲面图形的描绘上了。最近在做游戏,要用到这方面的东西。

  4. 8字我是这么解决的,把createPolygon()里的代码换成:
    private function createPolygon():void
    {
    var geomPoly:GeomPoly = new GeomPoly(vertice);

    //解决8字报错问题,先生成简单多边形,再分解凹多边形
    var polyShapeListStep1:GeomPolyList = geomPoly.simpleDecomposition();
    var polyShapeListStep2:GeomPolyList = new GeomPolyList();
    polyShapeListStep1.foreach(function(shape:*):void
    {
    polyShapeListStep2.merge(shape.convexDecomposition());
    });
    var body:Body = new Body();
    polyShapeListStep2.foreach(function(shape:*):void
    {
    body.shapes.push(new Polygon(shape));
    });
    body.align();
    body.space = napeWorld;
    }
    不过这样在画极其复杂图形的时候还是会报错,无法再进行之后下一个刚体顶点组的分形操作…不知道有没有更好的办法?

发表回复

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