2013-02-26 2 views
6

나는 최근에 게임 프로그래밍에 들어갔다. 나는 자바에 대해 꽤 경험이 있지만 게임 프로그래밍에는 경험이 없다. 나는 http://www.koonsolo.com/news/dewitters-gameloop/를 읽고 다음 코드로이 제안 게임 루프 구현 : 루프가 유망 쉽게보고"deWiTTERS Game Loop"는 일정한 UPS를 가정합니까?

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

,하지만 지금은 실제로 난 더 이상 그렇게 확실하지 않다 그것으로 뭔가를 시도하고있다. 내가 완전히 오인하지 않은 경우 updateGame()는 위치 계산, 적 이동, 충돌 계산 등과 같은 일을 처리합니다 ...? 보간은 updateGame()으로 전달되지 않으므로 updateGame()이 초당 정확히 UPDATES_PER_SECOND 번 호출된다고 가정합니다. 그것은 우리의 모든 계산이 그 가정에 기초한다는 것을 의미합니까? 어떤 이유로 든 updateGame() 호출이 지연되면 우리에게 많은 문제가 발생하지 않을까요?
예를 들어, 내 캐릭터 스프라이트가 오른쪽으로 걷기로되어 있고 그 속도가 updateGame() 일 때마다 이동하는 경우 - 메서드가 지연되면 그 계산은 단순히 꺼져 있고 문자가 지연 될 수 있습니까? 웹 사이트에

는 보간에 대한 다음 예를 진술한다 : (600)가 위치를 gametick

제 10의하면은 위치 500 gametick하고, 속도는 11 년 후, 100이다. 그러면 자동차를 렌더링 할 때 자동차를 어디에 두겠습니까? 당신은 마지막 배우자의 위치 (이 경우 500)를 취할 수 있습니다. , 자동차는 다음 나는 그냥 예를 들어 알고 위치 (530)

에서 렌더링됩니다
view_position = position + (speed * interpolation)
:하지만 더 좋은 방법은 자동차가 정확한 10.3에있을 것입니다 어디에 예측하고,이 같은 일이 그러나 doesn''t는 차 속도를 UPDATES_PER_SECOND에 의존하게한다? 그래서 더 많은 UPS가 더 빠른 차를 의미할까요? 이건 옳지 않아 ...?

감사의 말은 무엇이든 도와주세요. (! 감사) - (캐릭터 스프라이트를 이동) 지금까지 꽤 잘 작동하지만, 이제 정말 복잡한 게임 물건을 기다릴 수 있도록

UPDATE 모든 도움 후에/솔루션

, 여기에 내가 현재 사용하고 무엇인가 이것을 결정하는 것. 아직도, 나는 이것을 공유하기를 원했다.
내 게임 루프는 이제 다음과 같습니다

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

당신이 볼 수 있듯이, 몇 가지 변경 :

  • delta = UPDATE_INTERVAL? 예. 지금까지는 실험적 이었지만 작동 할 것이라고 생각합니다. 문제는 실제로 두 개의 타임 스탬프에서 델타를 계산하자 마자 부동 계산 오류가 발생한다는 것입니다. 그것들은 작지만 업데이트를 수백만 번 호출한다고 생각하면 합쳐집니다. 그리고 두 번째 while 루프는 누락 된 업데이트를 따라 잡을 수 있기 때문에 (예를 들어 렌더링이 오래 걸릴 경우를 대비하여) 초당 25 건의 업데이트를 얻을 수 있습니다. 최악의 경우 : 업데이트가 MAX_FRAMESKIP 번 이상 발생합니다.이 경우 업데이트가 손실되어 게임이 지연됩니다. 아직도, 내가 말했듯이, 실험적. 이것을 실제 델타로 다시 변경할 수 있습니다.
  • predictes 다음 게임 상태? 예. GameState는 모든 관련 게임 정보를 보유한 객체이며 렌더러는이 객체를 전달 받아 게임을 화면에 렌더링합니다.필자는 렌더러에게 두 가지 상태를 부여하기로 결정했다 : 현재 게임 상태와 예측 된 미래 상태 인 UPDATE_INTERVAL을 정상적으로 통과시킬 상태. 이렇게하면 렌더러는 보간 값을 사용하여 둘 사이를 쉽게 보간 할 수 있습니다. 미래의 게임 상태를 계산하는 것은 실제로 매우 쉽습니다. 업데이트 방법 (createGameState())이 델타 값을 취하고 있기 때문에 델타 값을 UPDATE_INTERVAL만큼 늘리면 미래 상태가 예측됩니다. 물론 미래 상태는 사용자 입력 등이 동일하게 유지된다고 가정합니다. 그렇지 않으면 다음 게임 상태 업데이트가 변경 사항을 처리합니다.
  • 나머지는 거의 동일하게 유지되며 deWiTTERS 게임 루프에서 가져옵니다. MAX_FRAMESKIP은 하드웨어가 실제로 느린 경우에 대비하여 우리가 수시로 무언가를 렌더링하는 것을 확실히하기 위해 꽤 많은 안전 장치입니다. 그러나 이것이 시작되면 우리는 어쨌든 극단적 인 지체를 가질 것입니다. 보간법은 이전과 동일하지만 렌더러는 두 개의 gamestate를 간단히 보간 할 수 있습니다. 보간하는 숫자를 제외하고는 어떤 로직도 가질 필요가 없습니다. 멋지다!

아마도 명확한 예가 될 수 있습니다. 게임 로직을 기반으로하지 않습니다 ... 윈도우의 페인트 방법, 다음 ...

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 
} 

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

답변

2

: 여기에 내가 문자 위치 (조금 단순화)를 계산하는 방법이다 업데이트 간격이 일정하다는 가정하에 두 업데이트 사이를 지나는 시간을 정확하게 측정하는 게임 시계를 포함하십시오. 그런 다음 지연에 대한 모든 계산을 기반으로 할 수 있으며 실제 업데이트 속도에 대해 걱정할 필요가 없습니다. 이 경우

는 자동차의 속도는/단위 초 주어질 것이며, 델타는 마지막 업데이트 이후 전체 초 다음과 같습니다

car.position += car.velocity * delta; 

그것은 그리기에서 게임 로직을 업데이트 분리하는 것이 일반적이다 틀. 당신이 말했듯이, 이것은 프레임을 매번 렌더링하는 것을 건너 뛰어 안정된 업데이트 속도를 유지하는 것을 가능하게합니다.

그러나 업데이트 간격을 일정하게 유지하는 것은 그리 중요하지 않습니다. 시간 단위당 업데이트 수는 이어야합니다.. 장애물을 향해 빠르게 움직이는 차량을 상상해보십시오. 업데이트 빈도가 너무 낮 으면 두 업데이트 간의 이동 거리가 장애물 총 크기보다 커질 수 있습니다. 차량이 바로 그곳을 지나갈 것입니다.

+0

응답 해 주셔서 감사합니다. 나에게 혼란스러운 점은 deWITTER가 게임 루프에서 델타 (또는 기본적으로 델타에서 계산 한 보간)를 렌더링 기능에만 전달하지만 게임 로직을 담당하는 업데이트 기능은 전달하지 않는다는 것입니다. 그래서 두 함수 모두 델타 값을 어떤 방식으로 받아야한다고 말하고 있습니까? – BlackWolf

+0

델타는 업데이트 방법에서만 필요합니다. 게임 세계를 수정하는 곳이기 때문입니다. render 메서드는 [game world model] (http://stackoverflow.com/tags/model/info) 만 읽는 것으로 충분합니다. 그것은 세계를 대표하지만, 국가를 변화시키지 않아야한다. 그래서 render 메소드의 델타는 불필요한 것처럼 보입니다. – Lucius

+1

글쎄요, 그렇습니다. 그런 식으로 구현할 것입니다. 나에게도 더 의미가 있습니다. sidenote로서 델타를 렌더링에 전달하는 것이 여전히 유용 할 수 있다고 생각합니다. 두 개의 게임 업데이트간에 이동을 보간하는 것이 가능합니다. 그렇지 않으면 초당 25 회의 게임 업데이트가 있지만 초당 200 회의 렌더링 업데이트가 있다면 175 렌더링은 기본적으로 낭비됩니다. 당신이 말했듯이, 이것은 자동차가 장애물을 통해 "통과"한다는 것을 의미 할 수도 있지만 업데이트 방법이 충분히 자주 호출되어 이러한 일이 일어나지 않거나 눈치 채지 못할 가능성이 있습니다. – BlackWolf

관련 문제