나는 최근에 게임 프로그래밍에 들어갔다. 나는 자바에 대해 꽤 경험이 있지만 게임 프로그래밍에는 경험이 없다. 나는 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()
일 때마다 이동하는 경우 - 메서드가 지연되면 그 계산은 단순히 꺼져 있고 문자가 지연 될 수 있습니까? 웹 사이트에
제 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);
}
응답 해 주셔서 감사합니다. 나에게 혼란스러운 점은 deWITTER가 게임 루프에서 델타 (또는 기본적으로 델타에서 계산 한 보간)를 렌더링 기능에만 전달하지만 게임 로직을 담당하는 업데이트 기능은 전달하지 않는다는 것입니다. 그래서 두 함수 모두 델타 값을 어떤 방식으로 받아야한다고 말하고 있습니까? – BlackWolf
델타는 업데이트 방법에서만 필요합니다. 게임 세계를 수정하는 곳이기 때문입니다. render 메서드는 [game world model] (http://stackoverflow.com/tags/model/info) 만 읽는 것으로 충분합니다. 그것은 세계를 대표하지만, 국가를 변화시키지 않아야한다. 그래서 render 메소드의 델타는 불필요한 것처럼 보입니다. – Lucius
글쎄요, 그렇습니다. 그런 식으로 구현할 것입니다. 나에게도 더 의미가 있습니다. sidenote로서 델타를 렌더링에 전달하는 것이 여전히 유용 할 수 있다고 생각합니다. 두 개의 게임 업데이트간에 이동을 보간하는 것이 가능합니다. 그렇지 않으면 초당 25 회의 게임 업데이트가 있지만 초당 200 회의 렌더링 업데이트가 있다면 175 렌더링은 기본적으로 낭비됩니다. 당신이 말했듯이, 이것은 자동차가 장애물을 통해 "통과"한다는 것을 의미 할 수도 있지만 업데이트 방법이 충분히 자주 호출되어 이러한 일이 일어나지 않거나 눈치 채지 못할 가능성이 있습니다. – BlackWolf