2013-04-02 1 views
2

Le bloc de levage sur le raccord prismatique (b2PrismaticJointDef) sur le point supérieur donne une impulsion au corps dynamique qui repose sur la surface du bloc de levage même si le bloc de levage ne bouge pas du tout.Comportement étrange du raccord prismatique du moteur javascript du moteur Box2D

Peut-être que c'est un bug de port javascript. Mais je veux le réparer parce que j'ai besoin d'ascenseurs dans mon jeu.

Mise à jour v1

Je l'ai utilisé box2dweb javascript port de Box2DFlash 2.1a https://code.google.com/p/box2dweb/.

Update v2

Voici démonstration similaire sur le flash http://hyzhak.github.com/darlingjs/performance/box2dweb/ il a les mêmes problèmes, alors peut-être cette question sur le flash ou sur le moteur Box2D d'origine.

http://jsfiddle.net/hyzhak/2kjDZ/

var b2Vec2 = Box2D.Common.Math.b2Vec2, 
    b2BodyDef = Box2D.Dynamics.b2BodyDef, 
    b2Body = Box2D.Dynamics.b2Body, 
    b2FixtureDef = Box2D.Dynamics.b2FixtureDef, 
    b2World = Box2D.Dynamics.b2World, 
    b2PolygonShape = Box2D.Collision.Shapes.b2PolygonShape, 
    b2CircleShape = Box2D.Collision.Shapes.b2CircleShape,   
    b2DebugDraw = Box2D.Dynamics.b2DebugDraw, 
    b2PrismaticJointDef = Box2D.Dynamics.Joints.b2PrismaticJointDef; 

(function() {  
    var world = buildWorld(); 

    var leftBlock = buildBlock({world: world, x: 2, y: 8, width: 2, height: 12, static: true}); 
    var rightBlock = buildBlock({world: world, x: 12, y: 8, width: 2, height: 12, static: true}); 
    var bottomBlock = buildBlock({world: world, x: 7, y: 13, width: 8, height: 2, static: false}); 
    var box = buildBlock({world: world, x: 7, y: 10, width: 2, height: 2, static: false}); 

    var joint = buildPrismaticJoint({world: world, 
            anchorA: new b2Vec2(7, 13), 
            axis: new b2Vec2(0, 1), 
            bodyA: bottomBlock, 
            bodyB: world.GetGroundBody()}); 

    var debugDraw = buildDebugDraw(world); 

    setInterval(function(){ 
     world.Step(1/60, 10, 10); 
     world.DrawDebugData(); 
     world.ClearForces(); 
    },1000/60); 
})(); 

function buildWorld() {  
    return new b2World(
     new b2Vec2(0, 10), //gravity vector 
     true 
    ); 
} 

function buildBlock(state) { 
    var fixDef = new b2FixtureDef; 
    fixDef.shape = new b2PolygonShape; 
    fixDef.density = 1.0; 
    fixDef.friction = 0.5; 
    fixDef.restitution = .5;   
    fixDef.shape.SetAsBox(state.width/2, state.height/2); 
    var bodyDef = new b2BodyDef; 
    bodyDef.type = state.static?b2Body.b2_staticBody:b2Body.b2_dynamicBody; 
    bodyDef.position.Set(state.x, state.y); 
    var body = state.world.CreateBody(bodyDef); 
    body.CreateFixture(fixDef); 
    return body; 
} 

//buildPrismaticJoint(world, 9, 15, 0, 1, bottomBlock, world.GetGroundBody()); 
function buildPrismaticJoint(state) { 
    var jointDef = new b2PrismaticJointDef(); 
    jointDef.Initialize(state.bodyA, state.bodyB, state.anchorA, state.axis); 
    jointDef.collideConnected = false; 
    jointDef.lowerTranslation = 0.0; 
    jointDef.upperTranslation = 5.0; 
    jointDef.enableLimit = true; 
    jointDef.maxMotorForce = 400.0; 
    jointDef.motorSpeed = 3.0; 
    jointDef.enableMotor = true; 
    return state.world.CreateJoint(jointDef); 
} 

function buildDebugDraw(world) { 
    var debugDraw = new b2DebugDraw(); 
    debugDraw.SetSprite(document.getElementById("playground").getContext("2d")); 
    debugDraw.SetDrawScale(20.0); 
    debugDraw.SetFillAlpha(0.5); 
    debugDraw.SetLineThickness(1.0); 
    debugDraw.SetFlags(
     b2DebugDraw.e_shapeBit | 
     b2DebugDraw.e_jointBit | 
     b2DebugDraw.e_aabbBit | 
     b2DebugDraw.e_pairBit | 
     b2DebugDraw.e_centerOfMassBit | 
     b2DebugDraw.e_controllerBit 
    ); 
    world.SetDebugDraw(debugDraw); 

    return debugDraw; 
} 

Répondre

0

J'ai eu le même problème en utilisant le code Box2D V2.2.1 C++. J'utilise un b2PrismaticJoint entre un corps statique et un corps mobile (dynamique) pour simuler un «lift» (ou ascenseur).

Lorsque l'élévateur atteint l'extrémité supérieure de la translation le long du joint prismatique, il semble s'arrêter. Cependant, lorsqu'un autre corps est assis au sommet de l'ascenseur et qu'il atteint l'extrémité supérieure de la translation, le corps sur le dessus de l'ascenseur commence à "rebondir" comme si l'ascenseur appliquait toujours une force. (Je crois que c'est exactement ce qui se passe, c'est-à-dire que le corps de l'ascenseur ne se déplace pas plus loin le long du joint prismatique, mais le moteur du joint prismatique continue de pousser et d'appliquer des forces vers le haut).

J'ai d'abord résolu ce problème en réglant manuellement la vitesse du moteur du joint prismatique à 0,0 lorsque l'extrémité supérieure de la translation était atteinte. Dans ma boucle de jeu principale, c'est-à-dire la fonction qui est appelée à la fréquence d'images ("tick:" dans Box2D ou "update:" dans Cocos2D), j'ai inséré ce qui suit (J'utilise Objective-C ici et kLiftJointValue est un constante entière arbitraire):

// Iterate over all joints (used to prevent upper end oscillations from psimaticJoints on lifts) 
for (b2Joint* j = world->GetJointList(); j; j = j->GetNext()) 
{ 
    if ((int)(j->GetUserData()) == kLiftJointValue) // check to see if lift is at upper end of translation 
    { 
     CGFloat currentTranslation = ((b2PrismaticJoint*)j)->GetJointTranslation(); 
     CGFloat lowerLimit = ((b2PrismaticJoint*)j)->GetLowerLimit(); 
     CGFloat upperLimit = ((b2PrismaticJoint*)j)->GetUpperLimit(); 
     b2Body *bodyA = j->GetBodyA(); 
     b2Body *bodyB = j->GetBodyB(); 
     BOOL notStopped = (bodyA->GetType() == b2_dynamicBody || bodyB->GetType() == b2_dynamicBody); 

     if (fabsf(currentTranslation - lowerLimit) <= 0.01*(upperLimit - lowerLimit) && notStopped) 
     { 
      CCLOG(@"Stopping platform"); 
      (j->GetBodyA())->SetType(b2_staticBody); 
      (j->GetBodyB())->SetType(b2_staticBody); 
      ((b2PrismaticJoint*)j)->SetMotorSpeed(0.0); 
     } 
    } 
} 

le code ci-dessus simplement itère sur tous les joints dans le monde et cherche ceux étiquetés de façon appropriée, et si le corps a traduit à 1% de la traduction maximale autorisée alors le corps en mouvement est le moteur est désactivé en réglant la vitesse du moteur sur 0. Vous pouvez également utiliser la détection de collision Box2D et régler la vitesse du moteur sur 0 de cette manière ...

J'ai alors réalisé que la meilleure façon de mettre en œuvre ce ascenseur (sans déranger tout ce qui se trouvait au-dessus de l'ascenseur) était de ralentir lentement la vitesse du moteur de l'articulation prismatique progressivement. Voici comment je l'ai fait:

// Iterate over all joints (used to prevent upper end oscillations from psimaticJoints on lifts) 
for (b2Joint* j = world->GetJointList(); j; j = j->GetNext()) 
{ 
    if ((int)(j->GetUserData()) == kLiftJointValue) // check to see if lift is at upper end of translation 
    { 
     CGFloat currentTranslation = ((b2PrismaticJoint*)j)->GetJointTranslation(); 
     CGFloat lowerLimit = ((b2PrismaticJoint*)j)->GetLowerLimit(); 
     CGFloat upperLimit = ((b2PrismaticJoint*)j)->GetUpperLimit(); 
     b2Body *bodyA = j->GetBodyA(); 
     b2Body *bodyB = j->GetBodyB(); 
     BOOL notStopped = (bodyA->GetType() == b2_dynamicBody || bodyB->GetType() == b2_dynamicBody); 

     if (fabsf(currentTranslation - lowerLimit) <= 0.25*(upperLimit - lowerLimit) && notStopped) 
     { 
      // Get current motor speed and update 
      CGFloat currentSpeed = ((b2PrismaticJoint*)j)->GetMotorSpeed(); 
      CGFloat newSpeed; 

      if (currentSpeed < 0.0) 
      { 
       if (fabsf(currentTranslation - lowerLimit) <= 0.01*(upperLimit - lowerLimit) && notStopped) 
       { 
        CCLOG(@"Stopping platform"); 
        (j->GetBodyA())->SetType(b2_staticBody); 
        (j->GetBodyB())->SetType(b2_staticBody); 
        ((b2PrismaticJoint*)j)->SetMotorSpeed(0.0); 
       } 
       else 
       { 
        CCLOG(@"Decelerating: speed=%f", currentSpeed); 
        newSpeed = 0.95*currentSpeed; 
       } 
      } 
      else if (currentSpeed > 0.0) 
      { 
       CCLOG(@"Accelerating: speed=%f", currentSpeed); 

       newSpeed = 1.05*currentSpeed; 

       if (newSpeed > 20.0) 
        newSpeed = 20.0; 
      } 

      // update motor speed 
      ((b2PrismaticJoint*)j)->SetMotorSpeed(newSpeed); 
     }    
    } 
} 

Vous aurez besoin de jouer avec les différentes constantes (en particulier les constantes 0,95 et 1,05), car ils sont parfaitement adaptés aux besoins des propriétés motrices spécifiques (maxMotorForce = 1000 et MotorSpeed ​​= 20 dans mon cas). Bien sûr, ailleurs dans mon projet, j'ai des minuteries qui réinitialisent les propriétés du corps de levage et du moteur de joint si nécessaire (pour le faire remonter, puis redescendre en inversant la vitesse du moteur de l'articulation avec une valeur positive).Je n'ai pas mis en œuvre la technique d'accélération/décélération lorsque l'ascenseur était translaté près de l'autre limite (inférieure), car je ne m'inquiétais pas de ce que l'ascenseur soulevait des forces dans cette direction lorsque l'ascenseur s'arrêtait. Cependant, la même approche que celle décrite ci-dessus peut être utilisée pour accélérer ou décélérer en douceur lorsque l'ascenseur quitte ou retourne au fond ...