【Box2D】物理引擎入門003

一、物理編輯器的使用

在本系列博客的第一篇就介紹了如何使用Box2D內(nèi)置的幾種方式創(chuàng)建剛體的材質(zhì),然而我們在開發(fā)游戲的時候需要根據(jù)很多非常復雜的物體創(chuàng)建對應的形狀,使用內(nèi)置的多邊形很難去描出合適的圖形,所以Box2D物理游戲引擎支持外部導入文件進行物理剛體的創(chuàng)建。

1.1、根據(jù)物理編輯器文件創(chuàng)建物理剛體

首先使用PhysicsEditor物理編輯器將需要創(chuàng)建的材質(zhì)信息編輯好,然后導出cocos2d支持的plist文件,假設我們將文件保存為“boxEdit.plist”并放在資源目錄下的physics文件夾里面。

創(chuàng)建精靈

-- 創(chuàng)建車身精靈
self.car = D.img("common/cars/car_1/carBody_1.png"):p(480, 270):to(self)
-- 創(chuàng)建車輪精靈
self.w1 = D.img("common/cars/car_1/wheel_1.png"):p(430, 210):to(self)
self.w2 = D.img("common/cars/car_1/wheel_1.png"):p(530, 210):to(self)

接下來就要使用物理編輯器導出的文件了:

1、創(chuàng)建剛體

local bodyDef           = b2BodyDef()
-- 類型:靜態(tài)(b2_staticBody),平臺(b2_kinematicBody),動態(tài)(b2_dynamicBody)
bodyDef.type            = b2_dynamicBody
bodyDef.position        = b2Vec2(self.car.px() / 32, self.car:py() / 32)
bodyDef.angle           = math.rad(0)
-- 用戶數(shù)據(jù):存儲用戶的數(shù)據(jù),可以是任何類型的數(shù)據(jù)。一般要求存儲的數(shù)據(jù)的類型是一致的
bodyDef.userData        = self.car 

-- 創(chuàng)建一個剛體對象,根據(jù)剛體定義創(chuàng)建
local body          = world:CreateBody(bodyDef)

2、有了剛體,接下來就要創(chuàng)建材質(zhì),代碼如下:

-- 加載physicsDesigner的.plist格式文件
GB2ShapeCache:sharedGB2ShapeCache():addShapesWithFile("physics/boxEdit.plist")
-- 把生成的剛體和形狀綁在一起,key即圖片名
GB2ShapeCache:sharedGB2ShapeCache():addFixturesToBody(body, "carBody_1")

可以使用GB2ShapeCache:sharedGB2ShapeCache():addShapesWithFile("plist文件名路徑")來加載物理編輯器導出的數(shù)據(jù),然后通過GB2ShapeCache:sharedGB2ShapeCache():addFixturesToBody(剛體對象, "精靈列表上的形狀名稱")來給剛體綁定材質(zhì)信息。

根據(jù)PhysicsEditor編輯的文件創(chuàng)建剛體材質(zhì)

刷新模擬器就可以看到編輯的車身顯示在場景中(要開啟物理調(diào)試模式)。

3、使用同樣的方式創(chuàng)建汽車的兩個車輪

bodyDef.position        = b2Vec2(13.5, 8.5)
bodyDef.userData        = self.w1 
local bodyWheel1 = world:CreateBody(bodyDef)
GB2ShapeCache:sharedGB2ShapeCache():addFixturesToBody(bodyWheel1, "wheel_1")

bodyDef.position        = b2Vec2(16.5, 8.5)
bodyDef.userData        = self.w2
local bodyWheel2 = world:CreateBody(bodyDef)
GB2ShapeCache:sharedGB2ShapeCache():addFixturesToBody(bodyWheel2, "wheel_1")

刷新模擬器,可以看到如下畫面:

根據(jù)PhysicsEditor編輯的文件創(chuàng)建剛體材質(zhì)

二、物理更新

2.1、物理更新

在這之前,其實物理世界并沒有開始進行物理模擬,那么我們可以使用下面這個代碼來進行一次物理更新:

-- 更新[物理世界]
function M.tick()
    if not world then return end 

    -- 時間步、速度迭代device、位置迭代
    local velocityIterations, positionIterations = 32, 32
    -- 物理引擎進行物理模擬,生成模擬后的數(shù)據(jù)
    world:Step(60, velocityIterations, positionIterations)
end 

注意:這個函數(shù)里面的“world”就是之前創(chuàng)建的物理世界,所以我們可以將world定義成全局的,以保證可以在任何地方訪問。

調(diào)用物理世界的Step()方法可以進行一次物理模擬,方法的第一個參數(shù)是時間步,第二個參數(shù)是速度迭代次數(shù),第三個參數(shù)是位置迭代參數(shù)。

  • 時間步用于控制物理模擬的時間間隔,我們一般讓其與渲染世界同步,所以設置為60,設備性能較低時可以設置為40。
  • 速度迭代:迭代次數(shù)越高,模擬越逼真,但是越耗性能。
  • 位置迭代:迭代次數(shù)越高,模擬越逼真,但是越耗性能。

2.2、開啟物理模擬

開啟一個調(diào)度器來調(diào)用上面的這個函數(shù),實現(xiàn)實時模擬:

local timer             = SC.open(self.tick, 1.0 / 60)

開啟調(diào)度進行物理模擬的時候發(fā)現(xiàn)車身和車輪開始做只有落地運動,并且它們散架了,這是因為車身和車輪之間沒有連接到一起,為了讓車身和車輪組成一個整體,可以使用關節(jié)(約束)將他們連接到一起。

三、關節(jié)(b2Joint)

關節(jié)又稱之為“約束”,是用于限制剛體運動的一些條件,在box2D中,常用的關節(jié)有:鼠標關節(jié)、距離關節(jié)、移動關節(jié)、旋轉(zhuǎn)關節(jié)等,其實這些關節(jié)的中文名稱并非翻譯的很準確,很多諸如《Cocos2d-JS 游戲開發(fā)》的書籍里面都不進行翻譯,而是直接使用關節(jié)的類名來進行講解。

1、關節(jié)描述(b2JointDef)

與創(chuàng)建剛體一樣,創(chuàng)建關節(jié)也需要先創(chuàng)建關節(jié)的描述,在box2D中,關節(jié)描述b2JointDef的定義如下:

struct b2JointDef
{
    b2JointDef()
    {
        type = e_unknownJoint;
        userData = NULL;
        bodyA = NULL;
        bodyB = NULL;
        collideConnected = false;
    }

    /// The joint type is set automatically for concrete joint types.
    b2JointType type;

    /// Use this to attach application specific data to your joints.
    void* userData;

    /// The first attached body.
    b2Body* bodyA;

    /// The second attached body.
    b2Body* bodyB;

    /// Set this flag to true if the attached bodies should collide.
    bool collideConnected;
};

b2JointDef是一個結(jié)構(gòu)體(struct)類型,包含如下五個屬性:

  • type:關節(jié)類型。
  • userData:用戶數(shù)據(jù)(可以存放任意數(shù)據(jù))。
  • bodyA:被關節(jié)約束的第一個剛體對象。
  • bodyB:被關節(jié)約束的第二個剛體對象。
  • collideConnected:被約束的兩個剛體是否可以發(fā)生碰撞,true表示可以發(fā)生碰撞。

1、鼠標關節(jié)(b2MouseJoint)

1.1、鼠標關節(jié)描述(b2MouseJointDef)

在創(chuàng)建鼠標關節(jié)之前,需要先創(chuàng)建對應的關節(jié)描述,鼠標關節(jié)描述b2MouseJointDef的定義如下:

struct b2MouseJointDef : public b2JointDef
{
    b2MouseJointDef()
    {
        type = e_mouseJoint;
        target.Set(0.0f, 0.0f);
        maxForce = 0.0f;
        frequencyHz = 5.0f;
        dampingRatio = 0.7f;
    }

    /// The initial world target point. This is assumed
    /// to coincide with the body anchor initially.
    b2Vec2 target;

    /// The maximum constraint force that can be exerted
    /// to move the candidate body. Usually you will express
    /// as some multiple of the weight (multiplier * mass * gravity).
    float32 maxForce;

    /// The response speed.
    float32 frequencyHz;

    /// The damping ratio. 0 = no damping, 1 = critical damping.
    float32 dampingRatio;
};

b2MouseJointDef繼承自b2JointDef,它的type屬性值為“e_mouseJoint”,指明使用該關節(jié)定義的關節(jié)是一個鼠標關節(jié)。

b2MouseJointDef相比于b2JointDef多出了如下幾個屬性:

  • maxForce:作用在剛體上的最大的力,力越大拖動剛體越輕松。
  • frequencyHz:響應速度,數(shù)值越高,關節(jié)響應的速度越快,關節(jié)看上去越堅固。
  • dampingRatio:阻尼率:值越大,關節(jié)運動阻尼越大。

典型情況下,關節(jié)頻率(響應速度)要小于一半的時間步(time step)頻率。比如每秒執(zhí)行60次時間步, 關節(jié)的頻率就要小于30赫茲。這樣做的理由可以參考Nyquist頻率理論。

影響關節(jié)的彈性(阻尼率無單位,典型是在0到1之間, 也可以更大。1是阻尼率的臨界值, 當阻尼率為1時,沒有振動。)

1.2、創(chuàng)建鼠標關節(jié)描述

-- 創(chuàng)建一個鼠標關節(jié)定義
local mouseJointDef             = b2MouseJointDef()
-- 要被鼠標關節(jié)約束的剛體
mouseJointDef.bodyA             = body
-- bodyB設置為世界的邊緣剛體
mouseJointDef.bodyB             = edgeBody
mouseJointDef.frequencyHz       = 30
mouseJointDef.collideConnected  = true
mouseJointDef.maxForce          = 100
-- 關節(jié)的位置
mouseJointDef.target            = b2Vec2(11, 10)

可以使用b2MouseJointDef()函數(shù)創(chuàng)建一個鼠標關節(jié)定義,然后直接設置其屬性值。值得注意的是,關節(jié)是用于連接兩個剛體的,而鼠標關節(jié)的主要作用是可以使用鼠標來對剛體進行拖拽操作,作用的是一個剛體,所以我們可以將一個剛體設置為要拖拽的剛體對象,另一個一般設置為世界邊緣剛體。

1.3、創(chuàng)建鼠標關節(jié)

-- 創(chuàng)建關節(jié),并轉(zhuǎn)換為b2MouseJoint類型
local mouseJoint = tolua.cast(world:CreateJoint(mouseJointDef), "b2MouseJoint")

物理世界對象的CreateJoint(jointDef)方法可以用于創(chuàng)建一個關節(jié),其參數(shù)jointDef為要創(chuàng)建的關節(jié)對應的關節(jié)描述對象,該方法的返回值類型是b2Joint類型,可以通過tolua.cast(obj, typeName)方法來進行類型轉(zhuǎn)換。

1.4、移動剛體

鼠標關節(jié)移動剛體的現(xiàn)方式是改變鼠標關節(jié)的位置,代碼如下:

-- 設置關節(jié)的位置
mouseJoint:SetTarget(b2Vec2(10, 10))

只需要在觸摸事件的移動回調(diào)中改變鼠標關節(jié)的位置即可實現(xiàn)剛體的拖拽。

2、焊接關節(jié)(b2WeldJoint)

2.1、焊接關節(jié)描述(b2WeldJointDef)

struct b2WeldJointDef : public b2JointDef
{
    b2WeldJointDef()
    {
        type = e_weldJoint;
        localAnchorA.Set(0.0f, 0.0f);
        localAnchorB.Set(0.0f, 0.0f);
        referenceAngle = 0.0f;
        frequencyHz = 0.0f;
        dampingRatio = 0.0f;
    }

    /// Initialize the bodies, anchors, and reference angle using a world
    /// anchor point.
    void Initialize(b2Body* bodyA, b2Body* bodyB, const b2Vec2& anchor);

    /// The local anchor point relative to bodyA's origin.
    b2Vec2 localAnchorA;

    /// The local anchor point relative to bodyB's origin.
    b2Vec2 localAnchorB;

    /// The bodyB angle minus bodyA angle in the reference state (radians).
    float32 referenceAngle;
    
    /// The mass-spring-damper frequency in Hertz. Rotation only.
    /// Disable softness with a value of 0.
    float32 frequencyHz;

    /// The damping ratio. 0 = no damping, 1 = critical damping.
    float32 dampingRatio;
};

焊接關節(jié)的定義如上,其相比與b2JointDef增加的屬性有:

  • localAnchorA:關節(jié)連接在剛體A上的局部錨點,或者就理解成是關節(jié)在剛體A上的連接點。
  • localAnchorA:關節(jié)連接在剛體B上的局部錨點,或者就理解成是關節(jié)在剛體B上的連接點。
  • referenceAngle:剛體B相對于剛體A的角度,使用弧度制表示。
  • frequencyHz:響應速度,數(shù)值越高,關節(jié)響應的速度越快,關節(jié)看上去越堅固。
  • dampingRatio:阻尼率:值越大,關節(jié)運動阻尼越大。

2.2、創(chuàng)建焊接關節(jié)描述

-- 創(chuàng)建一個焊接關節(jié)定義
local weldJointDef              = b2WeldJointDef()
-- 初始化
weldJointDef:Initialize(bodyA, bodyB, bodyB:GetWorldCenter())
weldJointDef.frequencyHz        = 30
weldJointDef.collideConnected   = false 

創(chuàng)建焊接關節(jié)的時候,設置關節(jié)約束的兩個剛體需要通過調(diào)用焊接關節(jié)定義的Initialize()方法,該方法有三個參數(shù),分別是:剛體A、剛體B和關節(jié)的錨點位置。

2.3、創(chuàng)建焊接關節(jié)

local weldJoint = tolua.cast(world:CreateJoint(weldJointDef), "b2WeldJoint")

3、距離關節(jié)(b2DistanceJoint)

3.1、距離關節(jié)定義(b2DistanceJointDef)

struct b2DistanceJointDef : public b2JointDef
{
    b2DistanceJointDef()
    {
        type = e_distanceJoint;
        localAnchorA.Set(0.0f, 0.0f);
        localAnchorB.Set(0.0f, 0.0f);
        length = 1.0f;
        frequencyHz = 0.0f;
        dampingRatio = 0.0f;
    }

    /// Initialize the bodies, anchors, and length using the world
    /// anchors.
    void Initialize(b2Body* bodyA, b2Body* bodyB,
                    const b2Vec2& anchorA, const b2Vec2& anchorB);

    /// The local anchor point relative to bodyA's origin.
    b2Vec2 localAnchorA;

    /// The local anchor point relative to bodyB's origin.
    b2Vec2 localAnchorB;

    /// The natural length between the anchor points.
    float32 length;

    /// The mass-spring-damper frequency in Hertz. A value of 0
    /// disables softness.
    float32 frequencyHz;

    /// The damping ratio. 0 = no damping, 1 = critical damping.
    float32 dampingRatio;
};

距離關節(jié)的定義如上,其相比與b2JointDef增加的屬性有:

  • localAnchorA:關節(jié)連接在剛體A上的局部錨點,或者就理解成是關節(jié)在剛體A上的連接點。
  • localAnchorA:關節(jié)連接在剛體B上的局部錨點,或者就理解成是關節(jié)在剛體B上的連接點。
  • length:錨點之間的自然長度。

3.2、創(chuàng)建距離關節(jié)

-- 創(chuàng)建一個距離關節(jié)定義
local distanceJointDef              = b2DistanceJointDef()
distanceJointDef:Initialize(bodyA, bodyB, bodyA:GetWorldCenter(), bodyB:GetWorldCenter())
distanceJointDef.bodyA              = bodyA
distanceJointDef.bodyB              = bodyB
-- 距離關節(jié)的長度
distanceJointDef.length             = 2
distanceJointDef.frequencyHz        = 30
-- 是否連續(xù)碰撞
distanceJointDef.collideConnected   = false 

-- 創(chuàng)建關節(jié)
lcoal distanceJoint = tolua.cast(world:CreateJoint(distanceJointDef), "b2DistanceJoint")

創(chuàng)建距離關節(jié)同樣需要調(diào)用Initialize()方法,具體參照焊接關節(jié)。

4、移動關節(jié)(b2PrismaticJoint)

4.1、移動關節(jié)定義(b2PrismaticJointDef)

struct b2PrismaticJointDef : public b2JointDef
{
    b2PrismaticJointDef()
    {
        type = e_prismaticJoint;
        localAnchorA.SetZero();
        localAnchorB.SetZero();
        localAxisA.Set(1.0f, 0.0f);
        referenceAngle = 0.0f;
        enableLimit = false;
        lowerTranslation = 0.0f;
        upperTranslation = 0.0f;
        enableMotor = false;
        maxMotorForce = 0.0f;
        motorSpeed = 0.0f;
    }

    /// Initialize the bodies, anchors, axis, and reference angle using the world
    /// anchor and unit world axis.
    void Initialize(b2Body* bodyA, b2Body* bodyB, const b2Vec2& anchor, const b2Vec2& axis);

    /// The local anchor point relative to bodyA's origin.
    b2Vec2 localAnchorA;

    /// The local anchor point relative to bodyB's origin.
    b2Vec2 localAnchorB;

    /// The local translation unit axis in bodyA.
    b2Vec2 localAxisA;

    /// The constrained angle between the bodies: bodyB_angle - bodyA_angle.
    float32 referenceAngle;

    /// Enable/disable the joint limit.
    bool enableLimit;

    /// The lower translation limit, usually in meters.
    float32 lowerTranslation;

    /// The upper translation limit, usually in meters.
    float32 upperTranslation;

    /// Enable/disable the joint motor.
    bool enableMotor;

    /// The maximum motor torque, usually in N-m.
    float32 maxMotorForce;

    /// The desired motor speed in radians per second.
    float32 motorSpeed;
};

距離關節(jié)的屬性會相對多一點,其相比與b2JointDef增加的屬性有:

  • localAnchorA:關節(jié)連接在剛體A上的局部錨點,或者就理解成是關節(jié)在剛體A上的連接點。
  • localAnchorA:關節(jié)連接在剛體B上的局部錨點,或者就理解成是關節(jié)在剛體B上的連接點。
  • localAxisA:在剛體A中的局部移動單元軸,就是剛體A滑動的方向。
  • referenceAngle:參考角度,剛體B減去剛體A的角度值。
  • enableLimit:是否啟用限制。
  • lowerTranslation:移動的最小限制,與方向同向為正,反向為負。啟用限制后才有效果。
  • upperTranslation:移動的最大限制,與方向同向為正,反向為負。啟用限制后才有效果。
  • enableMotor:是否啟動馬達。
  • maxMotorForce:馬達的最大扭力。
  • motorSpeed:馬達速度。

4.2、創(chuàng)建移動關節(jié)(平移關節(jié))

-- 創(chuàng)建一個移動關節(jié)的定義
local prismaticJointDef             = b2PrismaticJointDef()

if true then 
    -- 移動的方向,用矢量來表示可以移動的方向,零向量(0, 0)為任意方向
    local directVec     = b2Vec2(10, 0)
    -- 初始化關節(jié)
    prismaticJointDef:Initialize(bodyA, bodyB, bodyB:GetWorldCenter(), directVec)
end

prismaticJointDef.lowerTranslation  = -1.0
prismaticJointDef.upperTranslation  = 1.0
prismaticJointDef.enableLimit       = true
prismaticJointDef.collideConnected  = false 

-- 創(chuàng)建關節(jié)
local prismaticJoint = tolua.cast(world:CreateJoint(prismaticJointDef), "b2PrismaticJoint")

5、繩索關節(jié)(b2RopeJoint)

5.1、繩索關節(jié)定義(b2RopeJointDef)

struct b2RopeJointDef : public b2JointDef
{
    b2RopeJointDef()
    {
        type = e_ropeJoint;
        localAnchorA.Set(-1.0f, 0.0f);
        localAnchorB.Set(1.0f, 0.0f);
        maxLength = 0.0f;
    }

    /// The local anchor point relative to bodyA's origin.
    b2Vec2 localAnchorA;

    /// The local anchor point relative to bodyB's origin.
    b2Vec2 localAnchorB;

    /// The maximum length of the rope.
    /// Warning: this must be larger than b2_linearSlop or
    /// the joint will have no effect.
    float32 maxLength;
};

繩索關節(jié)其相比與b2JointDef增加的屬性有:

  • localAnchorA:關節(jié)連接在剛體A上的局部錨點,或者就理解成是關節(jié)在剛體A上的連接點。
  • localAnchorA:關節(jié)連接在剛體B上的局部錨點,或者就理解成是關節(jié)在剛體B上的連接點。
  • maxLength:繩索的最大長度。

5.2、創(chuàng)建繩索關節(jié)

-- 創(chuàng)建一個繩索關節(jié)的定義
local ropeJointDef          = b2RopeJointDef()
-- 繩索的最大長度
ropeJointDef.maxLength      = 4
-- 關節(jié)連接的剛體A
ropeJointDef.bodyA          = bodyA
-- 關節(jié)連接的剛體B
ropeJointDef.bodyB          = bodyB
-- 關節(jié)連接在剛體A上的錨點,其他關節(jié)應該也有,默認在中央
ropeJointDef.localAnchorA   = b2Vec2(0.5, 0.5)
-- 關節(jié)連接在剛體B上的錨點,其他關節(jié)應該也有,默認在中央
ropeJointDef.localAnchorB   = b2Vec2(0.5, 0.5)
-- 是否連續(xù)碰撞 
ropeJointDef.collideConnected = true

-- 創(chuàng)建關節(jié)
local ropeJoint = tolua.cast(world:CreateJoint(ropeJointDef), "b2RopeJoint")

6、旋轉(zhuǎn)關節(jié)(b2RevoluteJoint)

6.1、旋轉(zhuǎn)關節(jié)定義(b2RevoluteJointDef)

struct b2RevoluteJointDef : public b2JointDef
{
    b2RevoluteJointDef()
    {
        type = e_revoluteJoint;
        localAnchorA.Set(0.0f, 0.0f);
        localAnchorB.Set(0.0f, 0.0f);
        referenceAngle = 0.0f;
        lowerAngle = 0.0f;
        upperAngle = 0.0f;
        maxMotorTorque = 0.0f;
        motorSpeed = 0.0f;
        enableLimit = false;
        enableMotor = false;
    }

    /// Initialize the bodies, anchors, and reference angle using a world
    /// anchor point.
    void Initialize(b2Body* bodyA, b2Body* bodyB, const b2Vec2& anchor);

    /// The local anchor point relative to bodyA's origin.
    b2Vec2 localAnchorA;

    /// The local anchor point relative to bodyB's origin.
    b2Vec2 localAnchorB;

    /// The bodyB angle minus bodyA angle in the reference state (radians).
    float32 referenceAngle;

    /// A flag to enable joint limits.
    bool enableLimit;

    /// The lower angle for the joint limit (radians).
    float32 lowerAngle;

    /// The upper angle for the joint limit (radians).
    float32 upperAngle;

    /// A flag to enable the joint motor.
    bool enableMotor;

    /// The desired motor speed. Usually in radians per second.
    float32 motorSpeed;

    /// The maximum motor torque used to achieve the desired motor speed.
    /// Usually in N-m.
    float32 maxMotorTorque;
};

旋轉(zhuǎn)關節(jié)的定義和移動關節(jié)差不多,只是沒有l(wèi)ocalAxisA屬性,所以被旋轉(zhuǎn)關節(jié)約束的兩個剛體之間是可以隨意方向移動的,將平移關節(jié)的移動方向設置為另向量b2Vec2(0, 0),那么平移關節(jié)的效果就和旋轉(zhuǎn)關節(jié)一致了。

6.2、創(chuàng)建旋轉(zhuǎn)關節(jié)

-- 創(chuàng)建一個旋轉(zhuǎn)關節(jié)定義
local revoluteJointDef              = b2RevoluteJointDef()
revoluteJointDef:Initialize(bodyA, bodyB, bodyB:GetWorldCenter())
revoluteJointDef.bodyA              = bodyA
revoluteJointDef.bodyB              = bodyB
revoluteJointDef.lowerAngle         = 0
revoluteJointDef.upperAngle         = 0
-- 是否啟用角度限制,類似手臂只能在一定角度內(nèi)旋轉(zhuǎn)一樣。
revoluteJointDef.enableLimit        = false   
-- 馬達的速度 
revoluteJointDef.motorSpeed         = 0.0       
-- 馬達的最大扭矩
revoluteJointDef.maxMotorTorque     = 11110.0
-- 是否啟用旋轉(zhuǎn)馬達,啟用后關節(jié)會自動轉(zhuǎn)動
revoluteJointDef.enableMotor        = true     
revoluteJointDef.frequencyHz        = 32
revoluteJointDef.collideConnected   = false 
-- 關節(jié)長度 
revoluteJointDef.length             = 0.1

-- 創(chuàng)建關節(jié),并轉(zhuǎn)換為b2RevoluteJoint類型
local revoluteJoint = tolua.cast(world:CreateJoint(revoluteJointDef), "b2RevoluteJoint")

7、摩擦關節(jié)

7.1、摩擦關節(jié)定義

struct b2FrictionJointDef : public b2JointDef
{
    b2FrictionJointDef()
    {
        type = e_frictionJoint;
        localAnchorA.SetZero();
        localAnchorB.SetZero();
        maxForce = 0.0f;
        maxTorque = 0.0f;
    }

    /// Initialize the bodies, anchors, axis, and reference angle using the world
    /// anchor and world axis.
    void Initialize(b2Body* bodyA, b2Body* bodyB, const b2Vec2& anchor);

    /// The local anchor point relative to bodyA's origin.
    b2Vec2 localAnchorA;

    /// The local anchor point relative to bodyB's origin.
    b2Vec2 localAnchorB;

    /// The maximum friction force in N.
    float32 maxForce;

    /// The maximum friction torque in N-m.
    float32 maxTorque;
};

摩擦關節(jié)相對于b2JointDef增加的屬性是:

  • localAnchorA:關節(jié)在剛體A上面的作用點。
  • localAnchorB:關節(jié)在剛體B上面的作用點。
  • maxForce:關節(jié)的最大摩擦力。
  • maxTorque:摩擦關節(jié)最大扭力。

7.2、創(chuàng)建摩擦關節(jié)

-- 創(chuàng)建一個摩擦關節(jié)定義
local frictionJointDef = b2FrictionJointDef()
frictionJointDef:Initialize(bodyA, bodyB, bodyB:GetWorldCenter())
frictionJointDef.frequencyHz = 0.0
frictionJointDef.dampingRatio = 0.0
frictionJointDef.maxForce = 0.0

local frictionJoint = tolua.cast(world:CreateJoint(frictionJointDef), "b2FrictionJoint")

8、齒輪關節(jié)(b2GearJoint)

8.1、齒輪關節(jié)定義(b2GearJointDef)

struct b2GearJointDef : public b2JointDef
{
    b2GearJointDef()
    {
        type = e_gearJoint;
        joint1 = NULL;
        joint2 = NULL;
        ratio = 1.0f;
    }

    /// The first revolute/prismatic joint attached to the gear joint.
    b2Joint* joint1;

    /// The second revolute/prismatic joint attached to the gear joint.
    b2Joint* joint2;

    /// The gear ratio.
    /// @see b2GearJoint for explanation.
    float32 ratio;
};

齒輪關節(jié)的定義如上,其相比與b2JointDef增加的屬性有:

  • joint1:齒輪關節(jié)上關連的第一個關節(jié)(旋轉(zhuǎn)關節(jié)/平移關節(jié))。
  • joint2:齒輪關節(jié)上關連的第二個關節(jié)(旋轉(zhuǎn)關節(jié)/平移關節(jié))。
  • ratio:關節(jié)比例(齒輪系數(shù))。

8.2、創(chuàng)建齒輪關節(jié)

-- 創(chuàng)建一個齒輪關節(jié)
local gearJointDef  = b2GearJointDef() 
-- 齒輪關節(jié)關聯(lián)的關節(jié)1
gearJointDef.joint1 = jointA
-- 齒輪關節(jié)關聯(lián)的關節(jié)2
gearJointDef.joint2 = jointB
gearJointDef.bodyA  = bodyA
gearJointDef.bodyB  = bodyB
-- 關節(jié)比例(齒輪系數(shù))
gearJointDef.ratio  = 1 

local gearJointDef = tolua.cast(world:CreateJoint(gearJointDef), "b2GearJoint") 

上面jointA和jointB是預先創(chuàng)建好的旋轉(zhuǎn)關節(jié)或者平移關節(jié),**釋放這兩個關節(jié)時需要先釋放齒輪關節(jié),否則會引起空指針錯誤。 **

9、滑輪關節(jié)(b2PulleyJoint)

9.1、滑輪關節(jié)定義(b2PulleyJointDef)

struct b2PulleyJointDef : public b2JointDef
{
    b2PulleyJointDef()
    {
        type = e_pulleyJoint;
        groundAnchorA.Set(-1.0f, 1.0f);
        groundAnchorB.Set(1.0f, 1.0f);
        localAnchorA.Set(-1.0f, 0.0f);
        localAnchorB.Set(1.0f, 0.0f);
        lengthA = 0.0f;
        lengthB = 0.0f;
        ratio = 1.0f;
        collideConnected = true;
    }

    /// Initialize the bodies, anchors, lengths, max lengths, and ratio using the world anchors.
    void Initialize(b2Body* bodyA, b2Body* bodyB,
                    const b2Vec2& groundAnchorA, const b2Vec2& groundAnchorB,
                    const b2Vec2& anchorA, const b2Vec2& anchorB,
                    float32 ratio);

    /// The first ground anchor in world coordinates. This point never moves.
    b2Vec2 groundAnchorA;

    /// The second ground anchor in world coordinates. This point never moves.
    b2Vec2 groundAnchorB;

    /// The local anchor point relative to bodyA's origin.
    b2Vec2 localAnchorA;

    /// The local anchor point relative to bodyB's origin.
    b2Vec2 localAnchorB;

    /// The a reference length for the segment attached to bodyA.
    float32 lengthA;

    /// The a reference length for the segment attached to bodyB.
    float32 lengthB;

    /// The pulley ratio, used to simulate a block-and-tackle.
    float32 ratio;
};

滑輪關節(jié)的定義如上,其相比與b2JointDef增加的屬性有:

  • groundAnchorA:剛體A對應的那個滑輪的位置。
  • groundAnchorB:剛體B對應的那個滑輪的位置。
  • localAnchorA:關節(jié)在剛體A上面的作用點。
  • localAnchorB:關節(jié)在剛體B上面的作用點。
  • lengthA:滑輪到剛體A的線的長度。
  • lengthB:滑輪到剛體B的線的長度。
  • ratio:比例(關節(jié)傳動時,滑輪上升和下降的兩頭的位移比例)。

9.2、創(chuàng)建滑輪關節(jié)

-- 創(chuàng)建一個滑輪關節(jié)定義
local pulleyJointDef    = b2PulleyJointDef() 
if true then 
    local function ccpToVecRatioIfnil(point, defaultVec2, offset)
        point   = point and M.ccpToVecRatio(point) or b2Vec2(default.x + offset.x, default.y + offset.y) 

        return point 
    end

    -- 滑輪繩子拉動的點
    local bodyWorldPointA   = bodyA:GetWorldCenter()
    -- 滑輪繩子拉動的點
    local bodyWorldPointB   = bodyB:GetWorldCenter()
    -- 剛體A對應的那個滑輪的位置
    local groundPointA      = b2Vec2(bodyWorldPointA.x, bodyWorldPointA.y + 2)
    -- 剛體B對應的那個滑輪的位置
    local groundPointB      = b2Vec2(bodyWorldPointB.x, bodyWorldPointB.y + 3)
    -- 比例(關節(jié)傳動時,滑輪上升和下降的兩頭的位移比例)
    local ratio             = 1
    -- 初始化關節(jié)
    pulleyJointDef.Initialize(bodyA, bodyB, groundPointA, groundPointB, 
        bodyWorldPointA, bodyWorldPointB, ratio)
end
-- 繩子可拉動的最大長度
pulleyJointDef.maxLengthA = 5  
-- 繩子可拉動的最大長度
pulleyJointDef.maxLengthB = 5

local pulleyJoint = tolua.cast(world:CreateJoint(pulleyJointDef), "b2PulleyJoint") 

10、輪子關節(jié)(b2WheelJoint)

10.1、輪子關節(jié)定義(b2WheelJointDef)

struct b2WheelJointDef : public b2JointDef
{
    b2WheelJointDef()
    {
        type = e_wheelJoint;
        localAnchorA.SetZero();
        localAnchorB.SetZero();
        localAxisA.Set(1.0f, 0.0f);
        enableMotor = false;
        maxMotorTorque = 0.0f;
        motorSpeed = 0.0f;
        frequencyHz = 2.0f;
        dampingRatio = 0.7f;
    }

    /// Initialize the bodies, anchors, axis, and reference angle using the world
    /// anchor and world axis.
    void Initialize(b2Body* bodyA, b2Body* bodyB, const b2Vec2& anchor, const b2Vec2& axis);

    /// The local anchor point relative to bodyA's origin.
    b2Vec2 localAnchorA;

    /// The local anchor point relative to bodyB's origin.
    b2Vec2 localAnchorB;

    /// The local translation axis in bodyA.
    b2Vec2 localAxisA;

    /// Enable/disable the joint motor.
    bool enableMotor;

    /// The maximum motor torque, usually in N-m.
    float32 maxMotorTorque;

    /// The desired motor speed in radians per second.
    float32 motorSpeed;

    /// Suspension frequency, zero indicates no suspension
    float32 frequencyHz;

    /// Suspension damping ratio, one indicates critical damping
    float32 dampingRatio;
};

輪子關節(jié)相對于b2JointDef增加的屬性是:

  • localAnchorA:關節(jié)在剛體A上面的作用點。
  • localAnchorB:關節(jié)在剛體B上面的作用點。
  • localAxisA:在剛體A中的局部移動單元軸,就是剛體A滑動的方向。
  • enableMotor:是否啟動馬達。
  • maxMotorTorque:馬達的最大轉(zhuǎn)矩。
  • motorSpeed:馬達速度。
  • frequencyHz:響應速度,數(shù)值越高,關節(jié)響應的速度越快,關節(jié)看上去越堅固。
  • dampingRatio:阻尼率:值越大,關節(jié)運動阻尼越大。

10.2、創(chuàng)建輪子關節(jié)

-- 創(chuàng)建一個輪子關節(jié)定義
local wheelJointDef             = b2WheelJointDef()
if true then 
    -- 用坐標表示輪子軸的位置
    local axis = b2Vec2(1.0, 0.0)
    -- 初始化關節(jié)
    wheelJointDef:Initialize(bodyA, bodyB, bodyB:GetWorldCenter(), axis)
end
wheelJointDef.enableMotor       = false
wheelJointDef.motorSpeed        = 0.0
wheelJointDef.maxMotorTorque    = 0.0
wheelJointDef.frequencyHz       = 32
wheelJointDef.dampingRatio      = 0.2

-- 創(chuàng)建關節(jié) 
local wheelJoint = tolua.cast(world:CreateJoint(wheelJointDef), "b2WheelJoint")

每個關節(jié)的用法基本一致,主要的差別都是創(chuàng)建關節(jié)定義的時候設置的參數(shù)不同而已,上文創(chuàng)建關節(jié)的代碼里面只是隨便設置了幾個參數(shù)。讀者可以使用b2WheelJoint關節(jié)將前文的汽車和車輪約束到一起


三、接觸(b2Contact)

在物理游戲中經(jīng)常會需要實現(xiàn)碰撞檢測的邏輯,box2D提供了b2Contact來實現(xiàn)碰撞。

1、接觸檢測

1.1、創(chuàng)建接觸檢測監(jiān)聽器

-- 創(chuàng)建接觸偵聽器
local listener = GB2ContactListener:new_local()

1.2、為接觸監(jiān)聽注冊回調(diào)句柄

-- 為接觸注冊偵聽函數(shù)
listener:registerScriptHandler(function(contactType, contact, oldManifold)
    if contactType == GB2_CONTACTTYPE_BEGIN_CONTACT then 
        print("開始接觸")
    elseif contactType == GB2_CONTACTTYPE_BEGIN_CONTACT then 
        print("結(jié)束接觸")
    elseif contactType == GB2_CONTACTTYPE_BEGIN_CONTACT then 
        print("求解前")
    elseif contactType == GB2_CONTACTTYPE_BEGIN_CONTACT then 
        print("求解后")
    end 
end) 

在接觸回調(diào)里面如果做剛體的銷毀工作可能會導致b2Contact對象引用的剛體變成一個空指針,從而造成游戲閃退。所以通??梢匝舆t一點進行銷毀。

1.3、給物理世界設置接觸監(jiān)聽

-- 設置物理世界的接觸偵聽
world:SetContactListener(listener) 

2、接觸點的獲取

剛體在接觸的過程中,可以從接觸對象(b2Contact)中獲取接觸點,代碼如下:

    -- 定義一個b2WorldManifold對象,用來獲取并存儲碰撞點的全局坐標
    local manifold  = GB2Util:newWorldManifold()
    -- 通過GetWorldManifolde方法,計算出碰撞點的全局坐標,并存儲到manifold變量中
    contact:GetWorldManifold(manifold)
    -- 獲取碰撞點
    local point     = manifold.points[0]

四、渲染世界和物理世界的同步

物理世界只是負責物理效果的模擬,但是真正要反饋給玩家的是游戲的畫面,也就是渲染世界。

修改上文的tick()方法如下:

-- 更新[物理世界]
function M.tick()
    if not world then return end 

    -- 時間步、速度迭代device、位置迭代
    local velocityIterations, positionIterations = 32, 32
    -- 物理引擎進行物理模擬,生成模擬后的數(shù)據(jù)
    world:Step(M.getTimeStep(), velocityIterations, positionIterations)

    -- 同步物理世界和渲染世界
    local body = world:GetBodyList()
    while body do 
        if body:GetUserData() then
            local spr   = tolua.cast(body:GetUserData(), "CCSprite")
            local x, y  = body:GetPosition().x * PTM_RATIO, body:GetPosition().y * PTM_RATIO
            -- 更新位置、角度,同步物理世界和渲染世界
            spr:setPosition(ccp(x, y))
            spr:setRotation(-1 * PT.radians2degrees(body:GetAngle()))
        end
        -- 獲得下一個body
        body = body:GetNext()
    end
end 

實現(xiàn)物理世界和渲染世界的方式就是遍歷所有的剛體,然后將剛體的用戶數(shù)據(jù)(userData)綁定的精靈的位置和旋轉(zhuǎn)角度與剛體進行同步。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • 首先要熟悉一些基本的力學名詞及相關公式 剛體(Rigidbody):剛體是指在運動中和受到力的作用后,形狀和大小不...
    FLY旳影子閱讀 12,230評論 0 11
  • 一、什么是Box2D Box2D是一個強大的開源物理游戲引擎,用來模擬2D剛體物體運動和碰撞,由Erin Catt...
    babybus_newapp閱讀 15,965評論 1 15
  • Unity中可以使用物理關節(jié)組件使物體連接起來,如圖1.1是給游戲?qū)ο筇砑雨P節(jié)組件的步驟: 也可以在物體的Insp...
    S_H_C閱讀 2,692評論 0 1
  • 特喜歡六一兒童節(jié),于我,這個節(jié)日象征著新衣服,大餐,游園會以及學校發(fā)的不知從哪里買的過期食品。 我總是喜歡將這袋食...
    lj神經(jīng)刀閱讀 386評論 0 0
  • 文 | 塞柏 其實,我們的生活并沒有那么糟,雖然生活十有八九不如意,但是只要好好生活,日子過得總還是說的過去。 上...
    塞柏閱讀 592評論 0 1

友情鏈接更多精彩內(nèi)容