LibGDX 修复了使用 ashley 进行插值的时间步游戏循环
LibGDX Fixed timestep gameloop with interpolation using ashley
我有一个问题,因为我不能 100% 确定我是否理解正确。我的问题是关于 带插值的固定时间步长游戏循环 。
我做了一个小测试程序,它似乎可以工作,但我不确定它是否正确(请参阅下面的代码片段)。
我在 y 轴上以 1000 的速度创建了两个彼此相邻的实体,我可以清楚地看到 "stuttering" 非插值实体,而插值实体移动非常平滑。
我的问题是我的插值是否正确。我正在保存实体每帧的 先前位置,并将其插值到实际的当前位置 。这是正确的还是插值应该针对未来的下一个位置?
希望你明白我的意思:)
感谢您的帮助!
问候
西蒙
P.S.: Family.all(...) 的警告也很烦人,我不得不使用我个人不喜欢的 suppresswarning。有没有正确的方法来调用它而没有警告?
编辑:已解决 - 原因是我的 sourceCompatibility 与 Java 1.6 兼容。我现在将它设置为 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();
}
感谢@Tenfour04 提供详细信息。我做了一些最后的调整,对我来说现在看起来非常流畅。我改变的事情:
- 覆盖游戏实例的渲染方法以使用 getRawDeltaTime 而不是 getDeltaTime 因为 getDeltaTime 平均帧之间的时间而不是使用原始值
- 我现在使用 0.25,而不是通过物理步骤来限制 delta。没有特殊原因,但似乎很多人都在使用这个值
- 我现在使用 interpolate 插值法 smoother
而不是使用 lerp 进行插值
改变这三件事(并将物理步骤更改为 60fps)后,对我来说似乎非常顺利。
我有一个问题,因为我不能 100% 确定我是否理解正确。我的问题是关于 带插值的固定时间步长游戏循环 。
我做了一个小测试程序,它似乎可以工作,但我不确定它是否正确(请参阅下面的代码片段)。
我在 y 轴上以 1000 的速度创建了两个彼此相邻的实体,我可以清楚地看到 "stuttering" 非插值实体,而插值实体移动非常平滑。
我的问题是我的插值是否正确。我正在保存实体每帧的 先前位置,并将其插值到实际的当前位置 。这是正确的还是插值应该针对未来的下一个位置?
希望你明白我的意思:)
感谢您的帮助!
问候 西蒙
P.S.: Family.all(...) 的警告也很烦人,我不得不使用我个人不喜欢的 suppresswarning。有没有正确的方法来调用它而没有警告? 编辑:已解决 - 原因是我的 sourceCompatibility 与 Java 1.6 兼容。我现在将它设置为 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();
}
感谢@Tenfour04 提供详细信息。我做了一些最后的调整,对我来说现在看起来非常流畅。我改变的事情: - 覆盖游戏实例的渲染方法以使用 getRawDeltaTime 而不是 getDeltaTime 因为 getDeltaTime 平均帧之间的时间而不是使用原始值 - 我现在使用 0.25,而不是通过物理步骤来限制 delta。没有特殊原因,但似乎很多人都在使用这个值 - 我现在使用 interpolate 插值法 smoother
而不是使用 lerp 进行插值改变这三件事(并将物理步骤更改为 60fps)后,对我来说似乎非常顺利。