2016-06-24 4 views
0

Я делаю игра и я реализую FPS колпачок. Но это не работает, так как это не очень точно.FPS блокировка не точная

Это код у меня есть:

public static volatile int FPS_CAP = 60; 

@Override 
public void run() { 
    long lastTime = System.nanoTime(); 
    double amountOfTicks = 60.0; 
    double ns = 1000000000/amountOfTicks; 
    double delta = 0; 
    long timer = System.currentTimeMillis(), lastRender; 
    while (running) { 
     long now = System.nanoTime(); 
     delta += (now - lastTime)/ns; 
     lastTime = now; 
     while (delta >= 1) { 
      tick(); 
      delta--; 
     } 
     lastRender = System.currentTimeMillis(); 
     draw.render(); 
     draw.fps++; 

     if (FPS_CAP != -1) { 
      try { 
       int nsToSleep = (int) ((1000/FPS_CAP) - (System.currentTimeMillis() - lastRender)); 

       if (nsToSleep > 1/FPS_CAP) { 
        Thread.sleep(nsToSleep); 
       } 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 
     } 

     if (System.currentTimeMillis() - timer > 1000) { 
      timer += 1000; 
      draw.lastFPS = draw.fps; 
      draw.fps = 0; 
      // updates = 0; 
     } 
    } 
} 

Это должно быть укупорки FPS на 60 кадров в секунду, но вместо того, чтобы результат: enter image description here

Как вы можете видеть это действительно неточно, иногда это намного ниже 60, а иногда и выше! И проблема иногда не достигая 60 не относится к моему оборудованию для ПК, так как при разблокировке я получаю 1700-2000 FPS. Я хочу, чтобы он был как можно более точным.

Заранее спасибо.

+1

Хмм, возможно, это связано с тем, что 'System.currentTimeMillis' не всегда очень точен? Хотя это время в мс, его разрешение зависит от ОС, и оно может увеличиваться с шагом 10 или 15 мс. При 60 кадрах в секунду вы говорите о 16,7 мс за кадр, чтобы это могло изменить ситуацию? – Arjan

+1

Java недостаточно точен для достижения таких механизмов точности. Все библиотеки рендеринга на самом деле полагаются на уровень C или C++ для управления точностью в реальном времени. –

+0

@Arjan Это не может быть из-за этого, потому что, когда я закрываю его на 100 (что равно 10 мс на фрейм), тоже неточно. – L3n

ответ

1

Прежде всего, я вижу, что вы смешали System.currentTimeMilis() и System.nanoTime() не очень хорошая идея, используйте только один из них. Лучше использовать только System.nanoTime(), так как вы работаете с высокой точностью.

В чем причина вашей проблемы: Thread.sleep() не является достаточно точным. Поэтому вам нужно избегать сна. Измените свой спящий код на это;

lastRender = System.nanoTime(); //change it to nano instead milli 
draw.render(); 
draw.fps++; 

if (FPS_CAP > 0) { 
    while (now - lastRender < (1000000000/FPS_CAP)) 
    { 
     Thread.yield(); 

     //This stops the app from consuming all your CPU. It makes this slightly less accurate, but is worth it. 
     //You can remove this line and it will still work (better), your CPU just climbs on certain OSes. 
     //FYI on some OS's this can cause pretty bad stuttering. 
     try {Thread.sleep(1);} catch(Exception e) {} 

     now = System.nanoTime(); 
    } 
} 

О том, как включить VSYNC, приложение должно быть на весь экран, и вы должны вызвать Toolkit.sync() после каждого рендера.

+0

Ну, я вижу, что нет идеального решения, так что спасибо, сейчас это довольно точно, и спасибо, что методы синхронизации быть полноэкранным. – L3n

+0

также поставлен Thread.sleep (0, 100); так что он становится более точным и до сих пор не потребляет много CPU. – L3n

+0

'Thread.sleep (millis, nanos)' на самом деле проверяет, является ли параметр «nanos» более 500000 и соответственно увеличивает миллиз-парам. Таким образом, вы на самом деле вызываете 'Thread.sleep (0);': D Проверьте исходный код на [Thread.java] (http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/ 6-b14/java/lang/Thread.java # Thread.sleep% 28long% 2Cint% 29) Теперь дело в том, что 'Thread.sleep (int)' - [собственный метод] (http://stackoverflow.com/questions/6101311/что-это-на-родное-ключевое слово-в-Java-для? LQ = 1). Поэтому вызов 'Thread.sleep (0);' не спит, а ждет завершения обычного вызова. – Onur

3

Java недостаточно точен для достижения таких механизмов точности. Все библиотеки рендеринга на самом деле полагаются на уровень C или C++ для управления точностью в реальном времени.

В вашем случае лучшим обходным решением было бы избежать использования Thread.sleep().

Вы можете полагаться на события таймера вместо этого, чтобы запускать обновления во время TimerTask. Ваша игра будет иметь сердцебиение примерно 60 раз в секунду.

Другое решение, если 60fps хорошо для вас, - дождаться VSync перед отображением вашего экрана. Большинство ЖК-экранов составляют 60 или 120 кадров в секунду. VSync должен управляться вашей графической библиотекой (swing, JavaFX или другой).

Последнее решение, вы можете посмотреть в коде специализированного движка (например, JMonkey) в качестве ссылки.

+0

Как подождать VSync? – L3n

+0

Также как бы реализовать таймер, который работает 60 раз в секунду? – L3n

+0

Для таймера: http://www.journaldev.com/1050/java-timer-timertask-example - для VSync это зависит от того, какую графическую библиотеку вы используете, но я нашел эту ссылку: http: // gpsnippets .blogspot.fr/2010/10/how-to-use-bufferstrategy-in-java.html –

1

JavaFX основан на pulse mechanism.

Импульс представляет собой событие, которое указывает на графе сцены JavaFX, что является время, чтобы синхронизировать состояние элементов на графе сцены с Prism. Импульс сжимается со скоростью 60 кадров в секунду (fps) максимум и запускается всякий раз, когда анимация выполняется на графике сцены. Даже , когда анимация не работает, импульс запланирован, когда что-то в изменяется график сцены. Например, если позиция кнопки изменена, запланирован импульс.

Когда импульс запущен, состояние элементов на графике сцены равно , синхронизированное до уровня рендеринга. Импульс позволяет приложению разработчикам асинхронно обрабатывать события. Эта важная функция позволяет системе выполнять пакетные операции и выполнять события на пульсе.

. , ,

Стеклянный оконный инструмент отвечает за выполнение импульса событий. Он использует собственные таймеры с высоким разрешением для выполнения .

Чтобы понять реализацию импульсного механизма, лучше всего изучить исходный код JavaFX. Некоторые ключевые классы:

Timer.java WinTimer.java win/Timer.h win/Timer.cpp

Вышеуказанные ссылки предназначены для общего класса Timer и конкретных реализаций таймера окна. Исходный код JavaFX включает в себя реализации для других платформ, таких как OS X, GTK, iOS, Android и т. Д.

Некоторые реализации (например, OS X, казалось бы) позволяют синхронизировать синхронизацию реализации таймера, другие (такие как кажется, Windows) не позволяют синхронизировать vsync. Хотя эти системы могут усложниться, поэтому, я думаю, возможно, что в некоторых случаях синхронизация vsync может быть достигнута через конвейер аппаратной графики, а не через таймер.

Код на основе Windows основан на вызове timeSetEvent.

По умолчанию частота кадров JavaFX ограничена 60 кадрами в секунду, хотя это adjustable via undocumented properties.

Если вы не используете JavaFX (и, похоже, это не так), вы все равно можете изучить JavaFX source code и узнать о его реализации там, если вы хотите перенести какие-либо из концепций или кода для использования в вашем приложении , Вы также можете запустить механизм таймера JavaFX в приложение, отличное от JavaFX, сделав свой подкласс приложения JavaFX Application class или создав JFXPanel для запуска инструментария JavaFX, а затем выполнив обратный вызов таймера на основе AnimationTimer.

+0

Я не могу использовать недокументированные свойства, он просто не работает, но может быть, потому что он недокументирован правильно? – L3n

+0

Может быть, я думаю, поддержка для них может быть удалена в любое время. Они работали в прошлом для меня. Они специфичны для JavaFX, поэтому они будут работать только в отношении приложения JavaFX. – jewelsea

 Смежные вопросы

  • Нет связанных вопросов^_^