用Nape创建绳索效果

DistanceJointLineJoint关节里,我一直都在试图模拟《割绳子》里的绳子效果,如下图所示。

虽然样子是有了,但是小球不想是被系在绳子上,更像是被顶到了一个木板上。今天我们将在Nape中用多个PivotJoint关节模拟真实的绳索效果。

distance_lineJoint

我想象中的绳索模型是这样子的。用一个圆形刚体作为绳子的起首端startBody,从这个首端刚体开始,用PivotJoint连接一个段刚体segmentBody,然后再用一个新的PivotJoint关节将第2个段刚体与第1个段刚体连接起来,以此类推。最后,将最后一个段刚体链接到绳索是末端刚体上endBody。设置一个首和尾端刚体的好处在于,可以通过这个两个刚体,轻松的固定绳索的两端,或者将其他刚体链接到绳索的末端。

与Box2D绳索效果一节的思路不同,这一次我没有从绳索的末端添加刚体,而是从首端开始插入刚体,如下图所示:

 rope_segment1

这样做的话,就不用每次根据绳索的长度重新计算新增段刚体的坐标了。这样不管是新增或删除段刚体就可以很轻松的实现了。

另外一点就是节点的控制。我分为两部份,一部份是首末端与刚体链接的节点,一部份是段刚体之间的连接的节点。如下图所示。

rope_segment2

如左图所示,在添加第1个段刚体时,用两个PivotJoint关节joint1和joint2将其连接到首末刚体上,节点分别是首末刚体的坐标。

在添加第2个段刚体时,首先新建一个PivotJoint将其连接到首刚体上,节点是首刚体的坐标。然后将前一个关节p1的body1由原来的startBody改为新增的段刚体b2,anchor1设置为joint1.anchor的相对于b2的坐标。重复上的步骤可以添加更多的段刚体,让绳索变的更长。

把LineJoint中的示例用上面的绳索实现,就更像《割绳子》里的效果了。同样的,按下空格键可以”剪断”绳索,当小球掉到下面的节点范围内,会自动创建新的绳索。

[swfobject]892[/swfobject]

完整的代码如下:

package {
	import nape.space.Space;
	import ldEasyNape.LDEasyNape;
	import nape.constraint.PivotJoint;
	import nape.constraint.ConstraintList;
	import nape.phys.BodyList;
	import nape.phys.Body;
	import nape.geom.Vec2;
	import nape.phys.Compound;
	import flash.display.Graphics;

	/**
	 * @author yangfei
	 */
	public class RopeJoint {

		private var rope : Compound;
		private var segmentNum : uint;
		private var segmentHeight : Number = 30;
		private var segmentWidth : Number = 2;

		private var startPoint : Vec2;
		private var endPoint : Vec2;
		private var startBody : Body;
		private var endBody : Body;

		private var bodyList : BodyList;
		private var jointList : ConstraintList;

		public function RopeJoint(body1:Body, body2:Body, segmentSize:Number=20, ropeLenth:Number=0) {
			startBody = body1;
			endBody = body2;
			startPoint = startBody.position;
			endPoint = endBody.position;
			if(ropeLenth==0){
				ropeLenth=Vec2.distance(startPoint, endPoint);
			}
			segmentNum = Math.ceil(ropeLenth/ segmentHeight);
			segmentHeight=segmentSize;

			bodyList = new BodyList();
			jointList = new ConstraintList();

			rope = new Compound();
			init();
		}
		private function init():void{
			var joint : PivotJoint = new PivotJoint(startBody, endBody, Vec2.weak(), Vec2.weak());
			joint.compound = rope;
			jointList.unshift(joint);

			for(var i:int = 0; i<segmentNum; i++){
				addSegment();
			}
		}
		// used in addSegment();
		private var px : Number;
		private var py : Number;
		private var joint : PivotJoint;
		private var segment : Body;
		private var firstJoint : PivotJoint;

		public function addSegment():void{
			//定义刚体的初始坐标
			px = startPoint.x;
			py = startPoint.y + segmentHeight/2;
			//创建刚体
			segment = LDEasyNape.createBox(px, py, segmentWidth, segmentHeight);
			segment.shapes.at(0).sensorEnabled = true;
			//如果不是第一节刚体,则设置新刚体为第一节刚体的坐标
			if(bodyList.length>0){
				segment.position.set(bodyList.at(0).position);
				segment.rotation = bodyList.at(0).rotation;
			}
			//调整第一个关节
			firstJoint = jointList.at(0) as PivotJoint;
			firstJoint.body1 = segment;
			firstJoint.anchor1 = Vec2.weak(0, segmentHeight/2);
			//创建新的关节,将新增刚体连接到body1上
			joint  = new PivotJoint(startBody, segment, Vec2.weak(), Vec2.weak(0,-segmentHeight/2));
			joint.stiff=true;
			joint.ignore = true;
			joint.compound = rope;

			bodyList.unshift(segment);
			jointList.unshift(joint);

		}
		public function removeSegment():void{
			if(jointList.length < 5) return;
			bodyList.shift().compound = null;
			jointList.shift().compound = null;

			var joint : PivotJoint = jointList.at(0) as PivotJoint;
			joint.body1= startBody;
			joint.anchor1 = Vec2.weak();
		}
		public function set space(value : Space):void{
			rope.space = value;
		}
		public function get space():Space{
			return rope.space;
		}
		public function set active(value : Boolean):void{
			var firstJoint : PivotJoint = jointList.at(0) as PivotJoint;
			var lastJoint : PivotJoint = jointList.at(jointList.length-1) as PivotJoint;
			firstJoint.active=value;
			lastJoint.active=value;
		}
		public function get active():Boolean{
			var firstJoint : PivotJoint = jointList.at(0) as PivotJoint;
			return firstJoint.active;
		}
		public function set body1(value : Body):void{
			var joint : PivotJoint = jointList.at(0) as PivotJoint;
			joint.body1=value;
			startBody = value;
			startPoint = startBody.position;
		}
		public function set body2(value : Body):void{
			var joint : PivotJoint = jointList.at(jointList.length-1) as PivotJoint;
			joint.body2=value;
			endBody = value;
			endPoint = endBody.position;
		}
		public function drawLine(graphic : Graphics):void{
				graphic.clear();
				if(!active) return;
				graphic.lineStyle(5);
				graphic.moveTo(startPoint.x, startPoint.y);

				var px: Number, py:Number;
				var ax: Number, ay:Number;

				var i:int=1;
				var body: Body;
				var joint: PivotJoint;

				for(;i<jointList.length;i++){
					joint = jointList.at(i) as PivotJoint;
					body = joint.body1;

					px=body.position.x;
					py=body.position.y;
					ax=body.localPointToWorld(joint.anchor2).x;
					ay=body.localPointToWorld(joint.anchor2).y;
					graphic.curveTo( ax, ay, px, py);
				}
				graphic.curveTo(joint.body2.position.x,joint.body2.position.y,endPoint.x, endPoint.y);

		}
	}
}

值得注意的是,因为新增的段刚体都是从首端开始的,所以我下意识的用unshift将刚体添加到了bodyList和jointList数组的第1个位置,这样访问起来,就不用考虑绳索的长度了。

下载源文件

联系作者

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

4 Replies to “用Nape创建绳索效果”

回复 Samuel Batista

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