1. 程式人生 > >cocos2d-x box2d物理引擎深入研究 第一篇之旋轉關節詳解(b2RevoluteJoint)

cocos2d-x box2d物理引擎深入研究 第一篇之旋轉關節詳解(b2RevoluteJoint)

對於旋轉關節場常見的包括如下:

  • 滾輪或滾筒
  • 鏈條或懸橋(使用多個旋轉聯結器)
  • 破布娃娃的關節
  • 轉門,彈射器,槓桿

建立旋轉關節

建立旋轉關節首先設定b2RevoluteJointDef屬性,然後用世界物件建立之.

然後我們看一堆關於旋轉關節的屬性。

  • localAnchorA - 基於剛體A的本地座標系,在剛體A上的點,剛體A圍繞這一點進行旋轉.
  • localAnchorB - 基於剛體B的本地座標系,在剛體B上的點,剛體B圍繞這一點進行旋轉.
  • eferenceAngle - 剛體B減去剛體B的弧度值.Box2D中角度用弧度表示.
  • enableLimit - 關節的限制是否開啟
  • lowerAngle - 角度的最低限制
  • upperAngle - 角度的最高限制
  • enableMotor -關節馬達是否開啟
  • motorSpeed - 關節馬達的目標速度(線性速度)
  • maxMotorTorque - 馬達允許使用的最大扭矩值.扭矩就是力矩.力矩 = 力*力臂.

下面就用圖形化來詳細解釋這些屬性對旋轉關節(RevoluteJoint)的作用.

1.localAnchorA  和 localAnchorB.

    這兩個屬性實際都是座標點.而這兩個座標點都是基於剛體A和剛體B的自身本地座標系而言的.比如:剛體A是一個寬度和高度都是4的正方形.若localAnchorA是b2Vec2(2,2).則localAnchorA這個點就在剛體A的最右上角.因為剛體A的原點(origin point)是在剛體A的正中心(剛體A的錨點預設是(0.5,0.5)),所以b2Vec2(2,2)就自然是在正方形的最右上角.

見下圖:

見上圖:假設正方形的寬高是2,原點是在正中心.則localAnchorA(1,1)就在其右上角. localAnchorB(0,0)就是圓的正中心.這樣就相當於把正方形的右上角和圓的中心重合,並用一個旋轉關節(RevoluteJoint)進行固定.就像一顆釘子一樣.

2.eferenceAngle 參考角度.

參考角度實際上就是剛體B減去剛體A的角度.如果這個值為0,就是表示兩個剛體的角度差為0,參考角度實際上就是關節的角度,在程式中可以通過GetJointAngle()來檢視關節的旋轉程度。當你使用關節限制(joint limits)的時候,參考角度還是有用的。

看下圖:

在上圖中的左邊圖,如果參考角度為0, 則在B逆時針旋轉45角後(右邊圖),通過GetJointAngle()返回關節角度的值為45.即在參考角度上加45.

在上圖中的左邊圖,如果參考角度為123,則在B逆時針旋轉45角後(右邊圖),通過GetJointAngle()返回關節角度的值為168.即在參考角度上加45.

注意一下,bodyB的旋轉可以作為關節的角度值,並且相對於bodyA是逆時針方向。如果兩個物體同時被移動或旋轉,關節角度是不會改變的,因為它代表的是兩個物體的相對角度(Angle_B - Angle_A)。

3.旋轉關節限制(revolute joint limits)

根據目前為止我們所講解的屬性來看,關節中的兩個物體可以無限制的圍繞錨點旋轉,但是旋轉關節也可以對旋轉範圍作出限制。可以對旋轉的上限和下限作出設定,可以在’關節角度(joint angle)’方面作出設定。

假設你想對上面的例子在初始設定角度的時候範圍限定在45度內

當使用關節限制的時候有些東西需要牢記…

  • 對於enableLimits屬性的設定會同時影響兩個限制,所以如果你只想對其中一個做限制,需要把限制設定成一個非常高(例如,upper limit)或者非常低的值(例如,lower limit),以保證實際使用過程中永遠達不到此值。
  • 旋轉關節限制的設定可以超過一個全旋轉,比如說把一對lower/upper限制設定為-360,360,可以允許物體之間的相對旋轉為兩圈以內。
  • 可以很方便的把限制設定為相同值,可以把聯結器’夾’在給定的角度上。這個角度可以逐步的改變直到最後達到合適的位置,並保持其與另一個物體的之前的狀態,並且不需要聯結器馬達(joint motor)。
  • 在有限的時間步長內,快速的旋轉會穿過設定的限制,直到旋轉速度被修正。
  • 檢查當前是否達到限制非常簡單:
bool atLowerLimit = joint->GetJointAngle() <= joint->GetLowerLimit();
bool atUpperLimit = joint->GetJointAngle() >= joint->GetUpperLimit();
 

一個關節限制強制關節角度保持在lower和upper之間。限制範圍應該包含0,否則關節將在模擬開始的時候發生傾斜.

4.旋轉聯結器馬達revolute joint motor

旋轉關節預設的行為是沒有阻力的。如果你想控制物體運動讓其旋轉,需要對其施加力矩或轉動慣量。也可以設定能夠讓關節以某個特定的線速度旋轉物體的關節’馬達’。如果你想模擬具有動力效果的事物,比如汽車輪子或吊橋型別的門,這個特性還是很有用的。

線速度僅僅是一個目標速度,這也就意味著關節並不能保證達到這個速度。通過為關節馬達指定最大可允許的扭矩,你能夠控制關節最終達到目標速度的快慢程度,甚至在有些例子中決定了最終是否能夠達到目標速度.下面一段引文解釋了joint motor的最大扭矩.英文很簡單我就不翻譯了。

A joint motor allows you to specify the joint speed (the time derivative of the angle). The speed can be
negative or positive. A motor can have infinite force, but this is usually not desirable. Recall the eternal
question:
"What happens when an irresistible force meets an immovable object?"
I can tell you it's not pretty. So you can provide a maximum torque for the joint motor. The joint motor
will maintain the specified speed unless the required torque exceeds the specified maximum. When the
maximum torque is exceeded, the joint will slow down and can even reverse.

使用關節馬達的時候需要注意一些事情…

  • 為最大力矩設定一個小值,關節會花一些時間到達期望的速度。如果你增加關節所連線的物體,讓其更重一點兒的話,如果你還想讓物體保持同樣的加速度,你需要增加最大扭矩值。
  • 可以設定馬達速度為零,讓關節保持靜止。為最大扭矩設定一個較低的值,起到剎車的作用,逐漸關節慢下來。使用高最大扭矩值可以讓關節迅速停下來,需要一個很大的力移動關節,有點類似生鏽的輪子。
  • 驅動汽車或者說是車輛的輪子可以通過直接改變馬達的速度,當汽車停止的時候,通常會把目標速度設定為零

下面以一個例項來進行說明.

首先是初始化:

		//設定重力
		b2Vec2 gravity;
		gravity.Set(0.0f, 0.0f);
		m_world = new b2World(gravity);
		//設定是否sleep
		m_world->SetAllowSleeping(true);
		m_world->SetContinuousPhysics(true);

		//建立GLESDebugDraw物件.在DEBUG除錯環境下對剛體輪廓和關節等資訊
		//進行繪製。它是在b2World::Step中被呼叫.
		m_DebugDraw = new GLESDebugDraw(32.0f);
		m_world->SetDebugDraw(m_DebugDraw);
		uint32 flags = 0;
		//e_shapeBit:剛體的輪廓被繪製出來
		flags += b2Draw::e_shapeBit;
		m_DebugDraw->SetFlags(flags);


然後建立正方形和圓形的剛體

		b2BodyDef bodyDef;
		bodyDef.type = b2_dynamicBody;
 		b2FixtureDef fixtureDef;
		fixtureDef.density = 1;
		fixtureDef.filter.groupIndex = int16(1);
		fixtureDef.filter.categoryBits = uint16(2);
		fixtureDef.filter.maskBits = uint16(12);
 		b2PolygonShape boxShape;
 		boxShape.SetAsBox(2,2);
		CCSize size = CCDirector::sharedDirector()->getWinSize();
		bodyDef.position.Set(size.width/2/32, size.height/2/32);
		fixtureDef.shape = &boxShape;
		m_bodyA = m_world->CreateBody( &bodyDef );
		m_bodyA->CreateFixture( &fixtureDef );

		b2BodyDef bodyDefEx;
		bodyDefEx.type = b2_dynamicBody;
		bodyDefEx.position.Set(size.width/2/32, size.height/2/32);
		b2FixtureDef fixtureDedEx;
		fixtureDedEx.density = 1;
		fixtureDedEx.filter.groupIndex = int16(1);
		fixtureDedEx.filter.categoryBits = uint16(4);
		fixtureDedEx.filter.maskBits = uint16(12);
		b2CircleShape boxShapeEx;
		boxShapeEx.m_radius = 2;	
		fixtureDedEx.shape = &boxShapeEx;
		m_bodyB = m_world->CreateBody(&bodyDefEx);
		m_bodyB->CreateFixture(&fixtureDedEx);


最後建立關節,把兩個剛體連線起來.

		b2RevoluteJointDef revoluteJointDef;
		revoluteJointDef.bodyA = m_bodyA;
		revoluteJointDef.bodyB = m_bodyB;
		revoluteJointDef.collideConnected = false;
		revoluteJointDef.localAnchorA.Set(2,2);
		revoluteJointDef.localAnchorB.Set(0,0);
		revoluteJointDef.motorSpeed = 100;
		revoluteJointDef.maxMotorTorque = 100;
		revoluteJointDef.enableMotor = true;
		revoluteJointDef.referenceAngle = 0;
 		revoluteJointDef.enableLimit = true;
 		revoluteJointDef.lowerAngle =-45  ;
 		revoluteJointDef.upperAngle =  45;
	   m_joint =  (b2RevoluteJoint*)m_world->CreateJoint( &revoluteJointDef );


剛體A順時針旋轉。剛體B逆時針旋轉.

沒看到哪裡可以上傳附件的地方。鬱悶。否則把原始碼傳上去 。