Nape柔体教程(3)

实际上,前一节介绍的圆形柔体,严格意义上是下面做图所示的这么一个东西。这并不能算是一个柔体,对吧。我们真正想要的是一个类似于水滴或气球的柔体效果(如下面右图所示)。

futiao3water3

讲到这里,我禁不住又要感谢以下Luca了,谢谢他给我们带来这么强大的Nape物理引擎,以及这么多强大而使用的示例。如果你看过官网中的softBody源码,但是不知道如何下手,那就准备好感谢拉登大叔吧,我将详细向大家讲解这个逼真的柔体效果。

Luca的柔体效果实现思路可以简单的总结为”气球”法。什么意思呢?具体的讲,就是用多个刚体组合成目标柔体形状的轮廓,然后由内向外对每个刚体施加作用力,就像气球中的气会把气球鼓起来一样,如下图所示。没听懂?没关系,跟上拉登大叔的脚步,我们一点点把它吃透。

softbodyluca130509

官网中的softBody是基于Polygon的,相对复杂一些。我们还是循序渐进,从上一节的圆形柔体开始,不过思路上都是相同的。具体的步骤如下:

用多个刚体segmentBody组成柔体形状的轮廓,并用PivotJoint链接起来。这一步和上一节的第1步是类似的,但是细节上还是有很多不同的,注意集中精力,差异点来啦。

a) 柔体的轮廓完全由segmentBody的形状组成,而非PivotJoint关节。

上一节的圆形轮廓是通过关节勾勒出来的,但是关节并不参与碰撞检测,所以我在开头就说了,这并不能算是一个刚体。

本节的轮廓完全是由刚体组成,就像是一个切成多个片段的圆环,如下所示。

softbody_show1_130509

之前我们学习过,任何非标准圆形的刚体都是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关节链接,而不是一个。

这两个关节分别连接相邻两个刚体的顶点位置。那么为什么要用两个而不是一个关节链接呢?

用多个刚体组合成柔体轮廓非常重要的一点,就是在柔体发生形变时,保证轮廓的平滑。只有一个关节时,相邻刚体之间容易出现重迭或开口。相反如果用两个关节分别连接刚体的内外顶点,同时固定外关节,这样就可以防止形状轮廓发生重迭或开口。如下图所示:

 softbody_show2_130509

如刚才讲的,右图两个关节中,固定外关节,是指设置关节的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;
		}

	}
}

Click to download source code

联系作者

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

12 Replies to “Nape柔体教程(3)”

发表回复

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