用马达关节创建Box2D锁链效果

今天我们来学习Box2D锁链效果。这个效果并不难,记得我们学过的Box2D 关节——”马达关节” b2RevoluteJoint吗?锁链效果其实就是用多个b2RevolueJoint连接多个刚体,很简单吧!

简单在用多个关节连接多个刚体,难也难在这里。我们还是先来看看效果吧。在下面的效果中我创建了两个锁链,一个是小桥,另一个是连接了一个圆形刚体的锁链。鼠标可以点击并拖动下面的刚体。

[swfobject]665[/swfobject]

看过效果之后,我想你肯定也想到了,要用for循环来创建锁链。要用for循环就要有可以重复执行的过程,仔细想想这个过程并不难。

创建当前的刚体body,指定节点的位置anchor,创建节点为anchor的关节,链接当前刚体body和前一个刚体preBody。如下图所示。图中虚线框内是fox循环要重复执行的过程。anchor1用来连接preBody和Body。相信可能有人会跟我一样,犯下面的错误,把关节点设置为body的中心点。这样旋转后,会出现图中下面的效果。因为preBody和body都是围绕anchor1旋转的嘛,想想风扇效果?!

正确的做法是像下图一样,创建当前刚体时body时,让它偏移anchor1一点,偏移多少呢?最好是刚体的一半,这样节点就移到了刚体的边缘处了。旋转之后就会像图中下面的效果一样自然了。

另外设置刚体的宽度为两个节点之间的间距,这样刚体之间会紧密相连,效果就跟上的SWF示例一样了!

完整的代码和注释如下:

package  
{
	import Box2D.Common.Math.b2Vec2;
	import Box2D.Dynamics.b2Body;
	import Box2D.Dynamics.b2World;
	import Box2D.Dynamics.Joints.b2RevoluteJointDef;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;

	/**
	 * ...
	 * @author ladeng6666
	 */
	public class Bridge extends Sprite 
	{
		private var world:b2World;

		public function Bridge() 
		{
			//初始化,创建世界,添加事件侦听等等
			init();
			//创建小桥锁链
			createBridge();
			//创建一个可以拖动的锁链
			creaetLinkage();
			//随机创建几个刚体
			createBodies();
		}

		private function init():void 
		{
			//初始化世界
			world = LDEasyBox2D.createWorld();
			addChild(LDEasyBox2D.createDebug(world));
			LDEasyBox2D.stage = this;
			LDEasyBox2D.createWrapWall(world,this);

			//添加事件侦听
			addEventListener(Event.ENTER_FRAME, loop);
			stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseEventHandler);
			stage.addEventListener(MouseEvent.MOUSE_UP, mouseEventHandler);
		}
		//拖动刚体
		private function mouseEventHandler(e:MouseEvent):void 
		{
			if (e.type == MouseEvent.MOUSE_DOWN) {
				var body:b2Body = LDEasyBox2D.getBodyAtMouse(world);
				if (body != null) {
					LDEasyBox2D.startDragBody(world, body);
				}

			}else if (e.type == MouseEvent.MOUSE_UP) {
				LDEasyBox2D.stopDragBody(world);
			}
		}
		//更新世界
		private function loop(e:Event):void 
		{
			LDEasyBox2D.updateWorld(world);
		}

		private function createBodies():void 
		{
			//随机创建刚体
			for (var i:int = 0; i < 10; i++) {
				if (Math.random() > 0.5) {
					LDEasyBox2D.createBox(world, Math.random() * 300 + 100, 100, Math.random() * 20 + 10, Math.random() * 20 + 10);
				}else {
					LDEasyBox2D.createCircle(world, Math.random() * 300 + 100, 100, Math.random() * 10 + 10);
				}
			}
		}
		private function creaetLinkage():void 
		{
			//设置第一节刚体开始的位置
			var initx:Number = 150;
			var inity:Number = 50;
			//两个节点之间的间距
			var gapBetweenAnchor:Number = 30;
			//定义节点
			var anchor:b2Vec2=new b2Vec2();
			//当前刚体
			var body:b2Body;
			//前一个刚体
			var preBody:b2Body;
			//设置前一个刚体是一个圆形刚体,这个刚体不在for循环范围内,
			//但是要在for循环里引用,所以在这里预先定义好
			preBody = LDEasyBox2D.createCircle(world, initx, inity, 15);
			//定义关节
			var revoluteJoint:b2RevoluteJointDef = new b2RevoluteJointDef();

			for (var i:int = 0; i < 4; i++) {
				//循环创建刚体,它们的x坐标都基于节点的位置向右偏移15个像素(也就是刚体的半宽)
				body = LDEasyBox2D.createBox(world, initx + i * gapBetweenAnchor+15, inity, gapBetweenAnchor, 5);
				//设置节点的坐标
				anchor.Set((initx + i * gapBetweenAnchor) / 30, inity / 30);
				//初始化关节
				revoluteJoint.Initialize( preBody, body, anchor);
				world.CreateJoint(revoluteJoint);
				//设置preBody引用当前的刚体
				preBody = body;
			}
		}		

		private function createBridge():void 
		{
			//设置第一节刚体开始的位置
			var initx:Number = 100;
			var inity:Number = 250;
				//两个节点之间的间距
			var gapBetweenAnchor:Number = 50;
			//定义节点
			var anchor:b2Vec2=new b2Vec2();
			//当前刚体
			var body:b2Body;
			//前一个刚体
			var preBody:b2Body;
			//在for循环之外预先定义第一个刚体,定义成GetGroundBody,固定锁链的起点
			preBody = world.GetGroundBody();
			//定义关节
			var revoluteJoint:b2RevoluteJointDef = new b2RevoluteJointDef();

			for (var i:int = 0; i < 6; i++) {
				//循环创建刚体,它们的x坐标都基于节点的位置向右偏移15个像素(也就是刚体的半宽)
				body = LDEasyBox2D.createBox(world, initx + i * gapBetweenAnchor+25, inity, gapBetweenAnchor, 10);
				//设置节点的坐标
				anchor.Set((initx + i * gapBetweenAnchor) / 30, inity / 30);
				//初始化关节
				revoluteJoint.Initialize( preBody, body, anchor);
				world.CreateJoint(revoluteJoint);
				//设置preBody引用当前的刚体
				preBody = body;
			}
			//设置最后一个节点
			anchor.Set((initx + i * 50) / 30, inity / 30);
			//将最后一个节点链接到GetGroundBody,固定锁链的终点
			revoluteJoint.Initialize( preBody, world.GetGroundBody(), anchor);			
			world.CreateJoint(revoluteJoint).GetAnchorB().x;
		}
	}

}

值得注意的是第121行和139行,将关节的起点和终点设置成了GetGroundBody()返回的刚体,用来固定住这两个位置,模拟小桥的效果。关于GetGroundBody()的用法请参考Box2D如何固定动态刚体

源文件下载地址

联系作者

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

13 Replies to “用马达关节创建Box2D锁链效果”

  1. 拉登兄,你好,想请教一下,如何让一些物体的碰撞给过滤掉。我最近做了个东西就如同你的“桥”的做法,我做了4跟单头锁定的飘带并加了关节,但我不希望2根飘带之间碰撞,但同时又希望飘带自身是联动的。我尝试了简历几个世界但失败了,你能教教我怎么实现吗?谢谢

  2. 请教,我做了一个绳索桥,绑定方法如下,但是当我的主角走在桥上的时候,绳索就会断成一节一节的。。 ,怎么破?

    b2RevoluteJointDef jointDef;
    jointDef.Initialize(body1, body2, body1->GetWorldPoint(ToMeter1(anchor1)));
    m_world->CreateJoint(&jointDef);

回复 ladeng6666

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