Bla*_*olf 6 java frame-rate game-engine game-loop
我最近尝试进入游戏编程.我对Java很有经验,但对游戏编程却没有.我阅读http://www.koonsolo.com/news/dewitters-gameloop/并使用以下代码实现了那里提出的游戏循环:
private static int UPDATES_PER_SECOND = 25;
private static int UPDATE_INTERVAL = 1000 / UPDATES_PER_SECOND * 1000000;
private static int MAX_FRAMESKIP = 5;
public void run() {
while (true) {
int skippedFrames = 0;
while (System.nanoTime() > this.nextUpdate && skippedFrames < MAX_FRAMESKIP) {
this.updateGame();
this.nextUpdate += UPDATE_INTERVAL;
skippedFrames++;
}
long currentNanoTime = System.nanoTime();
double interpolation = (currentNanoTime + UPDATE_INTERVAL - this.nextUpdate) / UPDATE_INTERVAL;
this.repaintGame(interpolation);
}
}
Run Code Online (Sandbox Code Playgroud)
循环看起来很有前途和容易,但现在我实际上正在尝试用它做点什么我不再那么肯定了.如果我没有完全错误地updateGame()处理诸如计算位置,移动敌人,计算碰撞等事情,......?由于插值没有传递给updateGame(),这是否意味着我们假设每秒updateGame()精确稳定地调用UPDATES_PER_SECOND?这是否意味着我们所有的计算都基于这个假设?如果 - 无论出于何种原因 - 对updateGame()的调用被延迟,这不会给我们带来很多麻烦吗?
例如,如果我的角色精灵应该向右走,我们根据它的速度移动它updateGame()- 如果方法被延迟,这将意味着我们的计算只是关闭而角色会滞后?
在网站上,列出了以下插值示例:
如果在10Th的比赛中,位置是500,速度是100,那么在11Th的比赛中,位置将是600.所以当你渲染它时你将把你的车放在哪里?你可以采取最后一个游戏技巧的位置(在这种情况下为500).但更好的方法是预测,其中轿车将在精确的10.3,而这种情况是这样的:
view_position = position + (speed * interpolation)
这款车将随后在530位置呈现.
我知道这仅仅是一个例子,但这不会使车速依赖于UPDATES_PER_SECOND?那么更多UPS意味着更快的车?这不可能是正确的......?
任何帮助,教程,任何赞赏.
在所有的帮助之后(谢谢!),这是我目前使用的 - 到目前为止工作得很好(用于移动角色精灵),但让我们等待真正复杂的游戏内容来决定这一点.不过,我想分享一下.
我的游戏循环现在看起来像这样:
private static int UPDATES_PER_SECOND = 25;
private static int UPDATE_INTERVAL = 1000 / UPDATES_PER_SECOND * 1000000;
private static int MAX_FRAMESKIP = 5;
private long nextUpdate = System.nanoTime();
public void run() {
while (true) {
int skippedFrames = 0;
while (System.nanoTime() > this.nextUpdate && skippedFrames < MAX_FRAMESKIP) {
long delta = UPDATE_INTERVAL;
this.currentState = this.createGameState(delta);
this.newPredictedNextState = this.createGameState(delta + UPDATE_INTERVAL, true);
this.nextUpdate += UPDATE_INTERVAL;
skippedFrames++;
}
double interpolation = (System.nanoTime() + UPDATE_INTERVAL - this.nextUpdate) / (double) UPDATE_INTERVAL;
this.repaintGame(interpolation);
}
}
Run Code Online (Sandbox Code Playgroud)
如您所见,有一些变化:
delta = UPDATE_INTERVAL?是.到目前为止这是实验性的,但我认为它会起作用.问题是,只要您实际从两个时间戳计算一个增量,就会引入浮点计算错误.这些都很小,但考虑到你的更新被称为数百万次,他们加起来.而且由于第二个while循环确保我们能够了解错过的更新(例如,渲染需要很长时间),我们可以非常肯定我们每秒可以获得25次更新.最糟糕的情况:我们错过的不仅仅是MAX_FRAMESKIP更新 - 在这种情况下,更新将会丢失,游戏将会延迟.就像我说的那样,实验性的.我可能会再次将其更改为实际的delta.UPDATE_INTERVAL.这样,渲染器可以使用插值来在两者之间轻松插值.计算未来的游戏状态实际上非常简单 - 因为你的更新方法(createGameState())无论如何都要采用delta值,只需增加delta值UPDATE_INTERVAL- 这样就可以预测未来的状态.当然,未来状态假设用户输入等保持不变.如果没有,则下一个游戏状态更新将处理更改.MAX_FRAMESKIP在硬件真的很慢的情况下,它几乎是一个故障保护,以确保我们不时渲染一些东西.但是,如果这个开始,我猜我们将会有极端的滞后.插值与之前相同 - 但现在渲染器可以简单地在两个游戏状态之间进行插值,除了插值数字之外,它不必具有任何逻辑.真好!也许是为了澄清一个例子.这是我如何计算角色位置(简化一点):
public GameState createGameState(long delta, boolean ignoreNewInput) {
//Handle User Input and stuff if ignoreNewInput=false
GameState newState = this.currentState.copy();
Sprite charSprite = newState.getCharacterSprite();
charSprite.moveByX(charSprite.getMaxSpeed() * delta * charSprite.getMoveDirection().getX());
//getMoveDirection().getX() is 1.0 when the right arrow key is pressed, otherwise 0.0
}
Run Code Online (Sandbox Code Playgroud)
......然后,在窗口的绘画方法中......
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2d = (Graphics2D) g;
Sprite currentCharSprite = currentGameState.getCharacterSprite();
Sprite nextCharSprite = predictedNextState.getCharacterSprite();
Position currentPos = currentCharSprite.getPosition();
Position nextPos = nextCharSprite.getPosition();
//Interpolate position
double x = currentPos.getX() + (nextPos.getX() - currentPos.getX()) * this.currentInterpolation;
double y = currentPos.getY() + (nextPos.getY() - currentPos.getY()) * this.currentInterpolation;
Position interpolatedCharPos = new Position(x, y);
g2d.drawImage(currentCharSprite.getImage(), (int) interpolatedCharPos.getX(), (int) interpolatedCharPos.getY(), null);
}
Run Code Online (Sandbox Code Playgroud)
不要将您的游戏逻辑建立在更新间隔恒定的假设之上。包括一个精确测量两次更新之间经过的时间的游戏时钟。然后,您可以根据延迟进行所有计算,而不必担心实际的更新速率。
在这种情况下,汽车速度将以单位/秒为单位给出,增量将是自上次更新以来的总秒数:
car.position += car.velocity * delta;
Run Code Online (Sandbox Code Playgroud)
将更新游戏逻辑与绘制框架分开是一种常见的做法。正如您所说,这使得可以通过时不时地跳过渲染帧来保持稳定的更新率。
但这并不是要保持更新间隔不变。每个时间单位的更新次数最少非常重要。想象一下一辆车辆快速驶向障碍物。如果更新频率太低,两次更新之间的行进距离可能大于障碍物的总大小。车辆将直接穿过它。
| 归档时间: |
|
| 查看次数: |
2038 次 |
| 最近记录: |