Box2D关节——”距离关节”b2DistanceJoint

在上一篇文章中,我们初步认识了Box2D中的b2Joint关节类。今天我们开始学习具体的Box2D关节类,即b2Joint的子类。首先我们从b2DistanceJoint类开始。

b2DistanceJoint关节,从它的名字可以猜到距离关节的作用:通过固定长度的关节将两刚体联系到一起,使这两个刚体始终保持一定的距离。可以像想象的出来吗?如果这样解释很难理解,那就想想自行车吧。把自行车的两个轱辘当成刚体,车架就是关节,两个轱辘可以自由的旋转,但是因为有车架的限制,两个轱辘之间始终保持一定的距离。

了解了b2DistanceJoint的定义,下面我们来看看创建过程。在初认识Box2D中的b2Joint关节类中,我们知道所有的关节都可以用initialize方法来创建,b2DistanceJoint也不例外,它的initialize方法结构如下:

	public function Initialize(
		bA:b2Body,
		bB:b2Body,
		anchorA:b2Vec2,
		anchorB:b2Vec2) : void

方法的每个参数功能如下:

  • bA:即bodyA,关节连接的一个刚体。
  • bB:即bodyB,关节连接的另一个刚体。
  • anchorA:关节的端点A,通常为bodyA的中心位置。
  • anchorB:关节的端点B,通常为bodyB的中心位置。

结合上图来看,两个车轮分别表示b2Distance的连接的两个刚体bA和bB,车轮中的两个红点分别表示关节的两个控制点,即车轮的中心点。

另外还可以用关节的collideConnected属性,设置关节连接的两个刚体之间是否进行碰撞检测。

在下面的示例中,我创建了两个大小不一的圆形刚体,大的为bodyA,小的为bodyB,选择右上角的radio选项,设置A或B为静态刚体,或者两个刚体都是动态刚体,同时给bodyA添加一个角速度,就成了一个简单的”自行车”。

完整的代码和注释如下:

package
{
	import Box2D.Common.Math.b2Vec2;
	import Box2D.Dynamics.b2Body;
	import Box2D.Dynamics.b2World;
	import Box2D.Dynamics.Joints.b2DistanceJoint;
	import Box2D.Dynamics.Joints.b2DistanceJointDef;
	import com.bit101.components.Panel;
	import com.bit101.components.RadioButton;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.geom.Point;

	/**
	 * http://www.ladeng6666.com
	 * @author ladeng6666
	 */
	public class DistanceJointTest extends Sprite
	{
		private var world:b2World;
		private var debugSprite:Sprite;
		private var bodyA:b2Body, bodyB:b2Body;
		private var posA:Point, posB:Point;

		private var joint:b2DistanceJoint;

		public function DistanceJointTest()
		{
			//设置Box2D舞台
			LDEasyBox2D.stage = stage;
			//用LDEasyBox2D快速创建世界
			world=LDEasyBox2D.createWorld();
			debugSprite=LDEasyBox2D.createDebug(world);
			addChild(debugSprite);

			//创建地面,
			LDEasyBox2D.createWrapWall(world, stage);
			//添加事件侦听器
			addEventListener(Event.ENTER_FRAME, loop);
			stage.addEventListener(MouseEvent.MOUSE_DOWN, onStageMouseDown);
			stage.addEventListener(MouseEvent.MOUSE_UP, onStageMouseUp);

			//创建关节连接的两个刚体
			createBodies();
			//创建关节
			createDistanceJoint();
			//设置右上角的UI
			setUI();
		}

		private function createDistanceJoint():void
		{
			//声明关节需求
			var distanceJoint:b2DistanceJointDef = new b2DistanceJointDef();
			//实例化关节需求
			distanceJoint.Initialize(bodyA, bodyB, new b2Vec2(posA.x/30,posA.y/30), new b2Vec2(posB.x/30, posB.y/30));
			distanceJoint.collideConnected = false;
			//创建关节
			joint=world.CreateJoint(distanceJoint) as b2DistanceJoint;
		}

		private function createBodies(staticA:Boolean=false,staticB:Boolean=false):void
		{
			//设置连接刚体的坐标
			posA = new Point(100, 100);
			posB = new Point(200, 100);
			//用LDEasyBox2D静态类创建两个刚体
			bodyA = LDEasyBox2D.createCircle(world, posA.x, posA.y, 30, staticA);
			//给刚体A添加一个角速度,这样可以做一个简单的自行车
			bodyA.SetAngularVelocity(30/ 180 * Math.PI*30);
			bodyB = LDEasyBox2D.createCircle(world, posB.x, posB.y, 10, staticB);

		}

		private function onStageMouseDown(e:MouseEvent):void
		{
			//鼠标点击后,首先获取鼠标位置的刚体
			var dragBody:b2Body = LDEasyBox2D.getBodyAtMouse(world);
			//如果鼠标点击到了刚体
			if (dragBody != null) {
				//则开始拖动刚体
				LDEasyBox2D.startDragBody(world,dragBody,10000*dragBody.GetMass());
			}

		}

		private function onStageMouseUp(e:MouseEvent):void
		{
			//鼠标弹起后,停止拖动刚体
			LDEasyBox2D.stopDragBody(world);
		}

		private function loop(e:Event):void
		{
			LDEasyBox2D.updateWorld(world);
			//把下面的注释去点,看看更有趣的效果
			//bodyA.ApplyTorque(5*bodyA.GetMass());
		}

		private function setUI():void {
			var panel:Panel = new Panel(stage, 400, 30);
			panel.width = 100;
			panel.height = 60;
			var radio1:RadioButton = new RadioButton(panel, 5, 5, "no static", true,onRadioChange);
			var radio2:RadioButton = new RadioButton(panel, 5, 25, "bodyA is static", false,onRadioChange);
			var radio3:RadioButton = new RadioButton(panel, 5, 45, "bodyB is static", false,onRadioChange);
		}

		private function onRadioChange(e:Event):void
		{
			world.DestroyBody(bodyA);
			world.DestroyBody(bodyB);
			world.DestroyJoint(joint);

			switch(e.target.label) {
				case "no static":
					createBodies();
					break;
				case "bodyA is static":
					createBodies(true);
					break;
				case "bodyB is static":
					createBodies(false, true);
					break;
			}
			createDistanceJoint();
		}
	}

}

代码中用到了我写的静态类LDEasyBox2D,可以有效的简化代码,具体请参考这里

点击刚体,还可以进行拖动,如果对鼠标拖动刚体,我建议你看看之前的让刚体听我的——鼠标拖动刚体

 源代码下载

联系作者

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

2 Replies to “Box2D关节——”距离关节”b2DistanceJoint”

  1. 拉登,在你的LDEasyBox2D中,
    public static function createWrapWall(world:b2World,canvas:DisplayObject):void {
    var w:Number = canvas.width;
    var h:Number = canvas.height;
    var wallThick:Number = 20;//in pixels

    createBox(world, w / 2, 0, w , wallThick, true);
    createBox(world, w / 2, h, w , wallThick, true);
    createBox(world, 0, h / 2, wallThick, h , true);
    createBox(world, w, h / 2, wallThick, h , true);
    }
    其中的形参2:canvas:DisplayObject, 在主文件中,形参2被实参stage传递,
    有个问题,
    w = canvas.width;
    h = canvas.height;
    实参stage被传递过来后,往往会出现问题。
    w = stage.width;
    h = stage.height;
    我们知道stage.width/stage.height和stage.stageWidth/stage.stageHeight是有所不同的。
    stage.width指的是舞台上所有物体形成的矩形包裹框的长度,stage.height指的是矩形包裹框的高度。
    当舞台上没有物体的时候,stage.width和stage.height为0。
    这样会导致一个bug的出现,就是debugDraw不出来四周的墙壁。
    我很纳闷,你的演示文件中却正常的dubugDraw出来了四周的墙壁。

发表回复

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