Box2D - FRIM(帧速率独立运动)

时间:2013-11-25 12:12:31

标签: actionscript-3 box2d

我过去几天一直试图为我的游戏实施一个FRIM系统。我做了一些研究并发现了this article - 它似乎很简单,所以我开始了。 一切似乎工作得很好,除了我得到一些时间混叠(移动的身体似乎向前跳了一点) - 当处理更多的Box2D步骤时会发生这种情况....我想。

private  const FIXED_TIMESTEP:Number = 1 / 60;
private  const velocityIterations:int = 8;
private  const positionIterations:int = 3;
private  var fixedTimestepAccumulator:Number = 0;
private  var fixedTimestepAccumulatorRatio:Number = 0;

public function Step(dt:Number):void
{
  //dt - time between frames - I'm passing the e.passedTime - from the enter frame event; using Starling

   fixedTimestepAccumulator += dt;
   var nSteps:uint = Math.floor(fixedTimestepAccumulator / FIXED_TIMESTEP);

   if (nSteps > 0)
   {
      fixedTimestepAccumulator  = fixedTimestepAccumulator - nSteps * FIXED_TIMESTEP;
    }

    fixedTimestepAccumulatorRatio = fixedTimestepAccumulator / FIXED_TIMESTEP;

    var nStepsClamped:int = Math.min(nSteps, MAX_STEPS);

            for (var i:int = 0; i < nStepsClamped; ++i)
            {
                    resetSmoothStates();
                    singleStep(FIXED_TIMESTEP);     

            }

            world.ClearForces();
            smoothStates(); 

}

private  function resetSmoothStates():void
{       

     for (var bb:b2Body = world.GetBodyList(); bb; bb = bb.GetNext())
    {

    if (bb.GetUserData() is MyUserData && bb.GetType() != b2Body.b2_staticBody  )
    {
                 //each of my bodies have a reference to their sprite (actor) in userData
        var _userdata:MyUserData=bb.GetUserData();
        _userdata.x =  _userdata.bodyPreviousX  =  bb.GetPosition().x * RATIO;
        _userdata.y= _userdata.bodyPreviousY =   - bb.GetPosition().y* RATIO;
        _userdata.rotation = _userdata.bodypreviousRotation=  _userdata.bodypreviousRotation =  - bb.GetAngle();

    }
   }

}

private function smoothStates():void
{

    var oneMinusRatio:Number = 1.0  - fixedTimestepAccumulatorRatio;

    for (var bb:b2Body = world.GetBodyList(); bb; bb = bb.GetNext())
    {

      if (bb.GetUserData() is MyUserData && bb.GetType() != b2Body.b2_staticBody  )
       {

    var userdata=bb.GetUserData();
    userdata.x =  (fixedTimestepAccumulatorRatio *  bb.GetPosition().x * RATIO + oneMinusRatio * userdata.bodyPreviousX)  ;
    userdata.y =   (- fixedTimestepAccumulatorRatio * bb.GetPosition().y * RATIO    + oneMinusRatio  * userdata.bodyPreviousY) ;
    userdata.rotation =  (- fixedTimestepAccumulatorRatio * bb.GetAngle() + oneMinusRatio * userdata.bodypreviousRotation);

     }
     }                      

}

private  function singleStep(dt:Number):void
{
    Input();
    world.Step(dt, velocityIterations, positionIterations);

}

我做错了什么?

任何帮助,建议将受到高度赞赏。 谢谢

2 个答案:

答案 0 :(得分:0)

我在一个正在进行的游戏中做过一次,这样我就可以将游戏锁定到一定的更新速率(这是在iOS中)。这是基于Daley的书(学习iOS游戏编程)中的代码,该书基于网络上的其他一些文章(我相信,它可能是Allen Bishop的)。代码看起来像这样:

void GameManager::UpdateGame()
{
   const uint32 MAXIMUM_FRAME_RATE = Constants::DEFAULT_OBJECT_CYCLES_PER_SECOND();
   const uint32 MINIMUM_FRAME_RATE = 10;
   const uint32 MAXIMUM_CYCLES_PER_FRAME = (MAXIMUM_FRAME_RATE/MINIMUM_FRAME_RATE);
   const double UPDATE_INTERVAL = (1.0/MAXIMUM_FRAME_RATE);

   static double lastFrameTime = 0.0;
   static double cyclesLeftOver = 0.0;

   double currentTime;
   double updateIterations;

   currentTime = CACurrentMediaTime();
   updateIterations = ((currentTime - lastFrameTime) + cyclesLeftOver);

   if(updateIterations > (MAXIMUM_CYCLES_PER_FRAME*UPDATE_INTERVAL))
   {
      updateIterations = MAXIMUM_CYCLES_PER_FRAME*UPDATE_INTERVAL;
   }

   while (updateIterations >= UPDATE_INTERVAL)
   {
      //      DebugLogCPP("Frame Running");
      updateIterations -= UPDATE_INTERVAL;
      // Set the random seed for this cycle.
      RanNumGen::SetSeed(_cycleManager->GetObjectCycle());
      // Dispatch messages.
      _messageManager->SendMessages();
      // Update all entities.
      _entityManager->Update();
      // Update the physics
      _gameWorldManager->Update(Constants::DEFAULT_OBJECT_CYCLE_SECONDS());
      // Advance the cycle clock.
      _cycleManager->Update();
   }

   cyclesLeftOver = updateIterations;
   lastFrameTime = currentTime;
}

我无法理解您的错误中的特定项目。但是,这部分是可疑的:

var nStepsClamped:int = Math.min(nSteps,MAX_STEPS);

在此之前,您使用以下内容更新了累加器:

fixedTimestepAccumulator = fixedTimestepAccumulator - nSteps * FIXED_TIMESTEP;

但是现在由于钳位(nStepsClamped),您要执行的实际步数可能会有所不同。所以你的时间累积不同于你实际执行的步数。

这有用吗?

答案 1 :(得分:0)

我决定采用另一种方法。我正在使用过滤增量时间进行物理(我知道这可能会导致一些问题)。 这就是我现在正在做的事情:

//Play around with this filter value if things don't look right
var filter:Number=0.4;
filtered_dt= time_between_frames * filter + filtered_dt * (1 - filter);

//Poll imputs and apply forces

// I use velocityIterations =6, positionIterations=3
world.Step(filtered_dt, velocityIterations, positionIterations);

//move sprites here

 world.ClearForces();

你需要做的另一件事是使用filtered_dt缩放你应用于身体的力量,这样当帧率发生很大变化时,事情就不会“爆炸”。

希望这有助于其他人...这不是完美的解决方案,但它对我有用。 如果你的移动体很慢,上面的插值方法也可以正常工作。