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,可以有效的简化代码,具体请参考这里。
点击刚体,还可以进行拖动,如果对鼠标拖动刚体,我建议你看看之前的让刚体听我的——鼠标拖动刚体。
联系作者
如果要将距离关节也变为刚体的话,该怎么做呢?是否有专门的固定关节?
拉登,在你的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出来了四周的墙壁。