Символ JavaGame не движется точно во время рисования?
Для личной практики я переделываю Flappy bird для настольных компьютеров на Java, мне удалось добиться генерации всех опор, перемещения экрана, bg, но теперь у меня есть одна проблема - производительность.
Иногда я чувствую, что игра движется не так быстро, а иногда застревает на 0,5 секунды или еще что-то, но это не тот случай, когда я двигаю свою птицу, она движется немного странно, похоже, что она движется слишком сильно вперед, а затем назад смотрите gif в формате MP4:
http://gyazo.com/d7e94c0b772192e5a5dd1d2b61b8c529
Что может быть причиной этого? это может быть мой игровой цикл или то, как я рисую графику? Я не использую двойную буферизацию, я выполняю рендеринг, просто вызывая.repaint в jframe, который перерисовывает добавленную мной JPanel.
Игровой цикл:
private void gameLoop() {
new Thread() {
private long last;
private long start;
private int wait;
public void run() {
while(game) {
long now = System.nanoTime();
long length = now - lastLoop;
lastLoop = now;
double delta = length / ((double) Constants.OPTIMAL);
lastFps += length;
fps++;
if (lastFps >= 1000000000) {
System.out.println("FPS: " + fps);
lastFps = 0;
fps = 0;
}
update(delta);
render();
this.sleep
}
}
private void sleep() {
try {
Thread.sleep((this.last - System.nanoTime() + Constants.OPTIMAL) / 1000000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.start();
}
Моя область рисования:
public void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
int translateAmount = this.game.getLevel().getTranslate();
g.translate(-translateAmount, 0);
this.background.render(g2d);
this.game.getLevel().renderLevel(g2d);
g.translate(0, 0);
this.game.getBird().render(g2d);
}
Вот как я рисую фон: я добавляю фон один за другим, и если фон находится за рамкой, я не буду его рендерить.
public void render(Graphics2D g) {
Iterator<RepeatedBackground> itr = this.repeats.iterator();
while (itr.hasNext()) {
RepeatedBackground b = itr.next();
if (this.game.getLevel().getTranslate() - b.getX() >= 300) {
itr.remove();
continue;
}
if (b.getX() - this.game.getLevel().getTranslate() < Constants.WIDTH) {
b.render(g);
}
}
}
Вот как я двигаю свою птицу (я не использую дельту, я использовал некоторые уроки по этому вопросу):
private void update(double delta) {
if (System.currentTimeMillis() - this.level.getTime() >= Constants.TRANSLATE_SPEED) {
// move background
this.level.updateTranslate();
this.level.setTime();
// move bird
this.getBird().move();
}
}
public void move() {
this.x += 2 / 1.10;
}
Что может быть причиной отставания птицы или фона? Что-то не так с моими способами рендеринга или игровым циклом?
Fps всегда печатает это:
FPS: 1724172
FPS: 1551857
FPS: 1494378
FPS: 1471987
FPS: 1434095
FPS: 1629905
1 ответ
Ваш игровой цикл смущает меня - но это не сложно;)
В принципе, как я понимаю эти вещи, это должно работать что-то вроде...
while (gamming) {
now = get current time;
update game state
delta = get current time - now
delay = desired delay - delta
wait for delay
}
Вы, кажется, не принимаете во внимание количество времени, которое render
а также update
методы "могут" занять выполнение и вычисление времени ожидания на основе этих требований...
Теперь я не уверен, что ваш delta
Значения должны быть... время через текущую секунду?? Так что я оставил это вне своего примера...
Следующий пример дает мне постоянные 25 кадров в секунду, даже со случайными задержками в update
а также render
методы
Теперь, простите, я обычно работаю за миллисекунды, это просто на мой слабый и слабый ум.
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
public class TestGameLoop {
public static void main(String[] args) {
new TestGameLoop();
}
public TestGameLoop() {
gameLoop();
}
public static class Constants {
public static final double OPTIMAL = 25; // fps...
}
private boolean game = true;
private long lastLoop;
private long lastFps;
private long fps;
private void gameLoop() {
new Thread() {
private long last;
private long start;
private int wait;
public void run() {
// Calculate the optimal/maximum delay time
// This is converted to nanos so it can be
// used to calculate the actual delay...
long millisPerSecond = TimeUnit.MILLISECONDS.convert(1, TimeUnit.SECONDS);
long optimalDelay = Math.round(millisPerSecond / Constants.OPTIMAL);
optimalDelay = TimeUnit.MILLISECONDS.toNanos(optimalDelay);
// Last start of a "second" loop
long loop = System.nanoTime();
// While gaming...
while (game) {
// Start of this cycle...
long now = System.nanoTime();
// Update the state and render the
// current frame...
update();
render();
// How long did that update take??
long timeTaken = System.nanoTime();
long delta = timeTaken - now;
// Subtract the delay from the maximum delay
long delay = optimalDelay - delta;
if (delay > 0) {
try {
// Sleep expects milliseconds...
delay = TimeUnit.NANOSECONDS.toMillis(delay);
Thread.sleep(delay);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
// Calculate if we've being running for a second yet...
long loopDelay = TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - loop);
// If the loop has been cycling for a second...
if (loopDelay >= 1) {
// Reset the loop time
loop = System.nanoTime();
System.out.println("FPS = " + fps);
fps = 0;
} else {
// Add another frame to the pile...
fps++;
}
}
}
}.start();
}
public void update() {
try {
Thread.sleep(Math.round(Math.random() * 20));
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
public void render() {
try {
Thread.sleep(Math.round(Math.random() * 20));
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}