Программирование игр, создание игрового движка, OpenGL, DirectX, физика, форум
GameDev.ru / Программирование / Форум / Game Loop, Главный игровой цикл: рассуждения и непонятности.

Game Loop, Главный игровой цикл: рассуждения и непонятности.

Страницы: 1 2 Следующая »
VhodnoyloginПостоялецwww3 мая 20150:48#0
Здравствуйте.
Главный цикл игры или Game Loop - это основа, фундамент игры. Без него не может быть и игры.
Поэтому его создание - это необходимый этап написания игры, а правильное создание цикла - это еще и полезный этап.

Чтобы написать хороший ГЛ, начал читать по этой теме. Прочитав некоторый материал, я пришел к непониманию некоторых моментов. Их я бы и хотел обсудить.
Вот основные источники (http://habrahabr.ru/post/136878/) и (http://live13.livejournal.com/469940.html). Можно заметить, что материал в них похож, возможно даже, что их писал один человек.

И вот, какие непонятки у меня появились.
___________________________________________
Рассуждение 1. Предсказания.
Особенность такой организации заключается в том, что вызов рендера происходит "между" вызовами обновления. И, чтобы картинка была плавной, мы применяем интерполяцию и предсказания.
Пример с пулей, что указан в источнике. Сказано, что надо "предсказывать" положение объекта и вычислять его положение в тик+интерполяция. НО!
То, что мне кажется непонятным, странный и даже неприемлемым:
1) для того, чтобы сделать предсказание, надо высчитать положение пули в какой-то промежуточный момент времени... Высчитать положение.. Высчитать...
Стойте, высчитать - это же прерогатива функции update(). То есть мы помещаем в рендер какую-то логику. Во-первых, мы дублируем логику, а дублирование какой-то подпрограммы - это плохо... Ну ладно, дублирования легко избежать...
Но, во-вторых. Мы теряем тогда различия между Апдейтом (логикой) и рендером (отрисовкой) - мне кажется это неправильным.
2) чтобы провести интерполяцию, нужно, чтобы этот процесс был возможен. В приведенном примере с пулей интерполяция всего лишь формула пути. Но в общем смысле мы должны изменять состояние. А это - процесс сложный, а может даже и невозможный для какого-то момента.
3) в примере используется предсказание только одной пули, а если объектов - 1000?.. И каждому надо сделать свое предсказание?.. Получается, что в рендере мы делаем еще один Апдейт. А это итак плохо (см. п. 1), так мы еще и дополнительный тяжелый апдейт делаем.
А смысл тогда предсказания, если можно сделать некий апдейт и тут же начать отрисовывать?.. То же самое, что и внутренний апдейт, названный предсказанием?..

Вывод: предсказание странная штука. И не нужная. Идеал: на один апдейт - один вызов рендера. Рендер же должен отрисовывать всегда статичное состояние. От вообще должен быть тупой - он не должен ничего соображать, только рисовать то, что ему сказано.


Рассуждение 2. Update(delta).
Как можно заметить, везде в статьях пишется только о update() - без параметров. Единственный пример, где есть параметр - и там говорится о "взрыве" вычислений.
А я вообще не понимаю, как апдейт может жить без дельты. Ибо обновление - это состояние + пересчет его изменения на момент (state_time+delta).
Тут непонятны сразу две вещи:
1) откуда взяться взрыву?
2) почему апдейты живут без дельты?
По первому: почему это должен произойти "взрыв"? Ведь мы же не в будущее заглядываем? То есть частый вызов функции update(delta) должен привести к более точному просчету (без учета набегающей ошибки) - и только.
В общем (представляя, что ошибки представления данных нет) получаем, что единственной проблемой вызова апдейта с дельтой - это его бессмысленность: если рендер не успеет отрисовать состояние игры до вызова следующего апдейта, то это состояние просто пропадет. И можно было бы сразу вызывать update(delta1 + delta2).

Вывод: совершенно неясно, почему везде пишутся апдейты без времени. Лично я вообще не вижу работу программы (в частности функции апдейт) без передачи туда дельты.
___________________________________________

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

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

BishopПостоялецwww3 мая 20151:38#1
Ух... тема довольно обширная, а писать много мне не охота. Т.к. сам занимался этими вопросами недавно, могу проконсультировать, но только по скайпу. Если будет желание - пиши в ПМ.
FordPerfectПостоялецwww3 мая 20151:43#2
>Можно заметить, что материал в них похож, возможно даже, что их писал один человек.
Это переводы вот этих статей:
http://www.koonsolo.com/news/dewitters-gameloop/ (автор: Koen Witters)
http://gameprogrammingpatterns.com/game-loop.html (автор: Bob Nystrom)

>[b]Рассуждение 1. Предсказания.[/b]
Идея предсказания, насколько я себе представляю, - как раз втащить в render() товсем тупую экстраполяцию (или интерполяцию), которой не нужен полноценный update(), но которая смотрится сильно лучше, чем ничего. Т. е. без всякого:
>Но в общем смысле мы должны изменять состояние.
Впрочем если мы может достичь:
>Идеал: на один апдейт - один вызов рендера.
и быть уверенными, что оно работает как часы и render'ит каждые 1/60 с (например), тогда, наверно, действительно, предсказание ни к чему.

>[b]Рассуждение 2. Update(delta).[/b]
>Как можно заметить, везде в статьях пишется только о update() - без параметров.
Да вроде обе статьи приводят примеры с дельтой, и говорят, почему это сомнительная идея.
Если delta фиксированная, то и передавать её (явно) особого смысла не видно.
>почему это должен произойти "взрыв"? Ведь мы же не в будущее заглядываем?
Не понял.

А взрыв, по-моему, более вероятен для больших delta. Нарушился где-нибудь критерий Куранта - и привет.
Впрочем, если внутри update(delta) устроен как "вызвать сколько нужно фиксированных обновлений" то одно и то же.

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

ZabПостоялецwww3 мая 20153:26#3
Странные статьи. Ощущение, что человек только начинает программировать и делает для себя "открытия". Очень путанные и возможно даже не верные.
Игр без отработки дельты времени не видел. Работающих не видел, а вот переделывать модули, где дельты не было, приходилось. Так, эти модули не просто так переделывать поручали, ибо они не работали как надо.
Запаздывание менее чем на одну двадцатую долю секунды разве нуждается в экстраполяции? Где задержки больше? Если только у вас синхронная работа с сервером или вы по глупости физику в другой поток вынесли - тогда да, может быть. Есть еще и быстродвижущиеся объекты, типа пуль, может только о них речь? Ну так, их моделируют иначе, отрезками траектории, а не фиксированными положениями в момент просчета.

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

BUzerПостоялецwww3 мая 20155:07#4
Vhodnoylogin
> Мы теряем тогда различия между Апдейтом (логикой) и рендером (отрисовкой)
Не, суть экстраполяции в том, что точность её результата не зависит от дельты. Например, если ты делал основной апдейт сначала 60 раз в секунду, а затем стал 30, то если в игре есть хоть какое-то подобие физики, это обязательно почувствуется. А для интерполяции-экстраполяции пофиг, на какую величину её применять — там нет ускорения, нет сил и столкновений, просто линейная скорость. Ну или движение по какой-то фиксированной траектории.
KKHПостоялецwww3 мая 201512:04#5
>Вот основные источники (http://habrahabr.ru/post/136878/) и (http://live13.livejournal.com/469940.html).
То что там написано хорошо подходит для несложного проекта. С другой стороны статья должна быть понятной. А на сложном проекте понятную статью написать не просто. Что мне не хватило в этой статье так это распределение ресурсов, погрузка уровней. Много поточность. Всё это закинуто в game_update().

>Рассуждение 1. Предсказания.
>1 Мы теряем тогда различия между Апдейтом (логикой) и рендером (отрисовкой) - мне кажется это неправильным.
Я нигде не нашёл такого диссонанса в статье. Если рассуждать в данном ключе то там речь шла о том, что на разных ЭВМ и на одной но с разным количеством объектов на сцене при условии что рендер работает на своём пределе появится разность в времени отрисовки кадра. Чтобы человек не ходил то быстрее то медленнее, он предложил воспользоваться таймером реального времени, чтобы объект класть в траекторию.

>2 А это - процесс сложный, а может даже и невозможный для какого-то момента.
автор не предлагает считать систему дифференциальных уравнений для каждого кадра. Формула может быть и эмпирической, лишь бы было красиво. Пример с пулей не совсем удачен на мой взгляд либо автор подразумевал какой то контекст.

3 Повторение 1. Нет никакого двойного просчёта. Есть один просчёт на конкретную единицу времени и есть время рендера. Если ЭВМ не справляется с рендером, есть смысл задуматься об оптимизации а затем и упрощении этого рендера и формул, процессов.

Вывод. Об этом нужно почитать из других источников, где тоже самое описано другими словами.

>Рассуждение 2. Update(delta).
из статьи

bool game_is_running = true;
    while( game_is_running ) {

        loops = 0;
        while( GetTickCount() > next_game_tick && loops < MAX_FRAMESKIP) {
            update_game();

            next_game_tick += SKIP_TICKS;
            loops++;
        }

        interpolation = float( GetTickCount() + SKIP_TICKS - next_game_tick )
                        / float( SKIP_TICKS );
        display_game( interpolation );
>совершенно неясно, почему везде пишутся апдейты без времени. Лично я вообще не вижу работу программы (в частности функции апдейт) без >передачи туда дельты.

Статья у него кривовато написана. Он слишком много подразумевает. Как видно он всё-таки использует время для расчёта апдейта анимации и положения объектов. Всё что выше написано это пример того чем плох или хорош тот или иной вариант. А также множество упрощений и подразумеваний.

f1ufx_Постоялецwww3 мая 201513:16#6
Рефакторил сиё у одной большой игры.
На этапе главного цикла - вообще ни о никаких "предсказаниях" нет и речи. Главная задача главного цикла в том, чтобы код разбить по модулям, при написании которых ты вообще не задумываешься когда они там выполняются.

Идеальный пример main loop-а - это фреймворк игровой логики для Unigine. Так вообще пипец - архитектура построена так, что главного цикла вообще словно вроде бы нет.

Ну это как ты например пишешь Qt приложение.. Ты эммитишь евенты и прочее вообще не задумываясь где там главный цикл.

На счёт "предсказаний" - это лаг-компенсатион, вот валвовцы хорошую статейку писали
https://developer.valvesoftware.com/wiki/Source_Multiplayer_Netwo… g?language=ru

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

VhodnoyloginПостоялецwww3 мая 201513:34#7
KKH
> Чтобы человек не ходил то быстрее то медленнее, он предложил воспользоваться
> таймером реального времени, чтобы объект класть в траекторию.
А с чего бы ему ходить "то быстрее, то медленнее"? Как скорость зависит от частоты кадров? Объект за время Х переместиться на столько, сколько ему положено, независимо от фпс. Рывки и лаги - да, будут. Но это же не "то быстрее, то медленнее"...

FordPerfect
> Да вроде обе статьи приводят примеры с дельтой, и говорят, почему это
> сомнительная идея.
> Если delta фиксированная, то и передавать её (явно) особого смысла не видно.
Почему это сомнительная идея - я так и не понял (если только проблема не одна - ограниченность флоата).
А вот с фиксированной дельтой я тоже не совсем понимаю. А кто сможет гарантировать, что она будет фиксированной?


Ладно, будем списывать все на упрощенность статей.

KKHПостоялецwww3 мая 201513:39#8
>Объект за время Х переместиться на столько, сколько ему положено, независимо от фпс.
В этом смысл приведённой тобой статьи.
>Рывки и лаги - да, будут.
Если всё сделать верно, то нет, не будет.
>Но это же не "то быстрее, то медленнее"...
это вырвано из контекста. В одном случае будут рывки и скорость перемещения различна, в других случаях этого не будет. Это рассмотрено в приведённой тобой статье.
VhodnoyloginПостоялецwww3 мая 201514:08#9
KKH
> Если всё сделать верно, то нет, не будет.
Ну, если видюха не справляется, то будут рывки.

> В одном случае будут рывки и скорость перемещения различна
Ну не могу понять я - отчего скорость перемещения будет различной. Скорость - это скорость, одна из составляющих состояния объекта. Поэтому графон не может (не должен) на нее влиять - хоть при 60 фпс, хоть при 15 объект будет проходить одинаковое расстояние с разной плавностью.

DampireУчастникwww3 мая 201519:04#10
Vhodnoylogin
> Рывки и лаги - да, будут. Но это же не "то быстрее, то медленнее"...
Что это за бред? Рывок это ускорение, лаг это остановка/откат с рывком к новой позиции. ТС, ты наркоман? Если ты не можешь понять почему, то запусти юнити и targetFrameRate сначала в 25, потом в 100. В коде пропиши перемещение кубика по удержанию клавиши с определенной скоростью без умножения на timeDelta. Посмотрим, насколько одинаково они у тебя поедут.
VhodnoyloginПостоялецwww3 мая 201519:48#11
Dampire
> Что это за бред? Рывок это ускорение, лаг это остановка/откат с рывком к новой
> позиции.
Рывок - это рваная картинка. Когда все дергается. Равно "у меня лаги и низкий фпс".  Никакого отношения к "Рывок это ускорение" не имеет. Кстати, рывок - это производная от ускорения, а не ускорение.
Так что не надо грубить, за приличного человека сойдешь.

DampireУчастникwww3 мая 201521:07#12
Vhodnoylogin
Не сойду, потому что я быдло и хам. Лаги и низкий фпс это совершенно разные вещи, а рваная картинка - это когда происходят разрывы изображения из-за особенностей построчной отрисовки картинки на экране и происходит как раз из-за избыточного фпс. Раз уж ты докопался до определения слова рывок, то будь точен в определениях и далее. 3:1 не в твою пользу. Вторую часть моего поста ты благополучно проигнорировал, что характерно для бесполезных гуманитариев-интеллигентишек вроде тебя.

timeDelta вычисляется для всей итерации. Именно поэтому все апдейты без дельты, а сама дельта берется из условно глобальной переменной, когда нужно переместить объект на 1метр за одну секунду. Напомни мне, какой фреймрейт у тебя, и какое расстояние пройдет объект, если position += Vector3(0f,0f,-1f) за одну секунду? Интерполяция нужна ТОЛЬКО если физика у тебя в другом треде, и ЗНАЧИТЕЛЬНО отличается по фреймрейту в МЕНЬШУЮ сторону. Я специально выделил основные слова, чтобы до тебя получше дошло, мой культурный друг. А твои чудные предсказания нужны ТОЛЬКО для сетевой игры, где ты НЕ МОЖЕШЬ гарантировать скорость передачи пакетов, и приходится ВЫКРУЧИВАТЬСЯ как можешь. Именно из-за некорректного предсказания игроки бегут рожей в стену, а не огибают угол. Если ты хочешь реализовать такую лабуду в оффлайновой игре, то на здоровье.

+ Открой меня, если ты ничего не понял

dubПостоялецwww3 мая 201521:52#13
>предсказание
в топку

>update без dt
Тут подразумевается, что dt фиксированный и обеспечивается это вызовом update() (без параметра) столько раз, сколько прошло времени. Фиксированное dt удобно тем, всякоразные вычисления ведут себя стабильнее и можно использовать более простые формулы.

Например: update() вызывается с частотой 0.1 сек. Можно проверять факт столкновения кирпича с головой просто проверяя пересечение параллелепипеда со сферой (считаем, что кирпич просто не успеет разогнаться до околосветовых скоростей и на одном из апдейтов обязательно окажется внутри головы). А если мы позволяем переменное dt, то должны учитывать, что dt может оказаться чрезмерно большим (железо слабое и dt оказался равен 1 сек). В итоге между двумя апдейтами кирпич МОЖЕТ пролететь сквозь голову не коснувшись ее. А может и не пролететь (т.н. баг шреденгера). Решается это усложнением функции update: либо ищем пересечение траектории полета кирпича со сферой, либо разбиваем траекторию на несколько мелких отрезков и ищем пересечение в каждой точке.

Кода больше, но зато переменное dt!

FordPerfectПостоялецwww3 мая 201522:02#14
Нарыл условно-древнюю статью.
http://www.gamedev.ru/community/gamedev_lecture/articles/game_orhetecture
Про game loop со 2-й страницы.
Страницы: 1 2 Следующая »

/ Форум / Программирование игр / Общее

Тема в архиве.

2001—2018 © GameDev.ru — Разработка игр