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


19

Мне было интересно, есть ли какой-нибудь возможный вред, когда мой игровой цикл работает так быстро, как позволяет система?

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

Сам цикл, хотя и работает так быстро, как ему хочется, составляет около 11,7 миллионов циклов в секунду на моей машине.

Петля (простой псевдокод):

while(!isGameOver){

 if(canPollInputs){
    pollInputs()
 }

 while(canStepLogic){
    stepLogic()
 }

 if(canRender){
    render()
 }
}

Мой вопрос заключается в том, может ли этот простой цикл, если он не работает на контролируемой скорости, причинить какой-либо вред системе?

Редактировать: это означает, что моя логика работает 30 раз в секунду (30 тактов в секунду), мой рендерер работает со скоростью 60 кадров в секунду, я опрашиваю ввод 100 раз в секунду, и есть также некоторая логика, чтобы справиться с логикой или рендерингом занимает больше времени, чем ожидалось , Но сама петля не задушена.

Редактирование: Использование, Thread.sleep()например, для уменьшения основного цикла до 250 циклов в секунду, приводит к уменьшению, но циклы работают со скоростью около 570 циклов в секунду вместо желаемых 250 (добавит код, когда я на своем настольном компьютере ...)

Редактировать: здесь мы идем, рабочий Java Gameloop, чтобы прояснить вещи. Также не стесняйтесь использовать это, но не претендуйте на это свое;)

private void gameLoop() {

    // Time that must elapse before a new run
    double timePerPoll = 1000000000l / targetPPS;
    double timePerTick = 1000000000l / targetTPS;
    double timePerFrame = 1000000000l / targetFPS;
    int maxFrameSkip = (int) ( (1000000000l / MINIMUM_FPS) / timePerTick);

    int achievedPPS = 0;
    int achievedFPS = 0;
    int achievedTPS = 0;

    long timer = TimeUtils.getMillis();

    int loops = 0;

    int achievedLoops = 0;

    long currTime = 0l;
    long loopTime = 0l;

    long accumulatorPPS = 0l;
    long accumulatorTPS = 0l;
    long accumulatorFPS = 0l;

    long lastTime = TimeUtils.getNano();

    while(!isRequestedToStop) {
        currTime = TimeUtils.getNano();
        loopTime = currTime - lastTime;
        lastTime = currTime;

        loops = 0;

        accumulatorPPS += loopTime;
        accumulatorTPS += loopTime;
        accumulatorFPS += loopTime;

        if(accumulatorPPS >= timePerPoll) {
            pollInputs();
            playerLogic();
            achievedPPS++;
            accumulatorPPS -= timePerPoll;
        }

        while(accumulatorTPS >= timePerTick && loops < maxFrameSkip) {
            tick();
            achievedTPS++;
            accumulatorTPS -= timePerTick;
            loops++;
        }

        // Max 1 render per loop so player movement stays fluent
        if(accumulatorFPS >= timePerFrame) {
            render();
            achievedFPS++;
            accumulatorFPS -= timePerFrame;
        }

        if(TimeUtils.getDeltaMillis(timer) > 1000) {
            timer += 1000;
            logger.debug(achievedTPS + " TPS, " + achievedFPS + " FPS, "
                    + achievedPPS + " Polls, " + achievedLoops + " Loops");
            achievedTPS = 0;
            achievedFPS = 0;
            achievedLoops = 0;
        }

        achievedLoops++;
    }
}

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


2
обязательная ссылка «исправьте ваш временной шаг»: gafferongames.com/game-physics/fix-your-timestep, пожалуйста, прочитайте ее. Также обратите внимание, что Thread.Sleep () не может использоваться для надежного регулирования вашего временного шага, так как ОС не гарантирует возврат управления вашему приложению по истечении запрошенного промежутка времени. (Может варьироваться).
Рой Т.

Да, это проблема с псевдокодом. Я сделал большую часть того, о чем пишет gaffer (в настоящее время работаю над дельта-реализацией, которая дополняет мою структуру цикла). Если я не могу использовать Thread.sleep (), что еще доступно в стандартной Java?
dot_Sp0T

1
Вы обычно используете занятый цикл с Thread.Sleep (0) и надеетесь на лучшее. Гаффер много говорит об этом в нескольких статьях.
Рой Т.

Ну, это в основном то, что я делал, просто без thread.sleep (0), И что изначально спровоцировало этот вопрос. Есть ли какой-либо вред в занятом (или не занятом, поскольку даже эта небольшая повторяющаяся логика замедлена) системе?
dot_Sp0T

Некоторое время назад ошибка заставила Starcraft II сделать это при определенных условиях. В итоге буквально расплавились несколько графических процессоров. Так что да, вполне возможно, что неконтролируемый игровой цикл может причинить вред.
Мейсон Уилер

Ответы:


35

Это приведет к тому, что одно ядро ​​процессора будет всегда работать на 100%. Это обычно не наносит никакого вреда системе. Процессоры предназначены для работы на 100% в течение нескольких часов. Но на мобильном устройстве он быстро разряжает батарею и нагревает устройство, что, вероятно, будет стоить вам около звезд в рейтингах вашего магазина. На настольном компьютере это менее проблемная задача, но она потребляет больше электроэнергии пользователя, заставляет вентилятор ЦП вращаться быстрее, что может вызвать некоторый шум и лишние циклы ЦП, которые в противном случае могли бы использоваться другими процессами. Хотя это не критичные дефекты, они все же плохой стиль, поэтому следует избегать их, когда это возможно.

Вы ничего не сказали о том, как работает ваш игровой логический цикл внутри, но когда вы используете подход с дельта-временем (каждый вычисление, которое вы делаете, требует времени с момента последнего вызова), вы имеете дело с очень маленькой дельтой. значения времени Это означает, что вы можете столкнуться с проблемами с неточностями с плавающей точкой, которые могут вызвать все виды странного поведения. Кроме того, разрешение системного таймера часто ограничено, поэтому, когда ваш логический цикл слишком быстрый, вы можете получить значение delta-t, равное нулю, которое затем может вызвать деление на ноль, что приведет к сбою.

Чтобы смягчить эту проблему, вы должны ограничить частоту кадров (графическую и логическую) до максимума, который может воспринимать человеческий глаз. Сколько это оспаривается и зависит от типа анимации и от того, какой тип отображения она отображается, но оценки варьируются от 40 FPS до 120 FPS. Это означает, что вам нужно установить минимальное время выполнения каждого цикла от 20 мс до 8 мс. Если итерация цикла завершится быстрее, оставьте поток ЦП sleepна оставшееся время.


Спасибо за объяснение Филипп. Я на самом деле имел в виду тот факт, что я бегу с определенными частями кадров в секунду и частотой кадров, а также выполняю опрос только определенное количество раз в секунду. Постараюсь сделать это более понятным, хотя.
dot_Sp0T

@ dot_Sp0T После того, как вы отредактировали свой вопрос, только первый абзац и последнее предложение относятся к вашему делу. Однако я хотел бы сохранить второй и третий абзацы моего ответа, потому что они могут быть полезны для других.
Филипп

Предположительно, я также приму ваш ответ, так как первый параграф великолепно дает ответ. Не могли бы вы как-нибудь визуально отличить его от остального ответа?
dot_Sp0T

Сыграйте в непатентованную Civilization 2 на рабочем столе, и вы обнаружите, что это проблема. По крайней мере, я делаю.
пожимает

1
В дополнение к соображениям, связанным с батареями и вентиляторами, повышенная загрузка ЦП может привести к избыточному нагреву, что может быть неудобно (например, ноутбук, особенно мой rMBP) и даже привести к выключению компьютера (особенно на старых ПК с пыльными кулерами). Эти факторы раздражены, если вы запускаете все ядра на 100%.
NPSF3000

2

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

Многие хардкорные геймеры также создают собственные ПК с нуля, которые находятся на грани спецификаций их фанатов, и в этом случае жаркий день и тепло, которое генерирует ваша игра, могут вызвать срабатывание предохранителей в машине (в лучшем случае) или даже перегреть детали и умереть (в худшем случае). Так что, если это ваша целевая аудитория, вы можете убедиться, что вы всегда оставляете немного места «наверху».

Наконец, есть проблемы с геймплеем, когда вы можете превзойти игроков с более быстрыми машинами по сравнению с более медленными. Ограничение игрового цикла на определенной частоте и использование дополнительных циклов исключительно для не относящихся к игровому процессу аспектов (таких как рендеринг графики с более высокой точностью, эффекты объемного звучания и т. Д.) Сделает вашу игру более справедливой.


Я бы сказал, наоборот, люди, которые страстно собирают свои машины, используют хорошие вентиляторы, даже водяное охлаждение. Только случаи HTPC или mini ITX могут иметь ограничения в этой части. В противном случае, бедные, но увлеченные люди будут использовать коробку Ventirad. что достаточно даже на 100%, горячо, но хорошо.
v.oddou

Я просто повторяю из опыта. Если вы посмотрите на Star Trek Online, у них на самом деле есть отдельный параметр в их графическом интерфейсе, который будет вставлять sleep () в их основной цикл, чтобы предотвратить перегрев определенных машин, так что это не может быть редкостью, если создатель Neverwinter и СТО увидел необходимость его добавить. Имейте в виду, что страсть! = Мастерство. Есть много опытных и увлеченных тинкеров, но есть и новички, и другие, которые еще учатся, и с приличным уровнем отсева, по статистике, они составляют большинство.
uliwitness

Благодаря этим комментариям я перечитал ваш ответ. Вы перечисляете действительные точки, но, к сожалению, на самом деле не решаете вопрос с более быстрыми машинами против медленных машин . Логика цикла ( так называемый gameloop в своем третьем абзаце) ограничен. Вопрос заключался в том, чтобы перекрыть магистральный цикл, который просто переоценивает каждый прогон, если входной сигнал должен быть извлечен, вычислена логика или должен быть отрисован кадр - так что нет более быстрой логики, если ваша машина ужасно хромает
dot_Sp0T

1

У вас не должно быть нервного движения / геймплея

Есть два способа реализовать игровую логику - привязать к реальному времени или привязать к количеству «ходов» / этапов обработки. Если какая-то вещь в вашей игре движется влево, будет ли она другой, если ваш stepLogic () был вызван 100 вместо 50 раз?

Если ваш код обрабатывает истекшее время явно везде и делает это правильно, то это может быть нормально; но если в вашем коде есть что-то, что зависит от количества «шагов», то вы получите нежелательные побочные эффекты.

Во-первых, скорость вещей, которые должны постоянно перемещаться, будет непредсказуемо изменяться (в зависимости от использования процессора), и это приводит к очень раздражающему контролю - вы не можете сделать точный удар или прыжок, если вдруг игра ускоряется или замедляется. Даже для игр без «подергивания» это выглядит раздражающим и нервным, если все идет не так гладко.

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

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


1
Как бы мне ни нравился ваш ответ, он не имеет отношения к вопросу, так как я не спрашиваю о нервном движении / игровом процессе, а о том, какой вред неуправляемый цикл (который не контролирует какую-либо логику, рендеринг или что-либо еще) может причинить системе
dot_Sp0T

1

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

Раньше было совершенно нормально просто рендерить игровые кадры как можно быстрее, иногда даже без таймера, чтобы компенсировать игровой процесс для разных скоростей процессора. Гоночная игра Bullfrog Hi-Octane особенно сильно пострадала от этого, и я подозреваю, что именно об этом и упоминает плакат, упоминающий Civ2.

Я бы посоветовал вам хотя бы опрашивать входные данные на каждом проходе, если вы работаете в системе Windows или аналогичной, чтобы обеспечить быстрый отклик на ввод.


Это не вопрос таймера, это вопрос хронометра, который я бы сказал, или счетчик производительности. Нужно получить реальное время в какой-то момент, и бесплатный цикл работает отлично. Однако таймер (в смысле регулярного прерывания) пытается регулировать скорость цикла в общем использовании. Я не считаю это хорошим. Предпочтительно использовать swap/ present/ flipфункцию сделать ожидание на Vsync сигнала, регулировать цикл игры.
v.oddou
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.