Nape柔体教程(3)
实际上,前一节介绍的圆形柔体,严格意义上是下面做图所示的这么一个东西。这并不能算是一个柔体,对吧。我们真正想要的是一个类似于水滴或气球的柔体效果(如下面右图所示)。
讲到这里,我禁不住又要感谢以下Luca了,谢谢他给我们带来这么强大的Nape物理引擎,以及这么多强大而使用的示例。如果你看过官网中的softBody源码,但是不知道如何下手,那就准备好感谢拉登大叔吧,我将详细向大家讲解这个逼真的柔体效果。
Luca的柔体效果实现思路可以简单的总结为”气球”法。什么意思呢?具体的讲,就是用多个刚体组合成目标柔体形状的轮廓,然后由内向外对每个刚体施加作用力,就像气球中的气会把气球鼓起来一样,如下图所示。没听懂?没关系,跟上拉登大叔的脚步,我们一点点把它吃透。
官网中的softBody是基于Polygon的,相对复杂一些。我们还是循序渐进,从上一节的圆形柔体开始,不过思路上都是相同的。具体的步骤如下:
用多个刚体segmentBody组成柔体形状的轮廓,并用PivotJoint链接起来。这一步和上一节的第1步是类似的,但是细节上还是有很多不同的,注意集中精力,差异点来啦。
a) 柔体的轮廓完全由segmentBody的形状组成,而非PivotJoint关节。
上一节的圆形轮廓是通过关节勾勒出来的,但是关节并不参与碰撞检测,所以我在开头就说了,这并不能算是一个刚体。
本节的轮廓完全是由刚体组成,就像是一个切成多个片段的圆环,如下所示。
之前我们学习过,任何非标准圆形的刚体都是Polygon,所以要创建这些片段,我们要计算出刚体四个角的顶点innerP1,innerP1,outP1和outP2。这个任务并不难,通过内外半径、角度和三角函数可以轻松出来。代码如下:
for (var i:int = 0; i< segmentsNum; i++){ angle1 = angleGap * i; angle2 = angleGap * (i+1); outP1 = new Vec2(Math.sin(angle1)*radius+cx, Math.cos(angle1)*radius+cy); outP2 = new Vec2(Math.sin(angle2)*radius+cx, Math.cos(angle2)*radius+cy); innerP1 = new Vec2(Math.sin(angle1)*innerRadius+cx, Math.cos(angle1)*innerRadius+cy); innerP2 = new Vec2(Math.sin(angle2)*innerRadius+cx, Math.cos(angle2)*innerRadius+cy); var body:Body = new Body(); var poly:Polygon = new Polygon([outP1,outP2,innerP2,innerP1]); body.shapes.add(poly); body.align(); body.compound=softBody; outPoints.push(outP1); innerPoints.push(innerP1); bodyList.push(body); }
compound类是一个刚体组合类,把刚体body添加到compound中,然后将这个compound对象添加到napeSpace中,同样可以实现对每个刚体的物理模拟。
简单的讲,你可以把他想象成一个Sprite类,讲其他的Sprite对象添加到一个Sprite容器中,然后把这个容器再添加到舞台上,我们可以看到容器里的所有Sprite对象。
b) 两个相邻segmentBody刚体之间用两个PivotJoint关节链接,而不是一个。
这两个关节分别连接相邻两个刚体的顶点位置。那么为什么要用两个而不是一个关节链接呢?
用多个刚体组合成柔体轮廓非常重要的一点,就是在柔体发生形变时,保证轮廓的平滑。只有一个关节时,相邻刚体之间容易出现重迭或开口。相反如果用两个关节分别连接刚体的内外顶点,同时固定外关节,这样就可以防止形状轮廓发生重迭或开口。如下图所示:
如刚才讲的,右图两个关节中,固定外关节,是指设置关节的stiff属性为true,而内关节的stiff属性为false,同时通过修改damping和frequency属性,让内关节可以自由拉伸。
var outJoint : PivotJoint = new PivotJoint(prevBody, currBody, prevBody.worldPointToLocal(currentPoint), currBody.worldPointToLocal(currentPoint)); outJoint.space = napeWorld; currentPoint=innerPoints[j]; var innerJoint : PivotJoint = new PivotJoint(prevBody, currBody, prevBody.worldPointToLocal(currentPoint), currBody.worldPointToLocal(currentPoint)); innerJoint.stiff = false; innerJoint.damping = 1; innerJoint.frequency = 10; innerJoint.space = napeWorld;
好了,所有的步骤大致就是这些了。下面我们来看看具体的示例效果。
画外音:还是有由内向外的作用力没讲呢!!
到这里效果貌似已经不错了,所以作用力的问题,我们下一节再讲!睡觉去咯!!
[swfobject]1005[/swfobject]
完整代码如下:
package learnNape { import flash.events.MouseEvent; import nape.phys.Compound; import flash.events.Event; import nape.phys.BodyType; import nape.shape.Polygon; import nape.constraint.PivotJoint; import nape.phys.BodyList; import nape.geom.Vec2; import ldEasyNape.LDEasyNape; import nape.phys.Body; import learnNape.AbstractNapeTest; /** * @author yangfei */ public class T31_SoftBody2 extends AbstractNapeTest { public function T31_SoftBody2(gravity : Number = 600) { } private var softBodiesList:Vector.<Compound> = new Vector.<Compound>(); override protected function onNapeWorldReady() : void { createSoftBody(200,100,50,20,10); createSoftBody(300,100,50,20,10); LDEasyNape.createBox(275, 200, 200, 10,true); } private function createSoftBody(cx:Number,cy:Number,radius:Number,segmentsNum,thickness:Number) : void { var softBody:Compound = new Compound(); var bodyList:BodyList = new BodyList(); var angleGap:Number=Math.PI*2/segmentsNum; var innerRadius :Number = radius-thickness; var angle1:Number, angle2:Number; var outP1:Vec2,outP2:Vec2; var innerP1:Vec2,innerP2:Vec2; var outPoints:Vector.<Vec2>= new Vector.<Vec2>(); var innerPoints:Vector.<Vec2>= new Vector.<Vec2>(); for (var i:int = 0; i< segmentsNum; i++){ angle1 = angleGap * i; angle2 = angleGap * (i+1); outP1 = new Vec2(Math.sin(angle1)*radius+cx, Math.cos(angle1)*radius+cy); outP2 = new Vec2(Math.sin(angle2)*radius+cx, Math.cos(angle2)*radius+cy); innerP1 = new Vec2(Math.sin(angle1)*innerRadius+cx, Math.cos(angle1)*innerRadius+cy); innerP2 = new Vec2(Math.sin(angle2)*innerRadius+cx, Math.cos(angle2)*innerRadius+cy); var body:Body = new Body(); var poly:Polygon = new Polygon([outP1,outP2,innerP2,innerP1]); body.shapes.add(poly); body.align(); body.compound=softBody; outPoints.push(outP1); innerPoints.push(innerP1); bodyList.push(body); } var prevBody:Body, currBody:Body; var currentPoint:Vec2; for (var j:int=0;j< bodyList.length; j++){ prevBody=bodyList.at((j-1+bodyList.length)%bodyList.length); currBody=bodyList.at(j); currentPoint=outPoints[j]; var outJoint : PivotJoint = new PivotJoint(prevBody, currBody, prevBody.worldPointToLocal(currentPoint), currBody.worldPointToLocal(currentPoint)); outJoint.space = napeWorld; currentPoint=innerPoints[j]; var innerJoint : PivotJoint = new PivotJoint(prevBody, currBody, prevBody.worldPointToLocal(currentPoint), currBody.worldPointToLocal(currentPoint)); innerJoint.stiff = false; innerJoint.damping = 1; innerJoint.frequency = 10; innerJoint.space = napeWorld; } softBody.space = napeWorld; } } }
联系作者
很期待柔体贴图教程啊
这个有bug,使劲拖一拖,就缠到一起了
是的呢,不过,我现在也没有想到比较好的解决方案,正在努力中…
求问拉登叔,你的代码高亮插件是哪个啊??
Crayon ,网上搜一下,很流行的!
Nape柔体教程(3)
Thank you for this article.
Box2d也可以实现这样的效果吧?比如说大叔你用box2d做的那个锁链桥就有些类似啊
是的,Nape和Box2D原理上都是一样的,所以Nape能实现的效果,Box2D基本也都可以实现!
那nape有浮力方面的类吗?
有的,Nape的Shape对象都有一个fluidEnabled属性,设置为true,就可以模拟浮力效果啦!