Чи є якась шкода в тому, щоб основний цикл гри був безконтрольним?


19

Мені було цікаво, чи можлива шкода, коли мій цикл гри працює так швидко, як дозволяє система?

На даний момент у мене є цикл, який, вимірюючи пройдений час у наносекундах, без проблем запускає логіку гри та логіку візуалізації з заданими швидкостями. Насправді будь-яка логіка, яку я виконую в циклі, працює на певну кількість викликів щосекунди.

Сам цикл, хоча просто працює так само швидко, як йому подобається, на мою машину доходить приблизно 11,7 мільйонів циклів в секунду.

Цикл (простий псевдокод):

while(!isGameOver){

 if(canPollInputs){
    pollInputs()
 }

 while(canStepLogic){
    stepLogic()
 }

 if(canRender){
    render()
 }
}

Моє питання в основному, якщо цей простий цикл, якщо його не працює з регульованою швидкістю, може завдати шкоди системі?

Редагувати: Це означає, що моя логіка працює 30 разів на секунду (30 секунд в секунду), мій рендер працює зі швидкістю 60 кадрів в секунду, я опитую вводи 100 разів в секунду, а також є певна логіка, щоб впоратися з логікою або візуалізація займає більше часу, ніж очікувалося . Але сама петля не заглушена.

Редагувати: Використовуючи, Thread.sleep()наприклад, для зменшення основного циклу до 250 циклів в секунду призводить до зменшення, але цикл працює зі швидкістю 570 циклів на секунду замість потрібних 250 (додасть код, коли я перебуваю на моїй настільній машині ..)

Редагувати: Ось ми працюємо, робочий ігровий цикл Java для того, щоб уточнити речі. Також не соромтеся ним користуватися, але не претендуйте на своє;)

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 () не можна використовувати для надійного придушення вашого кроку, оскільки ОС не гарантує повернення контролю вашій програмі після потрібної кількості часу. (Він може відрізнятися).
Рой Т.

Так, це проблема з псевдокодом. Я зробив більшу частину того, про що пише гаффер (зараз працюю над реалізацією дельти, яка доповнює мою структуру циклу). Якщо я не можу використовувати Thread.sleep (), що ще є в стандартній Java?
dot_Sp0T

1
Зазвичай ви використовуєте зайнятий цикл з Thread.Sleep (0) і сподіваєтесь на найкраще. Про це багато говорить у кількох статтях.
Рой Т.

Ну, це в основному те, що я робив, тільки без теми.спля (0), І що спочатку спровокувало це питання. Чи є якась шкода у зайнятій (респ. Не зайнятій, навіть якщо ця маленька повторювана логіка знижується) циклом для системи?
dot_Sp0T

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

Відповіді:


35

Це призведе до того, що одне ядро ​​CPU завжди працюватиме на 100%. Зазвичай це не завдає шкоди системі. Процесори розроблені для роботи на 100% протягом години. Але на мобільному пристрої він швидко зарядить акумулятор і нагріє пристрій, що, швидше за все, коштуватиме вам приблизно зірок у ваших рейтингах магазину. На настільному комп’ютері це менше проблем, але воно споживає більше споживача електроенергії, змушує вентилятор процесора крутитися швидше, що може спричинити певний шум і відпрацьовані цикли процесора, які в іншому випадку можуть використовуватися іншими процесами. Хоча це не критичні дефекти, вони все ще є поганим стилем, тому слід уникати їх, коли це можливо.

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

Щоб пом'якшити цю проблему, слід обмежити рамки (графічні та логічні) максимумом того, що може сприймати людське око. Скільки це спірне і залежить від виду анімації та від того, який дисплей відображається, але оцінки коливаються від 40 FPS до 120 FPS. Це означає, що вам потрібно встановити мінімальний час виконання кожного циклу між 20 мс і 8 мс. Якщо ітерація циклу закінчиться швидше, дозвольте потоку процесора sleepдля періоду, що залишився.


Дякую за пояснення Філіп. Я насправді мав на увазі той факт, що я працюю в певний кадр в секунду та в секунду, а також опитую лише певну кількість разів в секунду. Постараємося зробити це зрозумілішим.
dot_Sp0T

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

Імовірно, я також сприйму вашу відповідь, оскільки перший абзац чудово дає відповідь. Чи не заперечуєте ви якось візуально виокремити це з решти відповіді?
dot_Sp0T

Відтворіть на робочому столі непакетований Civilization 2, і ви виявите, що це проблема. Принаймні, я.
знизати

1
На додаток до батареї та вентиляторів, збільшення використання процесора може призвести до надмірного тепла, що може бути незручно (наприклад, ноутбук, esp мій rMBP) і навіть призведе до вимкнення ПК (особливо старші ПК із запиленими кулерами). Ці фактори сильно перебільшують, якщо запустити всі ядра на 100%.
NPSF3000

2

Ви витрачаєте цикли процесора. Це означає менший час заряду батареї на ноутбуках, планшетах та телефонах, більш високі рахунки за електроенергію, більше тепла, виробленого машиною, шумніші вентилятори. Крім того, ви можете їсти цикли від інших важливих системних процесів (наприклад, віконний сервер може стати ривком), що може вплинути на геймплей. Деякі системні планувальники сучасних багатозадачних систем також штрафують додатки, які використовують занадто багато циклів, тож ви, можливо, швидше спочатку, а потім раптом побачите дивну хитрість, коли вас запускає система.

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

Нарешті, є проблеми з геймплеєм, де ви можете надати перевагу гравцям із швидшими машинами над тими, що мають повільніші. Обмеження циклу гри з певною частотою та використання додаткових циклів виключно для неігрових аспектів (наприклад, надання графіки вищої чіткості, об'ємних звукових ефектів чи будь-чого іншого) зробить вашу гру більш справедливою.


Я б сказав, навпаки, люди, які пристрасно будують свої машини, використовують хороші вентилятори, навіть водяне охолодження. Тільки випадки HTPC або міні ITX можуть мати обмеження щодо цієї частини. Інакше бідні, але захоплені люди будуть використовувати коробку вентилятора. що достатньо навіть на 100%, гаряче, але добре.
v.oddou

Я просто повторююсь із досвіду. Якщо ви подивитесь на Star Trek Online, вони насправді мають окремий параметр у своєму графічному інтерфейсі, який буде вставляти сон () s у їх основний цикл, щоб запобігти перегріванню певних машин, тому це не може бути рідкістю, якщо виробник Neverwinter та STO побачив необхідність його додати. Майте на увазі, що пристрасть! = Майстерність. Є багато кваліфікованих і пристрасних майстрів, але є і початківці, і інші, які ще навчаються, і з гідною швидкістю відмови від статистики вони говорять про більшість.
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.