LibGDX 使用 ashley 进行插值的固定时间步长游戏循环

Posted

技术标签:

【中文标题】LibGDX 使用 ashley 进行插值的固定时间步长游戏循环【英文标题】:LibGDX Fixed timestep gameloop with interpolation using ashley 【发布时间】:2017-10-30 19:55:14 【问题描述】:

我有一个问题,因为我不能 100% 确定我是否理解正确。我的问题是关于带插值的固定时间步长游戏循环

我做了一个小测试程序,它似乎可以工作,但我不确定它是否正确(参见下面的代码 sn-ps)。

我在 y 轴上以 1000 的速度创建了两个彼此相邻的实体,我可以看到未插值的实体有明显的“卡顿”,而插值的实体移动非常顺畅。

我的问题是我的插值是否正确。我正在保存每帧实体的先前位置并将其插入到真正的当前位置。这是正确的还是应该对未来的下一个位置进行插值?

希望你明白我的意思:)

感谢您的帮助!

问候 西蒙

P.S.:Family.all(...) 的警告也很烦人,我必须使用我个人不喜欢的 suppresswarning。有没有正确的方法来调用它而不发出警告? 编辑:已解决 - 原因是我有 Java 1.6 的 sourceCompatibility。我现在将它设置为 Java 1.8,警告确实消失了(我认为这已经用 Java 1.7 解决了)

// position component
public class PositionComponent implements Component 
  public Vector2 position       = new Vector2(0, 0);
  public Vector2 previousPosition = new Vector2(0, 0);


// speed component
public class SpeedComponent implements Component 
  public Vector2 speed = new Vector2(0, 0);


// movement system to update position according to speed
public class MovementSystem extends IteratingSystem 
  private final ComponentMapper<SpeedComponent>    speedMapper;
  private final ComponentMapper<PositionComponent> positionMapper;

  @SuppressWarnings("unchecked")
  public MovementSystem() 
    super(Family.all(PositionComponent.class, SpeedComponent.class).get());
    speedMapper = ComponentMapper.getFor(SpeedComponent.class);
    positionMapper = ComponentMapper.getFor(PositionComponent.class);
  

  @Override
  protected void processEntity(Entity entity, float deltaTime) 
    final PositionComponent positionComponent = positionMapper.get(entity);
    final SpeedComponent speedComponent = speedMapper.get(entity);

    positionComponent.previousPosition.set(positionComponent.position);
    positionComponent.position.add(speedComponent.speed.x * deltaTime, speedComponent.speed.y * deltaTime);
  


// render method of libgdx screen
// fixedPhysicsStep = 1.0f / 25.0f = 25fps
@Override
public void render(float delta) 
  if (delta > fixedPhysicsStep) 
    delta = fixedPhysicsStep;
  

  accumulator += delta;
  while (accumulator >= fixedPhysicsStep) 
    world.update(fixedPhysicsStep);
    accumulator -= fixedPhysicsStep;
  

  renderer.render(accumulator / fixedPhysicsStep);


// the renderer render method that renders the first entity 
// with interpolation using lerp and the second entity without interpolation
public void render(float alpha) 
  Gdx.gl.glClearColor(0, 0, 0, 1);
  Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
  batch.begin();
  for (int i = 0; i < entities.size; ++i) 
    final Entity entity = entities.get(i);
    final PositionComponent posComp = positionComponentMapper.get(entity);

    if (i == 0) 
      posComp.previousPosition.lerp(posComp.position, alpha);
      batch.draw(img, posComp.previousPosition.x, posComp.previousPosition.y);
     else 
      batch.draw(img, posComp.position.x, posComp.position.y);
    
  
  batch.end();

【问题讨论】:

这就是传奇的 Gaffer On Games 文章中建议的方式。它确实始终导致最多一个物理延迟。根据游戏类型和物理步骤的大小,可能无法察觉。我想您可以尝试向前推断(将一个添加到您用于插值的 alpha 中)。取决于玩家按下输入时可能导致抖动的游戏。为什么要将 delta 限制在物理步长? 感谢您的回答!将 delta 钳制到物理步的原因没有特殊意义。我认为它可以是任意数字,因为我在任何地方都没有看到特殊的解释,有时我看到了 0.25,但我也看到了 0.04 和 0.01。所以我想为什么不只使用我已经拥有的常量。对于基于 RPG 的游戏,是否有推荐的 fps/clamp delta 值? 【参考方案1】:

感谢@Tenfour04 提供详细信息。我做了一些最后的调整,对我来说现在看起来很流畅。我改变的事情: - 覆盖游戏实例的渲染方法以使用 getRawDeltaTime 而不是 getDeltaTime,因为 getDeltaTime 平均帧之间的时间而不是使用原始值 - 我现在使用 0.25,而不是通过物理步骤来限制 delta。没有特殊原因,但似乎很多人都在使用这个值 - 我现在使用 interpolate 方法和 Interpolation smoother

而不是使用 lerp 进行插值

更改这三项后(并将物理步长更改为 60fps)对我来说似乎非常顺利。

【讨论】:

以上是关于LibGDX 使用 ashley 进行插值的固定时间步长游戏循环的主要内容,如果未能解决你的问题,请参考以下文章

通知用户可达性(Ashley Mills 的可达性)

当 wifi 重新打开时,Ashley Mills 可达性不发送通知?

Libgdx 之SpriteBatch深入分析

在使用pandas MultiIndex时,如何基于索引值进行插值?

7.Libgdx扩展学习之Box2D_距离关节 旋转关节

7.Libgdx扩展学习之Box2D_距离关节 旋转关节