Ограничение FPS для моей игры

Хорошо, я делаю небольшую игру, и мне нужно ограничить свой FPS, потому что, когда я играю на своем очень быстром компьютере, у меня около 850 FPS, и игра будет идти ОЧЕНЬ быстро, и когда я переключаюсь на мой старый компьютер работает намного медленнее, поэтому мне нужно будет ограничить свой FPS, чтобы сделать это правильно. Как ограничить FPS? Мой основной игровой цикл:

    public void startGame(){
    initialize();
    while(true){
        drawScreen();
        drawBuffer();
        plyMove();

        //FPS counter
        now=System.currentTimeMillis(); 
        framesCount++; 
        if(now-framesTimer>1000){
            framesTimer=now; 
            framesCountAvg=framesCount; 
            framesCount=0; 
        }

        try{
            Thread.sleep(14);
        }catch(Exception ex){}
    }
}

Как я рисую экран и рисую все остальные вещи, игроков, мяч и т. д. Кстати, игра представляет собой ремейк понга.

    public void drawBuffer(){
    Graphics2D g = buffer.createGraphics();
    g.setColor(Color.BLACK);
    g.fillRect(0,0,600,500);
    g.setColor(Color.GREEN);
    g.fillRect(ply1.getX(),ply1.getY(),ply1.getWidth(),ply1.getHeight());
    g.setColor(Color.RED);
    g.fillRect(ply2.getX(),ply2.getY(),ply2.getWidth(),ply2.getHeight());
    g.setColor(Color.WHITE);
    g.fillOval(ball1.getX(),ball1.getY(),ball1.getWidth(),ball1.getHeight());
    g.drawString("" + framesCountAvg,10,10);
}

public void drawScreen(){
    Graphics2D g = (Graphics2D)this.getGraphics();
    g.drawImage(buffer,0,0,this);
    Toolkit.getDefaultToolkit().sync();
    g.dispose();
}

person Stan    schedule 02.06.2011    source источник
comment
Ну, если скорость вашей игры зависит от количества кадров в секунду, то весь дизайн довольно... плох.   -  person Dr McKay    schedule 02.06.2011
comment
Вместо ограничения FPS вы также можете сделать так, чтобы ваш код игры действительно учитывал прошедшее время (например, двигайтесь меньше, если у вас высокий FPS, и больше, если FPS низкий). Это можно получить, взяв разницу System.nanoTime() между фактическим кадром и предыдущим кадром.   -  person Howard    schedule 02.06.2011
comment
Является ли это дубликатом stackoverflow.com/questions /3102888/ ?   -  person Michael McGowan    schedule 02.06.2011
comment
Я посмотрел на это, но это не совсем решило мою проблему, и я понятия не имею, как заставить это работать.   -  person Stan    schedule 02.06.2011


Ответы (6)


Похоже, ваш дисплей привязан к вашему игровому движку. Убедитесь, что они отключены. Вы хотите, чтобы игра работала с одинаковой скоростью независимо от частоты кадров. Если ваш быстрый компьютер перерисовывает экран быстрее, это нормально, если движок заставляет игру играть с постоянной скоростью.

person jzd    schedule 02.06.2011
comment
Я немного озадачен тем, как это сделать. Создал основной игровой цикл, выложу в ОП. - person Stan; 02.06.2011
comment
@Stan, ваш основной игровой цикл будет выполнять движение / изменения в зависимости от времени, а не цикла. - person jzd; 02.06.2011
comment
Как я сделаю это на основе цикла? Если я удалю Thread.sleep, частота кадров составит около 850 кадров в секунду, а я этого не хочу. - person Stan; 02.06.2011
comment
Он основан на цикле. Вместо этого вносите изменения в зависимости от времени. Так что ваш игровой движок вычисляет вещи быстрее/медленнее, чем ваша картина. - person jzd; 02.06.2011
comment
Более того, вы, вероятно, захотите, чтобы рендеринг происходил в другом потоке, чем обработка игры. Обработка игры создает контент для рендеринга, а затем механизм рендеринга может выбрать, сколько или мало данных для рендеринга. - person Stefan Kendall; 03.06.2011
comment
Как реализовать то, что описывает jzd: временной шаг - person Prof. Falken; 24.10.2012

Вместо того, чтобы ограничивать свой FPS, сделайте так, чтобы игра не шла очень быстро, когда FPS высок.

Предположительно, у вас есть код, который делает определенные вещи в каждом кадре, например. перемещает персонажа вперед, если нажата кнопка. Вместо этого вы хотите переместить персонажа вперед на величину, зависящую от количества времени, прошедшего с момента предыдущего кадра.

person τεκ    schedule 02.06.2011

Как упоминалось выше, вам нужно отделить цикл отображения от цикла обновления где-то в основе вашей игры, вероятно, что-то вроде этого

while (1)
{
    drawscene();
    update();
}

и в обновлении вы продвигаете время на фиксированную величину, например, 0,17 сек. Если вы рисуете со скоростью ровно 60 кадров в секунду, то анимация выполняется в реальном времени, при 850 кадрах в секунду все будет ускорено в 14 раз (0,17 * 850), чтобы предотвратить это, вы должны сделать свое обновление зависимым от времени. например

elapsed = 0;
while (1)
{
   start = time();
   update(elapsed); 
   drawscene();
   sleep(sleeptime);
   end = time();

   elapsed = end - start;
}
person Harald Scheirich    schedule 02.06.2011
comment
Это крайне неэффективно. Обновление должно происходить асинхронно с рендерингом. - person Stefan Kendall; 03.06.2011

Описание того, что я имел в виду в своем комментарии. Внутри вашего метода plyMove(); я подозреваю, что есть что-то вроде

plyMove() {
    ply1.x += amountX;
    ply1.y += amountY;
}

Вместо этого сделайте движение в зависимости от прошедшего времени

plyMove(double timeElapsed) {
    ply1.x += amountX * timeElapsed;
    ply1.y += amountY * timeElapsed;
}

где вы вычисляете timeElapsed из разницы во времени внутри вашего основного цикла и соответствующим образом масштабируете его, чтобы получить приличное движение.

double timePrev = System.currentTimeMillis();
while(true) {
    double timeNow = System.currentTimeMillis();
    double elapsed = timeNow - timePrev;

    drawScreen();
    drawBuffer();
    plyMove(0.001 * elapsed);

    timePrev = timeNow;
}
person Howard    schedule 02.06.2011

Если вам действительно нужна игра с фиксированным таймфреймом, вы можете сделать это:

float timer = 0;
float prevTime= System.currentTimeMillis();
while(true)
{
    draw();

    float currentTime = System.currentTimeMillis();
    timer += currentTime - prevTime;

    while (timer > UPDATE_TIME) // To make sure that it still works properly when drawing takes too long
    {
        timer -= UPDATE_TIME;
        update();
    }
}

Где UPDATE_TIME — это интервал, с которым вы хотите обновлять в миллисекундах.

person CptRed    schedule 04.06.2011

Одним из способов будет использование

try {
    Thread.sleep(20);
} catch(InterruptedException e) {}

в конце основного цикла. Отрегулируйте номер в соответствии с вашими потребностями.

person Lanbo    schedule 02.06.2011
comment
Это замедлит игру на обеих машинах. Это не то, чего хочет ОП. - person jzd; 02.06.2011
comment
Что ж, это исправило скорость игры, она составляет 60 кадров в секунду, но не будет ли она работать медленнее на моем другом, более медленном компьютере? - person Stan; 02.06.2011