首先,让我描述一下我所说的卡顿现象。当玩家移动时,它看起来好像向前移动了一点,然后又回到了原来的位置,并且一直这样循环。我正在使用lwjgl3制作一个小游戏进行学习,并且使用JOML作为我的数学库。我实现了一个固定时间步长循环(FPS = 60和UPS = 30),并且我使用插值尝试平滑我的玩家移动。有时它效果不错(虽然不如我所希望的那么平滑),但有时却和没有使用插值一样卡顿。有什么想法可以解决这个问题吗?我是否正确地使用了插值?
游戏循环:
@Override
public void run() {
window.init("Game", 1280, 720);
GL.createCapabilities();
gameApp.init();
timer.init();
float delta;
float accumulator = 0f;
float interval = 1f / Settings.TARGET_UPS;
float alpha;
while (running) {
delta = timer.getDelta();
accumulator += delta;
gameApp.input();
while (accumulator >= interval) {
gameApp.update();
timer.updateUPS();
accumulator -= interval;
}
alpha = accumulator / interval;
gameApp.render(alpha);
timer.updateFPS();
timer.update();
window.update();
if (Settings.SHOW_PERFORMANCE) {
System.out.println("FPS: " + timer.getFPS() + " UPS: " + timer.getUPS());
}
if (window.windowShouldClose()) {
running = false;
}
}
gameApp.cleanUp();
window.cleanUp();
}
SpriteRenderer:
public class SpriteRenderer {
public StaticShader staticShader;
public SpriteRenderer(StaticShader staticShader, Matrix4f projectionMatrix) {
this.staticShader = staticShader;
staticShader.start();
staticShader.loadProjectionMatrix(projectionMatrix);
staticShader.stop();
}
public void render(Map<TexturedMesh, List<Entity>> entities, float alpha) {
for (TexturedMesh mesh : entities.keySet()) {
prepareTexturedMesh(mesh);
List<Entity> batch = entities.get(mesh);
for (Entity entity : batch) {
Vector2f spritePos = entity.getSprite().getTransform().getPosition();
Vector2f playerPos = entity.getTransform().getPosition();
spritePos.x = playerPos.x * alpha + spritePos.x * (1.0f - alpha);
spritePos.y = playerPos.y * alpha + spritePos.y * (1.0f - alpha);
prepareInstance(entity.getSprite());
GL11.glDrawArrays(GL11.GL_TRIANGLES, 0, entity.getSprite().getTexturedMesh().getMesh().getVertexCount());
}
unbindTexturedMesh();
}
}
private void unbindTexturedMesh() {
GL20.glDisableVertexAttribArray(0);
GL20.glDisableVertexAttribArray(1);
GL30.glBindVertexArray(0);
}
private void prepareInstance(Sprite sprite) {
Transform spriteTransform = sprite.getTransform();
Matrix4f modelMatrix = Maths.createModelMatrix(spriteTransform.getPosition(), spriteTransform.getScale(), spriteTransform.getRotation());
staticShader.loadModelMatrix(modelMatrix);
}
private void prepareTexturedMesh(TexturedMesh texturedMesh) {
Mesh mesh = texturedMesh.getMesh();
mesh.getVao().bind();
GL20.glEnableVertexAttribArray(0);
GL20.glEnableVertexAttribArray(1);
GL13.glActiveTexture(GL13.GL_TEXTURE0);
texturedMesh.getTexture().bind();
}
}
EntityPlayer:
public class EntityPlayer extends Entity {
private float xspeed = 0;
private float yspeed = 0;
private final float SPEED = 0.04f;
public EntityPlayer(Sprite sprite, Vector2f position, Vector2f scale, float rotation) {
super(sprite, position, scale, rotation);
this.getSprite().getTransform().setPosition(position);
this.getSprite().getTransform().setScale(scale);
this.getSprite().getTransform().setRotation(rotation);
}
@Override
public void update() {
this.getTransform().setPosition(new Vector2f(this.getTransform().getPosition().x += xspeed, this.getTransform().getPosition().y += yspeed));
}
public void input() {
if (KeyboardHandler.isKeyDown(GLFW.GLFW_KEY_RIGHT)) {
xspeed = SPEED;
} else if (KeyboardHandler.isKeyDown(GLFW.GLFW_KEY_LEFT)) {
xspeed = -SPEED;
} else {
xspeed = 0;
}
if (KeyboardHandler.isKeyDown(GLFW.GLFW_KEY_UP)) {
yspeed = SPEED;
} else if (KeyboardHandler.isKeyDown(GLFW.GLFW_KEY_DOWN)) {
yspeed = -SPEED;
} else {
yspeed = 0;
}
}
}
计时器:
public class Timer {
private double lastLoopTime;
private float timeCount;
private int fps;
private int fpsCount;
private int ups;
private int upsCount;
public void init() {
lastLoopTime = getTime();
}
public double getTime() {
return GLFW.glfwGetTime();
}
public float getDelta() {
double time = getTime();
float delta = (float) (time - lastLoopTime);
lastLoopTime = time;
timeCount += delta;
return delta;
}
public void updateFPS() {
fpsCount++;
}
public void updateUPS() {
upsCount++;
}
// Update the FPS and UPS if a whole second has passed
public void update() {
if (timeCount > 1f) {
fps = fpsCount;
fpsCount = 0;
ups = upsCount;
upsCount = 0;
timeCount -= 1f;
}
}
public int getFPS() {
return fps > 0 ? fps : fpsCount;
}
public int getUPS() {
return ups > 0 ? ups : upsCount;
}
public double getLastLoopTime() {
return lastLoopTime;
}
}
PerformanceCounters
(Windows)或RDTSC
(x86 x64)。引起卡顿的另一个原因可能是你的渲染与显示器同步信号不同步,以及双/三重缓冲或缓冲序列中的错误(偶尔交换它们)。 - undefined[us]
为单位)。我通过连接其他集成电路并通过ZEXALL来传递信号,这比仅仅渲染要困难得多。请参考这个链接:通过DeltaTime控制精灵序列 以及其中的所有子链接,这应该是解决你的问题的良好起点... 如果问题与时间无关,那可能是你使用的图形API是什么(GDI封装在VCL、FireMonkey或GL、DX、X11等)? - undefined