用GeomPoly绘制Nape多边形刚体
上一节我们学习了组合创建Nape多边形刚体,但是只是简单图形的拼拼凑凑是很难满足我们的需求的,不是吗?即便可以,但是要定义好每个图形的尺寸、位置和角度,也是一个很复杂的过程。
是不是想到Box2D的原生法创建刚体了?是的,今天我们要学习的就是Nape的原生法。
创建多边形刚体
和Box2D类似,Nape创建多边形刚体也是用Polygon对象实现,看过Polygon类的构造函数(我在简单的Nape刚体中有讲过)你应该可以猜到,这个过程并不难:
- 将要创建的多边形刚体顶点存储到一个变量中,这个变量可以是由Vec2对象组成的Array,也可以是Vector.<Vec2>或Vec2List类型。
- 将保存了顶点的数组作为第一个参数传入到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; } } } } }
源文件下载地址
联系作者
很棒啊,拉登
我有一个问题想问一下:就是在nape中如何绘制曲线或者说如何绘制很平滑的刚体呢?如果用GeomPoly的话,通过获得鼠标位置绘制得总不尽人意。不知道你有好的方法没有,拉登大叔。
谢谢了。
你想要的是下面链接中的效果吗?
http://napephys.com/samples.html#swf-DestructibleTerrain
如果是,别着急,我会陆续推出这个教程的!
谢谢你的关注!
谢谢拉登大叔的回复。其实我想做的很简单,flash中的curveTo方法可以画很流畅的贝茨曲线。我最近总是考虑,能不能通过鼠标滑动,动态绘制由曲线包围构成的刚体。现在可以做到,但我就是觉得效果很粗糙。
也不知道说明白了没有。
这样讲好了,只要你能绘制出来的图形,我都可以转换成刚体,即便是曲线
谢谢了拉登。那我把重点放在曲面图形的描绘上了。最近在做游戏,要用到这方面的东西。
请教下,nape如何做精确碰撞呢?特别是带动画的精确碰撞?
请问绘制8字形的图形时会报错, 是什么原因?
得到的 GeomPolyList 为 null
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;
}
不过这样在画极其复杂图形的时候还是会报错,无法再进行之后下一个刚体顶点组的分形操作…不知道有没有更好的办法?