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

그것은 60 FPS에서 FPS 상한해야 대신는 결과가됩니다 : 당신이 그것을 볼 수 있듯이 이 enter image description here

정말

는 내가 가지고있는 코드입니다 정확하지 않은, 때로는 60보다 훨씬 낮고 때로는 조금 더 높습니다! 그리고 때로는 60에 도달하지 못하는 문제는 PC 하드웨어가 아닌데 1700-2000 FPS를 얻을 때부터입니다. 가능한 한 정확하도록하고 싶습니다.

미리 감사드립니다.

+1

흠 이것은'System.currentTimeMillis'가 항상 정확하지 않다는 사실 때문일 수 있습니까? ms 단위의 시간이지만 해상도는 OS에 따라 다르며 10 또는 15ms 단위로 증가 할 수 있습니다. 60fps에서 프레임 당 약 16.7ms 가량 차이가 날 수 있습니다. – Arjan

+1

Java는 이러한 정밀 메커니즘을 구현하기에 충분하지 않습니다. 모든 렌더링 라이브러리는 실제로 실시간 정밀도를 관리하기 위해 C 또는 C++ 계층에 의존합니다. –

+0

@Arjan 왜냐하면 100 캡 (프레임 당 10ms)으로 모자를 씌우면 정확하지 않기 때문입니다. – L3n

답변

1

우선 나는 당신이 좋은 아이디어가 아닌 System.currentTimeMilis()System.nanoTime()을 섞은 것을 보았습니다. 둘 중 하나만 사용하십시오. 고정밀 도로 작업하기 때문에 System.nanoTime() 만 사용하십시오.

문제가 발생한 이유는 Thread.sleep()은 정확하지 않습니다. 그래서 자고 싶지 않아야합니다. 수면 코드를 이것으로 변경하십시오. VSYNC을 사용하는 방법에 대한

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

는 응용 프로그램이 전체 화면으로해야하고 모든 렌더링 후 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/what-is-the-native-keyword-in-java-for? lq = 1). 그래서'Thread.sleep (0);을 호출하는 것은 자고 있지 않지만 네이티브 호출이 완료되기를 기다린다. – Onur

3

Java는 이러한 정밀 메커니즘을 달성하기에 충분하지 않습니다. 모든 렌더링 라이브러리는 실제로 실시간 정밀도를 관리하기 위해 C 또는 C++ 계층에 의존합니다.

최상의 해결 방법은 Thread.sleep()을 사용하지 않는 것입니다.

대신 타이머 이벤트를 사용하여 TimerTask 중에 업데이트를 실행할 수 있습니다. 게임은 초당 약 60 회 하트 비트가 발생합니다.

60fps가 좋은 경우 다른 해결책은 화면을 렌더링하기 전에 VSync를 기다리는 것입니다. 대부분의 LCD 화면은 60 또는 120fps입니다. 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을 기반으로합니다.

펄스는 프리즘으로 장면 그래프 의 소자의 상태를 동기화 할 시간이라는 자바 FX 장면 그래프를 나타내는 이벤트이다. 펄스는 초당 60 프레임 (fps)으로 조절되며 최대 이며 애니메이션이 장면 그래프에서 실행될 때마다 발생합니다. 심지어 애니메이션이 실행되고 있지 않을 때, 의 무언가가 씬 그래프가 변경되면 펄스가 스케줄됩니다. 예를 들어 버튼의 위치가 으로 변경되면 펄스가 예약됩니다.

펄스가 발생하면 장면 그래프의 요소 상태는 렌더링 레이어와 동기화 된 입니다. 펄스를 사용하면 응용 프로그램 개발자가 이벤트를 비동기 적으로 처리 할 수 ​​있습니다. 이 중요한 기능을 사용하면 시스템에서 펄스를 일괄 처리하고 이벤트를 실행할 수 있습니다.

. . .

Glass Windowing Toolkit은 펄스 이벤트를 실행합니다. 고해상도 원시 타이머를 사용하여 을 실행합니다.

펄스 메커니즘의 구현을 이해하려면 JavaFX 소스 코드를 연구하는 것이 가장 좋습니다. 몇 가지 주요 클래스는 다음과 같습니다 win/Timer.h win/Timer.cpp

위의 링크

Timer.java WinTimer.java 는 일반 타이머 클래스와 윈도우 특정 타이머 구현을위한 것입니다. JavaFX 소스 코드에는 OS X, GTK, iOS, Android 등과 같은 다른 플랫폼을위한 구현이 포함됩니다.

일부 구현 (OS X와 ​​같음)은 Timer 구현의 vsync 동기화를 허용합니다. Windows처럼) vsync 동기화를 허용하지 않습니다. 그러나 이러한 시스템은 복잡해질 수 있으므로 어떤 경우에는 vsync 동기화가 타이머가 아닌 하드웨어 그래픽 파이프 라인을 통해 이루어질 수 있습니다.

Windows 고유 코드는 timeSetEvent 호출을 기반으로합니다.

기본적으로 JavaFX 프레임 속도는 60fps로 제한되지만, adjustable via undocumented properties입니다.

JavaFX를 사용하지 않고있는 것처럼 보이지 않는 경우에도 여전히 JavaFX source code을 검사하여 응용 프로그램에서 사용할 개념이나 코드를 이식하려는 경우에 해당 구현을 알아낼 수 있습니다 . 또한 응용 프로그램을 JavaFX 응용 프로그램 클래스의 하위 클래스로 만들거나 JFXPanel을 만들어 JavaFX 툴킷을 시작한 다음 AnimationTimer을 기반으로 타이머 콜백을 구현하여 JavaFX 타이머 메커니즘을 비 JavaFX 응용 프로그램에 구울 수도 있습니다.

+0

문서화되지 않은 속성을 사용할 수 없습니다. 제대로 작동하지 않지만 문서화되지 않았기 때문일 수 있습니다. – L3n

+0

언제든지 삭제할 수 있습니다. 그들은 나를 위해 과거에 일해 왔습니다. 그것들은 JavaFX에만 해당되므로 JavaFX 응용 프로그램과 관련하여 만 작동합니다. – jewelsea

관련 문제