Java 2D программирование игр: разные подходы к созданию игрового цикла

10

Я новичок в программировании игр на Java, но чем больше я читаю, тем больше я запутался, потому что видел несколько разных подходов к созданию игрового цикла: 1. Стандартный подход, использующий класс Timer (кажется, меньше точный). 2. Более точный подход, который использует System.nanoTime. 3. Простой подход, который использует scheduleAtFixedRate.

Какой из них в целом должен быть предпочтительным, и где преимущества / недостатки каждого подхода? Спасибо заранее за любую информацию.

user1812379
источник

Ответы:

7

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

Используя http://docs.oracle.com/javase/1.5.0/docs/api/java/lang/System.html#nanoTime%28%29 , вы можете опросить истекшее время. В принципе...

public static void main(String [ ] args)
{
    long current_frame_time = system.nanoTime();
    long last_frame_time = current_frame_time;

    while(gameIsRunning)
    {
        last_frame_time = current_frame_time;
        current_frame_time = system.nanoTime();

        long timeTaken = current_frame_time - last_frame_time;

        //update and render game here
    }
}

Этот основной метод может быть улучшен, например, http://www.koonsolo.com/news/dewitters-gameloop/ и http://gafferongames.com/game-physics/fix-your-timestep/ .

Кроме того, вы можете создать таймер и установить этот таймер для запуска обновления и рендеринга каждые X миллисекунд. Но у такого геймплея есть некоторые недостатки.

Согласно http://docs.oracle.com/javase/1.5.0/docs/api/java/util/Timer.html : « Каждому объекту Timer соответствует один фоновый поток, который используется для выполнения всех таймеров. задачи, последовательно. Задачи таймера должны завершаться быстро. Если задача таймера занимает слишком много времени, она «захватывает» поток выполнения задачи таймера. Это, в свою очередь, может задержать выполнение последующих задач, которые могут «сгруппироваться» и выполнить в быстрой последовательности, когда (и если) оскорбительная задача, наконец, завершена. "

Согласно http://docs.oracle.com/javase/1.5.0/docs/api/java/util/Timer.html#scheduleAtFixedRate%28java.util.TimerTask,%20java.util.Date,%20long%29 : « При выполнении с фиксированной скоростью каждое выполнение планируется относительно запланированного времени выполнения начального выполнения. Если выполнение задерживается по какой-либо причине (например, при сборке мусора или другой фоновой активности), два или более выполнения будут выполняться в быстрой последовательности «наверстать упущенное. В долгосрочной перспективе частота выполнения будет точно равна обратной величине указанного периода (при условии, что системные часы, лежащие в основе Object.wait (long), точны) ».

Поскольку вы обычно заранее не знаете, сколько времени занимает игровой цикл, установка таймера для выполнения игрового цикла каждые X миллисекунд (в зависимости от целевой частоты кадров) может привести к созданию нескольких сгруппированных кадров, которые будут выполняться всякий раз, когда кадр закончено вместо того, когда кадр запланирован. Когда это происходит ... зачем вообще использовать таймер?

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

Exilyth
источник
Вопрос был об игровых циклах, так что информация о awt / swing не по теме. Однако, похоже, эта информация была актуальной из-за неуместной и запутанной строки в вопросе, поэтому я удалил ее.
Джокинг
Я адаптировал свой ответ, чтобы отразить изменения, внесенные в вопрос.
Exilyth
7

Я бы не ставил основной цикл в таймер. Скорее, я бы сделал цикл while, который обрабатывает каждый кадр, а затем использовал бы какую-то функцию хронометража (звучит так, как в Java, которая будет System.nanoTime), чтобы вычислить, сколько времени прошло с момента последнего кадра / последней итерации петля.

Определенные языки здесь являются исключениями (например, JavaScript, ActionScript), потому что эти языки работают в среде, в которой есть неявный основной цикл (например, браузер, Flash Player), но это исключение не относится к Java.

jhocking
источник