Основной игровой цикл Java

Я пишу цикл игры, я нашел код в приведенном ниже примере здесь. Я также рассмотрел другие способы создания игрового цикла, например, из этой статьи. Я не мог заставить кого-нибудь из них работать. Таким образом, я сохранил это с первой ссылкой.

Что я хотел бы знать:

  • Является ли способ, которым я написал свой игровой цикл, хороший способ сделать это?
    • Какие-либо предложения?
  • Должен ли я использовать Thread.sleep(); в моей петле игры?

Вот мой текущий код:

public void run(){
    long lastLoopTime = System.nanoTime();
    final int TARGET_FPS = 60;
    final long OPTIMAL_TIME = 1000000000 / TARGET_FPS;
    long lastFpsTime = 0;
    while(true){
        long now = System.nanoTime();
        long updateLength = now - lastLoopTime;
        lastLoopTime = now;
        double delta = updateLength / ((double)OPTIMAL_TIME);

        lastFpsTime += updateLength;
        if(lastFpsTime >= 1000000000){
            lastFpsTime = 0;
        }

        this.updateGame(delta);

        this.repaint();

        try{
            Room.gameTime = (lastLoopTime - System.nanoTime() + OPTIMAL_TIME) / 1000000;
            System.out.println(Room.gameTime);
            Thread.sleep(Room.gameTime);
        }catch(Exception e){
        }
    }

Ответ 1

В конце концов вы захотите перейти к чему-то вроде LWJGL, но позвольте мне подчеркнуть, продолжайте делать то, что вы делаете сейчас. Это научит вас основам.

Хорошая работа в вашей петле. Выглядит неплохо, позвольте мне предложить несколько указателей:

  • Перекраска не будет отображать экран немедленно. Он сообщает рендерингу RepaintManager, когда он готов. Вместо этого используйте invalidate paintImmediately. paintImmediately блокирует выполнение до тех пор, пока компонент не будет перерисован, чтобы вы могли измерить время рендеринга.

  • Обычно Thread.sleep имеет дрейф в несколько миллисекунд. Вы должны использовать его, чтобы ваш цикл не использовал слишком много CPU, но убедитесь, что вы понимаете, что если вы спите 10 миллисекунд, вы можете спать 5 миллисекунд или вы можете спать 20.

  • И, наконец:

    double delta = updateLength / ((double)OPTIMAL_TIME);
    

    Если updateLength меньше OPTIMAL_TIME, не вызывайте обновление. Другими словами, если дельта меньше единицы, не обновляйте. Этот учебник объясняет, почему лучше, чем я когда-либо мог.

Ответ 2

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

В конечном итоге вы захотите перейти на LWJGL или какой-либо другой API-интерфейс java, но на данный момент изучите основы работы игровых циклов и то, что лучше всего подходит вашим потребностям.

  • Во-первых, в ответ на один из ваших вопросов нет. Вы лучше избегаете

    Thread.sleep()

    это может отклониться от реального количества времени, которое вы установили для сна.
    например, если вы установите его для сна в течение 10 миллисекунд, он может спать в программе в течение 5-20 миллисекунд.

  • Вторая проблема, которую я сразу вижу, заключается в том, что у вас нет способа остановить цикл игры для пользовательского метода stop(). Пытаться

    boolean running = true;
    во время бега) {
    // Ваш код здесь //
    }

  • В-третьих, вы можете подумать о том, как изменить свою дельту. Способ в приведенном ниже коде может быть лучше использован и построен для вас.

    Это пример моей игровой циклы, которую я использую в своих программах:

    @Override
    public void run() {
    
    long initialTime = System.nanoTime();
    final double timeU = 1000000000 / UPS;
    final double timeF = 1000000000 / FPS;
    double deltaU = 0, deltaF = 0;
    int frames = 0, ticks = 0;
    long timer = System.currentTimeMillis();
    
        while (running) {
    
            long currentTime = System.nanoTime();
            deltaU += (currentTime - initialTime) / timeU;
            deltaF += (currentTime - initialTime) / timeF;
            initialTime = currentTime;
    
            if (deltaU >= 1) {
                getInput();
                update();
                ticks++;
                deltaU--;
            }
    
            if (deltaF >= 1) {
                render();
                frames++;
                deltaF--;
            }
    
            if (System.currentTimeMillis() - timer > 1000) {
                if (RENDER_TIME) {
                    System.out.println(String.format("UPS: %s, FPS: %s", ticks, frames));
                }
                frames = 0;
                ticks = 0;
                timer += 1000;
            }
        }
    }