Box2D静态刚体旋转
如标题,在没遇到这个问题之前通常是用转轴把刚体固定在某一点,然后利用扭矩来实现刚体旋转,但使用转轴的问题在于,如果一个动态刚体掉落过程中撞到这个利用转轴转动的刚体时,这个刚体会发生微小的转动,虽然这属于正常的物理特性,但对于某些游戏来说对体验的影响还是不小的。Google了N多资料,试过N多种方法,最终给Box2d的源文件加入了个自定义方法,但总算是把问题解决了。
实现步骤
在Box2D/Dynamics/b2Body.as
中加入如下函数,静态刚体依靠该函数旋转。
public function rotateAroundExternalPoint(ox:Number, oy:Number, angle:Number) : void
{
var f:b2Fixture;
if (m_world.IsLocked() == true)
return;
// begin
var newPos:b2Vec2 = new b2Vec2();
newPos.x = m_xf.position.x - ox;
newPos.y = m_xf.position.y - oy;
var s:Number = Math.sin(angle);
var c:Number = Math.cos(angle);
var na:Number;
var nb:Number;
var nc:Number;
var nd:Number;
var nx:Number;
var ny:Number;
nx = newPos.x * c + newPos.y * -s;
ny = newPos.x * s + newPos.y * c;
newPos.x = nx;
newPos.y = ny;
na = m_xf.R.col1.x * c + m_xf.R.col1.y * -s;
nb = m_xf.R.col1.x * s + m_xf.R.col1.y * c;
nc = m_xf.R.col2.x * c + m_xf.R.col2.y * -s;
nd = m_xf.R.col2.x * s + m_xf.R.col2.y * c;
m_xf.R.col1.x = na;
m_xf.R.col1.y = nb;
m_xf.R.col2.x = nc;
m_xf.R.col2.y = nd;
newPos.x += ox;
newPos.y += oy;
m_sweep.c.SetV(newPos);
m_sweep.c0.SetV(newPos);
m_xf.position.SetV(newPos);
m_sweep.a += angle;
m_sweep.a0 = m_sweep.a;
// end
var broadPhase:IBroadPhase = m_world.m_contactManager.m_broadPhase;
for (f = m_fixtureList; f; f = f.m_next)
f.Synchronize(broadPhase, m_xf, m_xf);
m_world.m_contactManager.FindNewContacts();
}
实例源码
package
{
import Box2D.Collision.*;
import Box2D.Collision.Shapes.*;
import Box2D.Common.Math.*;
import Box2D.Dynamics.*;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.KeyboardEvent;
[SWF(width="400", height="400", backgroundColor="#EEEEEE", frameRate="30")]
public class B2DTest extends Sprite
{
public var m_world:b2World;
public var m_velocityIterations:int = 10;
public var m_positionIterations:int = 10;
public var m_timeStep:Number = 1.0/30.0;
private var _body:b2Body;
private var _leftKeyDown:Boolean = false;
private var _rightKeyDown:Boolean = false;
public function B2DTest()
{
// Add event for main loop
addEventListener(Event.ENTER_FRAME, Update, false, 0, true);
stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
stage.addEventListener(KeyboardEvent.KEY_UP, onKeyUp);
// Define the gravity vector
var gravity:b2Vec2 = new b2Vec2(0.0, 10.0);
// Allow bodies to sleep
var doSleep:Boolean = false;
// Construct a world object
m_world = new b2World( gravity, doSleep);
// set debug draw
var debugDraw:b2DebugDraw = new b2DebugDraw();
debugDraw.SetSprite(this);
debugDraw.SetDrawScale(30.0);
debugDraw.SetFillAlpha(0.3);
debugDraw.SetLineThickness(1.0);
debugDraw.SetFlags(b2DebugDraw.e_shapeBit | b2DebugDraw.e_jointBit);
m_world.SetDebugDraw(debugDraw);
m_world.DrawDebugData();
// Vars used to create bodies
var body:b2Body;
var bodyDef:b2BodyDef;
var boxShape:b2PolygonShape;
var circleShape:b2CircleShape;
// Add ground body
bodyDef = new b2BodyDef();
bodyDef.position.Set(6, 8);
bodyDef.type = b2Body.b2_staticBody;
boxShape = new b2PolygonShape();
boxShape.SetAsOrientedBox(9, 0.3, new b2Vec2(0, 0));
var fixtureDef:b2FixtureDef = new b2FixtureDef();
fixtureDef.shape = boxShape;
fixtureDef.friction = 0.3;
fixtureDef.density = 0; // static bodies require zero density
// Add sprite to body userData
bodyDef.userData = new Sprite();
bodyDef.userData.graphics.beginFill(0x00FF00);
bodyDef.userData.graphics.drawRect(-270, -9, 540, 18);
bodyDef.userData.graphics.endFill();
_body = m_world.CreateBody(bodyDef);
_body.CreateFixture(fixtureDef);
// Add some objects
for (var i:int = 1; i < 10; i++){
bodyDef = new b2BodyDef();
bodyDef.position.x = Math.random() * 15 + 5;
bodyDef.position.y = Math.random() * 5;
bodyDef.type = b2Body.b2_dynamicBody;
var rX:Number = Math.random() + 0.5;
var rY:Number = Math.random() + 0.5;
// Box
if (Math.random() < 0.5){
boxShape = new b2PolygonShape();
boxShape.SetAsBox(rX, rY);
fixtureDef.shape = boxShape;
fixtureDef.density = 1.0;
fixtureDef.friction = 0.5;
fixtureDef.restitution = 0.2;
body = m_world.CreateBody(bodyDef);
body.CreateFixture(fixtureDef);
}
// Circle
else {
circleShape = new b2CircleShape(rX);
fixtureDef.shape = circleShape;
fixtureDef.density = 1.0;
fixtureDef.friction = 0.5;
fixtureDef.restitution = 0.2;
body = m_world.CreateBody(bodyDef);
body.CreateFixture(fixtureDef);
}
}
}
public function Update(e:Event):void
{
var joint:b2Vec2 = new b2Vec2(6, 6);
if (_rightKeyDown)
_body.rotateAroundExternalPoint(joint.x, joint.y, -0.05);
if (_leftKeyDown)
_body.rotateAroundExternalPoint(joint.x, joint.y, 0.05);
m_world.Step(m_timeStep, m_velocityIterations, m_positionIterations);
m_world.DrawDebugData();
// Go through body list and update sprite positions/rotations
for (var bb:b2Body = m_world.GetBodyList(); bb; bb = bb.GetNext()){
if (bb.GetUserData() is Sprite){
var sprite:Sprite = bb.GetUserData() as Sprite;
sprite.x = bb.GetPosition().x * 30;
sprite.y = bb.GetPosition().y * 30;
sprite.rotation = bb.GetAngle() * (180/Math.PI);
}
}
}
private function onKeyDown(e:KeyboardEvent):void
{
if (e.keyCode == 37)
_leftKeyDown = true;
if (e.keyCode == 39)
_rightKeyDown = true;
}
private function onKeyUp(e:KeyboardEvent):void
{
if (e.keyCode == 37)
_leftKeyDown = false;
if (e.keyCode == 39)
_rightKeyDown = false;
}
}
}
因为贴图默认的原点是在左上角,而box2d刚体默认原点是在中心,所以要想办法在draw的时候将原点放到贴图中心,实现方法很简单,这里就不多说了。