gpt4 book ai didi

java - 即使有插值也固定了时间步长卡顿?

转载 作者:行者123 更新时间:2023-11-30 10:27:20 27 4
gpt4 key购买 nike

首先让我描述一下我所说的口吃是什么意思。当玩家移动时,它看起来好像向前移动了一点,然后又回到了它应该在的位置并继续这样做。我正在 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();
}

Sprite 渲染器:

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();
}
}

实体播放器:

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;
}
}

最佳答案

你的“固定时间步长”并没有你想的那么顺利。
这段代码:

while (accumulator >= interval) {
gameApp.update();
timer.updateUPS();
accumulator -= interval;
}

可能以 10000000Hz 或 0.1Hz 运行,具体取决于执行 gameApp.update() 所需的时间。

编辑:您不能确定 timer.getDelta() 每次调用时的值都大致相同。 accumulator 也是如此,它也取决于最后一次 -=interval 调用后的剩余值,但每次都以不同的 delta 开始。
操作系统可能需要更多的时间来处理它自己的进程,从而延迟你的进程。有时,您的基于度量的时间步可能运行良好,但下一秒它会停止几毫秒,足以扰乱这些度量。
此外,请注意,向 GPU 发送命令并不能保证它们会立即得到处理;也许他们积累起来,然后连续运行。

如果您希望某些代码每 M 毫秒执行一次(例如 60 FPS 为 16.6 毫秒),则使用计时器scheduleAtFixedRate()。参见 this

下一个您必须处理的问题是渲染必须在比固定步骤更短的时间内完成,否则会出现一些延迟。为了实现这个目标,只需将大部分数据(顶点、纹理等)发送到 GPU 一次。并且对于每一帧渲染,只发送更新的数据(相机位置,或者只是几个对象)。

关于java - 即使有插值也固定了时间步长卡顿?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45342366/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com