一、物理編輯器的使用
在本系列博客的第一篇就介紹了如何使用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ì)信息。

刷新模擬器就可以看到編輯的車身顯示在場景中(要開啟物理調(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")
刷新模擬器,可以看到如下畫面:

二、物理更新
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)角度與剛體進行同步。