Comments 62
Спасибо за перевод. Очень познавательная статья.
А есть что-то про игровой цикл, но только с распараллеливанием? Например физику обновлять в одном потоке, ИИ в другом, отрисовка в третьем. Как тут быть с синхронизацией и взаимодействием разных компонентов движка между собой?
А есть что-то про игровой цикл, но только с распараллеливанием? Например физику обновлять в одном потоке, ИИ в другом, отрисовка в третьем. Как тут быть с синхронизацией и взаимодействием разных компонентов движка между собой?
+3
Я об этом думал… Перевод как первый шаг к имплементации через синглтоны и мультитред. По поводу синхоронизации — все зависит от используемого языка. Там есть ньюансы.
0
Кажется все-таки синглетоны вообще не причем к возможности распараллеливания. Или я что-то пропустил?
0
Синглтон + Мультитред (локи, мьютексы и пр.) = доступный отовсюду в аппликейшене тредсейфти сервис. Как-то так.
0
Я это себе так представляю:
1) Есть поток рендеринга — Low Priority
2) Есть поток обновления состояния — High Priority
3) Есть синглтон, хранящий GameState
Воркфлоу:
А) Поток обновления стейта работает на выбранной частоте и периодически апдейтит закэшированный стейт в синглтоне
Б) Поток рендеринга считывает текущий стейт из синглтона.
В) В синглтоне реализовывается синхронизация. Можно разбить веси стейт на блоки, сделав несколько критических секций. Может еще какие оптимизации.
Концепт такой. Надо попробовать что-то типа танчиков запилить.
1) Есть поток рендеринга — Low Priority
2) Есть поток обновления состояния — High Priority
3) Есть синглтон, хранящий GameState
Воркфлоу:
А) Поток обновления стейта работает на выбранной частоте и периодически апдейтит закэшированный стейт в синглтоне
Б) Поток рендеринга считывает текущий стейт из синглтона.
В) В синглтоне реализовывается синхронизация. Можно разбить веси стейт на блоки, сделав несколько критических секций. Может еще какие оптимизации.
Концепт такой. Надо попробовать что-то типа танчиков запилить.
0
как мне кажется GameState не должен быть в единственном экземпляре. иначе получится как при отключенной вертикальной синхронизации верхняя часть экрана одна картинка, нижняя — вторая.
только по объектам разделение, одни по предыдущему отсчету, другие по следущему. результат будет такой, что танчики будут дергаться. и двигаться неравномерно.
его можно разбить на статические данные и динамические. динамические должны иметь по объекту на каждый временной отсчет. которые и будет передаваться от одного треда к другому.
и не должно быть у потока игросостояния более высокого приоритета. получится, что 1 раз в 40мс он просыпается, что то делает, на это время фпс критически падает. потом он завершается, и фпс взлетает до небес. игра будет пульсирующей.
имхо правильнее сделать, чтоб поток игросостояния так же не был привязан к тому, что его будут вызывать ровно 25 раз в секунду. лучше если он будет просчитывать следующее состояние из рассчета на то, сколько прошло времени. естественно можно выставить некий таргет, и если выполняется быстрее, чем нужно, то можно стравливать время, а если медленнее, то упрощать вычисления, насколько игромеханика это позволяет.
только по объектам разделение, одни по предыдущему отсчету, другие по следущему. результат будет такой, что танчики будут дергаться. и двигаться неравномерно.
его можно разбить на статические данные и динамические. динамические должны иметь по объекту на каждый временной отсчет. которые и будет передаваться от одного треда к другому.
и не должно быть у потока игросостояния более высокого приоритета. получится, что 1 раз в 40мс он просыпается, что то делает, на это время фпс критически падает. потом он завершается, и фпс взлетает до небес. игра будет пульсирующей.
имхо правильнее сделать, чтоб поток игросостояния так же не был привязан к тому, что его будут вызывать ровно 25 раз в секунду. лучше если он будет просчитывать следующее состояние из рассчета на то, сколько прошло времени. естественно можно выставить некий таргет, и если выполняется быстрее, чем нужно, то можно стравливать время, а если медленнее, то упрощать вычисления, насколько игромеханика это позволяет.
0
> лучше если он будет просчитывать следующее состояние из рассчета на то, сколько прошло времени
Может я чего то не понял правильно, но в статье про это вроде написано.
> как мне кажется GameState не должен быть в единственном экземпляре
«НЕ должен быть» — это опечатка? В синглтоне всегда хранится последний стейт. Там я нехорошо написал про несколько критических секций. Все таки секция должна быть одна.
Про приоритеты… Мы делаем обновление с фиксированной частотой (10, 25 или сколько то еще раз в сек.), и эта частота должна иметь наивысший приоритет. Фактическая частота рендеринга неважна.
Может я чего то не понял правильно, но в статье про это вроде написано.
> как мне кажется GameState не должен быть в единственном экземпляре
«НЕ должен быть» — это опечатка? В синглтоне всегда хранится последний стейт. Там я нехорошо написал про несколько критических секций. Все таки секция должна быть одна.
Про приоритеты… Мы делаем обновление с фиксированной частотой (10, 25 или сколько то еще раз в сек.), и эта частота должна иметь наивысший приоритет. Фактическая частота рендеринга неважна.
0
> Фактическая частота рендеринга неважна.
Точнее «сколько получится».
Точнее «сколько получится».
0
1)у автора про отвязку вывода кадров от статических временных промежутков. я имел ввиду, что и просчет игросостояний тоже можно и нужно от них отвязывать.
2) не опечатка. следует читать как неприменимость синглтона. как я это вижу. вместо этого некий таймлайн игровых состояний. в виде очереди. стейт(первый) на -5мс, дальше стейт на +35мс(второй), и дальше еще стейт на +75мс. и отображатель рисует интерполированное состояние между первым и вторым. когда второй стейт достигает нулевой отметки времени, первый освобождается, а позади третьего от другого потока должен приехать четвертый стейт еще на 40мс позже.
3) частота фпс условно не важна. но важна его стабильность. если брать из рассчета 10 стейтов в секунду, то если обработчик состояния каждые 100мс проснется и с высоким приоритетом будет 50мс что то делать, то за это время не будет ни одного кадра. а в следующие 50 мс он уснет, и отображатель нарисует скажем 5-6 кадров. фпс будет достаточно высокий, 50-60, но он будет дерганый, вплоть до неиграбельности. равномерный фпс 25 кадров лучше выглядит, чем настолько неравномерный 60.
2) не опечатка. следует читать как неприменимость синглтона. как я это вижу. вместо этого некий таймлайн игровых состояний. в виде очереди. стейт(первый) на -5мс, дальше стейт на +35мс(второй), и дальше еще стейт на +75мс. и отображатель рисует интерполированное состояние между первым и вторым. когда второй стейт достигает нулевой отметки времени, первый освобождается, а позади третьего от другого потока должен приехать четвертый стейт еще на 40мс позже.
3) частота фпс условно не важна. но важна его стабильность. если брать из рассчета 10 стейтов в секунду, то если обработчик состояния каждые 100мс проснется и с высоким приоритетом будет 50мс что то делать, то за это время не будет ни одного кадра. а в следующие 50 мс он уснет, и отображатель нарисует скажем 5-6 кадров. фпс будет достаточно высокий, 50-60, но он будет дерганый, вплоть до неиграбельности. равномерный фпс 25 кадров лучше выглядит, чем настолько неравномерный 60.
0
Если рендеринг будет тяжел, то он просто не будет поспевать за обновлением стейта всего навсего. Отрисовывать рендерер всегда будет последнее состояние.
При малой производительности просто будут пропускаться некоторые промежуточные состояния: к примеру сначала рендеринг начнет отрисовывать состояние между первым тиком и вторым. Будет делать это два-три тика и в следующий раз отрисует состояние между 3 и 4 тиками или между 4 и 5 тиками.
Динамика персонажа не изменится, хоть картинка и будет дерганной. А вот если тяжелый рендеринг будет аффектать обновление стейта, то уже перс начнет вести себя непредсказуемо. Про это в статье и написано.
У вас была ситуация в шутерах на слабом железе, когда инерционность поворота камеры увеличивалась из-за тормозов. Теперь представьте, что вместо инерции будет лишь дрожание картинки, а реакция на мышь останется приличной.
Про очередь стейтов… Нельзя заранее просчитать несколько стейтов наперед, т.к. они зависят от ввода от юзера.
При малой производительности просто будут пропускаться некоторые промежуточные состояния: к примеру сначала рендеринг начнет отрисовывать состояние между первым тиком и вторым. Будет делать это два-три тика и в следующий раз отрисует состояние между 3 и 4 тиками или между 4 и 5 тиками.
Динамика персонажа не изменится, хоть картинка и будет дерганной. А вот если тяжелый рендеринг будет аффектать обновление стейта, то уже перс начнет вести себя непредсказуемо. Про это в статье и написано.
У вас была ситуация в шутерах на слабом железе, когда инерционность поворота камеры увеличивалась из-за тормозов. Теперь представьте, что вместо инерции будет лишь дрожание картинки, а реакция на мышь останется приличной.
Про очередь стейтов… Нельзя заранее просчитать несколько стейтов наперед, т.к. они зависят от ввода от юзера.
0
теряется возможность интерполяции и собственно смысл развязки. чтобы иметь 60фпс нужно и стейт просчитывать 60 раз в секунду и кадр отрисовывать 60 раз в секунду. если стейт просчитывается 10 раз в секунду, то и на экране будет 10. формально счетчик покажет 60, но кажые 6 кадров будут одинаковы и пользователь будет видеть 10фпс.
и наоборот, если рендер «тяжелый» и выводит всего 10 фпс, то зачем просчитывать 60 состояний, отображаться будет лишь каждое 6-ое, остальные уйдут в корзину.
очередь стейтов решает вот эти проблемы. если рендер не отобразил стейт, то можно увеличивать время между отсчетами. и наоборот, если рендер быстр, то он и будет определять плавность анимации, а не обработчик состояния.
А несколько стейтов наперед просчитать можно. вопрос в инпут-лаге.
его кстати надо еще уметь просчитывать. это тоже в тему задач гейм-дева.
если взять опять же supreme commander. то 10 стейтов в секунду он просчитывает. 100мс между отсчетами. если ввод от игрока придет на 101-ой мс, то отклик на его действие попадет в стейт, который начнет просчитываться на 200-ой мс.
допустим стейт просчитывается за 40мс, рендер идет каждые 20мс(50фпс), то получится минимальный инпут лаг в 160мс. если ставить задачу просчитать на один стейт вперед, то инпут лаг становится 260мс. что для реал-тайм стратегий вполне приемлемо.
кстати лобби в этой игре красным отмечает пинг выше 500мс, все что ниже считается приемлемым.
в случае же шутеров сцена существенно проще, объектов на 2 порядка меньше в такой модели просто нет необходимости. стейт можно просчитывать очень быстро, и не париться интерполяцией, а задавать шаг анимации сразу в стейте.
и наоборот, если рендер «тяжелый» и выводит всего 10 фпс, то зачем просчитывать 60 состояний, отображаться будет лишь каждое 6-ое, остальные уйдут в корзину.
очередь стейтов решает вот эти проблемы. если рендер не отобразил стейт, то можно увеличивать время между отсчетами. и наоборот, если рендер быстр, то он и будет определять плавность анимации, а не обработчик состояния.
А несколько стейтов наперед просчитать можно. вопрос в инпут-лаге.
его кстати надо еще уметь просчитывать. это тоже в тему задач гейм-дева.
если взять опять же supreme commander. то 10 стейтов в секунду он просчитывает. 100мс между отсчетами. если ввод от игрока придет на 101-ой мс, то отклик на его действие попадет в стейт, который начнет просчитываться на 200-ой мс.
допустим стейт просчитывается за 40мс, рендер идет каждые 20мс(50фпс), то получится минимальный инпут лаг в 160мс. если ставить задачу просчитать на один стейт вперед, то инпут лаг становится 260мс. что для реал-тайм стратегий вполне приемлемо.
кстати лобби в этой игре красным отмечает пинг выше 500мс, все что ниже считается приемлемым.
в случае же шутеров сцена существенно проще, объектов на 2 порядка меньше в такой модели просто нет необходимости. стейт можно просчитывать очень быстро, и не париться интерполяцией, а задавать шаг анимации сразу в стейте.
0
> теряется возможность интерполяции и собственно смысл развязки
По какой причине? Если интерполяцию сделать невозможно даже приближенно (Хмм а почему, собственно? тоже задумывался, но пока в голову не пришел пример), тогда да… лесом идет эта имплементация с интерполяцией и мы откатываемся обратно к двупоточной системе, где нам уже нужно давать достаточный приоритет обоим.
> если рендер «тяжелый» и выводит всего 10 фпс, то зачем просчитывать 60 состояний
В случае физики, как раз нужно будет. В статье упомянут «взрыв» симуляции.
По какой причине? Если интерполяцию сделать невозможно даже приближенно (Хмм а почему, собственно? тоже задумывался, но пока в голову не пришел пример), тогда да… лесом идет эта имплементация с интерполяцией и мы откатываемся обратно к двупоточной системе, где нам уже нужно давать достаточный приоритет обоим.
> если рендер «тяжелый» и выводит всего 10 фпс, то зачем просчитывать 60 состояний
В случае физики, как раз нужно будет. В статье упомянут «взрыв» симуляции.
0
UFO just landed and posted this here
Сейчас активно CUDA используют в гейм деве (посмотрите шоукейсы на сайте NVidia). Но это для серьезных разработок. Но уже стираются границы между ЦП и ГП.
А в казуалках 2D акселерация железная, вроде, не так распространена. И тот же XNA ЦП нагружает значительно. Впрочем надо профилировать, чтоб точно сказать что в XNA игрушках (например та же Террария) загружает ЦП.
А в казуалках 2D акселерация железная, вроде, не так распространена. И тот же XNA ЦП нагружает значительно. Впрочем надо профилировать, чтоб точно сказать что в XNA игрушках (например та же Террария) загружает ЦП.
0
Это реальный способ ускорить игру, другие способы, когда разные подсистемы в отдельные потоки складывают просто не работают из-за тесной связи между ними. Анимация связана с физикой, которая связана с триггерами гейплея, которые вообще в скриптах обрабатываются и тесно связаны с ИИ.
0
> Фактически мы можем принять как данность, что даже на слабом железе функция update_game() вызывается 25 раз в секунду. Поэтому наша игра будет обрабатывать пользовательский ввод и обновление состояние без особых проблем даже в случае, когда отрисовка выполняется на частоту 15 кадров в секунду.
Если говорить простыми словами, то я вижу алгоритм таким:
Ну не могу я представить ситуацию, когда при последовательном вызове функций одна будет выполняться чаще, чем другая, при том, что ее запуск «прореживают». В статье ведь не говорится о многопоточности etc. Что я недопонял?
Если говорить простыми словами, то я вижу алгоритм таким:
бесконечный цикл {
если (update_game() выполняется не чаще 25 раз в секунду) {
update_game();
}
display_game();
}
Ну не могу я представить ситуацию, когда при последовательном вызове функций одна будет выполняться чаще, чем другая, при том, что ее запуск «прореживают». В статье ведь не говорится о многопоточности etc. Что я недопонял?
0
Запуск update_game не прореживают, а «стабилизируют»: она может запускаться как реже (если, к примеру, display_game уложится в 0.01с), так и чаще (0.1с на отображение). Ваш алгоритм на это не способен, увы.
0
Ъ. Пока писал самый первый комментарий «где же отличие?», до меня дошло, но я случайно нажал на Alt-Home, и простыня комментария с объяснением стерлась (>_<)
Отличие оказалось в том, как алгоритм будет реагировать на задержку. В моем случае — никак (^_^) В случае алгоритма из статьи оный при выявлении задержки будет обновлять мир столько раз, на сколько этот мир «опаздывает».
Моя ошибка была в том, что я сделал неверные выводы. Да, update_game() не будет выполняться каждую 1/25 секунды. Обновление может вызываться себе приблизительно с таким интервалом, а потом display_game() напорется на сложную сцену, отрисовка которой займет 1/20 секунды, и следующий update_game() будет вызван уже через 1/5 секунды (5/25 — три интервала, короче), но благодаря такому построению игрового цикла, как в статье, он будет вызван не один, а пять раз, чтобы компенсировать задержку (таким образом, в это время между вызовами update_game() будет проходить 1-2 мс). НО. В среднем (по больнице, есессено, и тем больше по больнице, чем круче скачки FPS) update_game() будет вызываться именно 25 раз в секунду (неважно, с какими промежутками между вызовами), а значит, что можно считать, что между вызовами update_game() в среднем проходит 1/25 секунды.
И, наконец, описание своими словами алгоритма из статьи:
Есессено, что значение времи между вызовами с учетом единиц измерения (секунд) будет дробным, а значит, что после деления на 1/25 (умножения на 25) нужно взять целое число, но это уже нюансы (равно как и переменные, точки отсчета etc).
Отличие оказалось в том, как алгоритм будет реагировать на задержку. В моем случае — никак (^_^) В случае алгоритма из статьи оный при выявлении задержки будет обновлять мир столько раз, на сколько этот мир «опаздывает».
Моя ошибка была в том, что я сделал неверные выводы. Да, update_game() не будет выполняться каждую 1/25 секунды. Обновление может вызываться себе приблизительно с таким интервалом, а потом display_game() напорется на сложную сцену, отрисовка которой займет 1/20 секунды, и следующий update_game() будет вызван уже через 1/5 секунды (5/25 — три интервала, короче), но благодаря такому построению игрового цикла, как в статье, он будет вызван не один, а пять раз, чтобы компенсировать задержку (таким образом, в это время между вызовами update_game() будет проходить 1-2 мс). НО. В среднем (по больнице, есессено, и тем больше по больнице, чем круче скачки FPS) update_game() будет вызываться именно 25 раз в секунду (неважно, с какими промежутками между вызовами), а значит, что можно считать, что между вызовами update_game() в среднем проходит 1/25 секунды.
И, наконец, описание своими словами алгоритма из статьи:
бесконечный цикл {
если (время между вызовами update_game() больше 1/25 секунды) {
[время_между_вызовами_update_game_в_секундах / (1/25)] раз сделать { update_game(); }
}
display_game();
}
Есессено, что значение времи между вызовами с учетом единиц измерения (секунд) будет дробным, а значит, что после деления на 1/25 (умножения на 25) нужно взять целое число, но это уже нюансы (равно как и переменные, точки отсчета etc).
0
Вы предлагаете игнорировать изредка возникающую проблему, автор предлагает её решение. Кроме того, в среднем 25 будет как раз у автора; у вас получится меньше (совсем немного), но это может легко привести, к примеру, к рассинхронизации в мультиплеере.
Ваш новый алгоритм уже стал сложнее предложенного, и вы забыли про время выполнения самого update_game и MAX_FRAMESKIP. И то и то может иметь значение. А с учётом нюансов, код усложнится ещё сильнее.
Ваш новый алгоритм уже стал сложнее предложенного, и вы забыли про время выполнения самого update_game и MAX_FRAMESKIP. И то и то может иметь значение. А с учётом нюансов, код усложнится ещё сильнее.
0
Как же хорошо было с контроллерами прерываний… Нажали кнопку или двинули мышкой и сразу прерывание и его обработка, а пока игрок ничего не вводит, только обсчитываем и отрисовываем. :)
0
Довольно часто этот баг встречается у начинающих разработчиков — начинаешь двигать мышью и игра резко начинает жрать много процессора. Поэтому, если по правильному, положение мыши сохраняют и обрабатывают только в update(). Тогда независимо от кол-ва движений мышью игра потребляет одно и то же кол-во ресурсов.
0
Если обработка движения мыши начинает жрать много процессора, выкиньте этот процессор.
-1
Вы плохо себе представляете логику работы игры.
Если при каждом событии мыши вы начинаете выполнять относительно непростые действия, то легко получается, что у вас эта обработка фактически вызывается более чем 100 раз в секунду. Для игры это бессмысленно, а нагрузку дает (особенно если это Flash, а если мобильное приложение, то вы бессмысленно «выжигаете» батарею).
Если при каждом событии мыши вы начинаете выполнять относительно непростые действия, то легко получается, что у вас эта обработка фактически вызывается более чем 100 раз в секунду. Для игры это бессмысленно, а нагрузку дает (особенно если это Flash, а если мобильное приложение, то вы бессмысленно «выжигаете» батарею).
0
Не понимаю, в чем разница? Биос все равно вызывает прерывания по изменению состояния датчиков. Если боитесь слишком часто обрабатывать ввод, то обрабатывайте не каждое изменение, а время проверяйте или в стек записывайте и обрабатывайте уже в самой игре.
0
Теперь перечитайте мое первое сообщение :)
0
Вы ответили как противопоставление обработке прерывания при его возникновении. Объявили обработку прерывания багом. И я объяснил, что обработка прерывания это не баг, а вполне рабочий вариант. Возможно, фраза «обработка прерывания» вызвала у Вас ассоциации с работой логической модели игры в зависимости от введенных данных. Это не так. Я про логику игры говорил только, что обсчитываем и отрисовываем, когда игрок ничего не вводит. А Вы?
0
Если вы при событии MOUSE_MOVE сохраняете координаты мыши, а обрабатываете после, то у вас по-любом вариант с update/draw, а не обработка по событию.
«двинули мышкой и сразу прерывание и его обработка» — это событийная модель, и она для игр не подходит.
«обрабатывайте не каждое изменение, а время проверяйте или в стек записывайте и обрабатывайте уже в самой игре» — это update/draw, и ее все используют.
«двинули мышкой и сразу прерывание и его обработка» — это событийная модель, и она для игр не подходит.
«обрабатывайте не каждое изменение, а время проверяйте или в стек записывайте и обрабатывайте уже в самой игре» — это update/draw, и ее все используют.
0
Вы издеваетесь (это больше к автору и приверженцам этой тактики), какие циклы, что за 'прошлый век'!
Событийный механизм — когда обновление экрана делается по времени по алгоритму XX кадров в YY интервал, когда события в игре выполняются строго когда нужно, когда не объекты сдвигаются на пиксел на game_update(), а их координаты вычисляются в момент их запроса.
И не будет тогда ни проблем с тачпадом (когда при меленном выводе невозможно нарисовать гладкую кривую, хотя проблемы могут вытекать еще глубже — из железа и драйверов), ни глюков с разными скоростями, набегающих из-за ошибок вычислений (того же float — ошибки с ним заметнее) и многого другого можно просто не заметить/не встретить, если правильно изначально НЕ ОРГАНИЗОВЫВТЬ цикл.
p.s. в конечном счете цикл будет, но не игровой а событийный, и его может реализовывать уже сама библиотека, используемая для организации событий.
p.p.s. FPS и 30 более чем достаточно, проблема чаще возникает не из-за низкого FPS а из-за его неравномерностей, и самое простое — сначала считаем изображение, затем ждем до момента вывода, затем выводим — т.е. должен быть небольшой лаг в 1/30 секунды (бывают алгоритмы, выдающие лаг в 2-3 кадра).
Еще неплохим решением со слабым железом может быть вывод недоконца построенного изображения (особенно если есть возможность ухудшить качество без уменьшения информативности) это воспринимается гораздо лучше чем пропуск кадров.
Событийный механизм — когда обновление экрана делается по времени по алгоритму XX кадров в YY интервал, когда события в игре выполняются строго когда нужно, когда не объекты сдвигаются на пиксел на game_update(), а их координаты вычисляются в момент их запроса.
И не будет тогда ни проблем с тачпадом (когда при меленном выводе невозможно нарисовать гладкую кривую, хотя проблемы могут вытекать еще глубже — из железа и драйверов), ни глюков с разными скоростями, набегающих из-за ошибок вычислений (того же float — ошибки с ним заметнее) и многого другого можно просто не заметить/не встретить, если правильно изначально НЕ ОРГАНИЗОВЫВТЬ цикл.
p.s. в конечном счете цикл будет, но не игровой а событийный, и его может реализовывать уже сама библиотека, используемая для организации событий.
p.p.s. FPS и 30 более чем достаточно, проблема чаще возникает не из-за низкого FPS а из-за его неравномерностей, и самое простое — сначала считаем изображение, затем ждем до момента вывода, затем выводим — т.е. должен быть небольшой лаг в 1/30 секунды (бывают алгоритмы, выдающие лаг в 2-3 кадра).
Еще неплохим решением со слабым железом может быть вывод недоконца построенного изображения (особенно если есть возможность ухудшить качество без уменьшения информативности) это воспринимается гораздо лучше чем пропуск кадров.
+3
Напишите похожую статью про событийный механизм в играх, заинтересовался очень даже… Раньше нигде не встречал подобного подхода.
+3
И мне интересно… Я видимо неправильно понял. Перечитаю еще несколько разков. Но пока сдается мне, что это подобие первого решения в данной статье, но с событиями. Наверняка я ошибаюсь.
0
Одно время уже обсуждали на Хабре события и событийный подход к программированию (кажется, для экономии энергии (читайте — аккумулятора)). Лично я сделал для себя такой вывод: пока не появится удобного (в плане строк кода, подключаемых библиотек, кроссплатформенности и тому подобных шняг) API для аппаратной поддержки событий (прерывания-то есть, но многим работать с ними не очень удобно, да и абстракции вроде API для универсального доступа к устройствам накладывают свои ограничения), события и их обработка в итоге сведутся к тому же бесконечному циклу.
0
habrahabr.ru/blogs/development/134559/ собственно
0
UFO just landed and posted this here
наш рабочий проект создавался еще когда не было операционных систем, точнее они были, но их использование было неоправдано. а «многозадачность» нужна. я то тут не так давно, но вот с опухшими глазами смотрю на это.
так вот, там реализован sched вот как раз по этому принципу, только более комплексно. очереди сообщений 2х-милисекундные, 20ти, 100. сам считает таймслайсы, и т.п.
сейчас это все, причем существенно эффективнее делает операционная система. хоть линух, хоть винда дает возможности многозадачности другого порядка.
собственно тут именно эта задача и стоит. два отдельных процесса — обработки состояния игры и ее отображения должны быть полностью независимы, чего собственно и добивается автор пытаясь абстрагировать механику игры от фпс.
как мне кажется проще и правильнее сделать два треда. в одном updateGame, в другом displayGame. и соответственно первый второму через пайп скармливает данные для отображения.
чем колхозить вот такую вот «двухзадачность», которая еще и при этом не сможет двухядерник даже напрячь.
так вот, там реализован sched вот как раз по этому принципу, только более комплексно. очереди сообщений 2х-милисекундные, 20ти, 100. сам считает таймслайсы, и т.п.
сейчас это все, причем существенно эффективнее делает операционная система. хоть линух, хоть винда дает возможности многозадачности другого порядка.
собственно тут именно эта задача и стоит. два отдельных процесса — обработки состояния игры и ее отображения должны быть полностью независимы, чего собственно и добивается автор пытаясь абстрагировать механику игры от фпс.
как мне кажется проще и правильнее сделать два треда. в одном updateGame, в другом displayGame. и соответственно первый второму через пайп скармливает данные для отображения.
чем колхозить вот такую вот «двухзадачность», которая еще и при этом не сможет двухядерник даже напрячь.
+1
кстати про аппроксимацию и интерполяцию. читал про игру Supreme Commander. там обсчет игрового состояния идет 10 раз в секунду всего. при этом фпс 60 рисует абсолютно плавное, и при этом анимированное движение. анимация вся явно в треде отображения. тред игросостояния
я так полагаю, что тред отображения при этом всегда должен запаздывать. ибо ракета должна попасть в цель, что может произойти по времени между этими двумя игровыми отсчетами. и тред игросостояния собственно не просто следующее состояние" должен выдавать, а всю достаточную для аппроксимации информацию.
и кстати игра утилизирует 2 треда.
я так полагаю, что тред отображения при этом всегда должен запаздывать. ибо ракета должна попасть в цель, что может произойти по времени между этими двумя игровыми отсчетами. и тред игросостояния собственно не просто следующее состояние" должен выдавать, а всю достаточную для аппроксимации информацию.
и кстати игра утилизирует 2 треда.
0
хмм… редактирование съело кучу текста… ну ладно…
0
Я думал о границах применения интерполяции. Если прогнозировать движение объекта, на которое оказывает влияние пользовательский ввод или другие в общем случае случайные факторы, то прогноз и интерполяция с использованием текущих параметров движения объекта будет давать пренебрежимую погрешность лишь на коротких промежутках времени. Видимо 1/10 секунды — достаточно короткий таймсэмпл.
Про физику и дифф. ур-ия тут умолчим.
Про физику и дифф. ур-ия тут умолчим.
0
про Unity3d не совсем верно описано.
Да, там нет единого цикла, который обновляет игру. Зато каждый объект может обновить свое(ну или если надо, то и другого объекта) состояние.
В юнитях в классах, наследующиесях от MonoBehaviour, есть такие методы как Update, FixedUpdate, LateUpdate.
Update вызывается при обновлении каждого фрейма. LateUpdate вызывается после выполнения Update(к примеру, на апдейте мы пересчитали данных, а на LateUpdate мы отрисовали с использованием уже обновленных данных).
А FixedUpdate — это такой же апдейт, но вызывается раз в определенное время, которое задается в настройках проекта (то есть не зависит от быстродействия девайса).
Да, там нет единого цикла, который обновляет игру. Зато каждый объект может обновить свое(ну или если надо, то и другого объекта) состояние.
В юнитях в классах, наследующиесях от MonoBehaviour, есть такие методы как Update, FixedUpdate, LateUpdate.
Update вызывается при обновлении каждого фрейма. LateUpdate вызывается после выполнения Update(к примеру, на апдейте мы пересчитали данных, а на LateUpdate мы отрисовали с использованием уже обновленных данных).
А FixedUpdate — это такой же апдейт, но вызывается раз в определенное время, которое задается в настройках проекта (то есть не зависит от быстродействия девайса).
0
А кем все эти эвенты вызываются? В фоне петля должна иметься какая-то. Либо все компоненты — отдельные треды в некотором пуле. Так или иначе петля есть?
+1
Достаточно много работаю в геймдеве и согласен, новички очень часто не понимают этой темы. Теперь будет ссылка, куда их можно послать :)
+2
Реквестиирую флешеров в тред))
Поделитесь своими размышлениями по сабжу.
Поделитесь своими размышлениями по сабжу.
0
А собственно что вы хотите услышать? Все приведенные принципы работают для любого языка программирования. Или вы думаете что пульс игры на Actionscript выглядит по другому? :)
0
Ну так то оно так, только даже таймер во флеше от фпс зависит и его обновление не возможно чаще чем раз в кадр. Т.е. 1/25-30 секунды или 1/60 секунды.
0
Насколько я знаю там эту систему привязывают к Enter Frame-у и работает она аналогично — проверяет время, которое было между кадрами, вызывает необходимое количество update_game() после этого вызывает уже display_game()
Но переменного FPS на флеше не получится, потому что там в любом случае ФПС ограничен настройкой флеш плеера.
Но переменного FPS на флеше не получится, потому что там в любом случае ФПС ограничен настройкой флеш плеера.
0
В Box2d вроде как единственный верный путь — использовать постоянный шаг физики с переменным шагом отрисовки. К сожалению, я не программист, поэтому не могу сказать точно. Там даже в комментариях к коду ссылка на статью есть. ;)
0
Не копался в Box2D, но читал/смотрел туториал про то как создается легковесный игровой физический движок. Как раз «проповедовался» подход на основе переменных промежутков времени. Применительно к линейному перемещению — просто вычислялось приращение позиции в зависимости от скорости и пройденного времени. От интегрирования дифф. уравнений настоятельно рекомендуют отказаться.
Поэтому подход с постоянным шагом не является и не заявляется как единственно верный. Но на мой взгляд — это способ получить более реалистичную физику, и такой подход в чем-то сильно упрощает реализацию физики. Конечно, появляются сложности выдерживания этого постоянного шага. Потому я и писал, что тред с обновлением состояния (или только тред физики) должен иметь наивысший приоритет.
Поэтому подход с постоянным шагом не является и не заявляется как единственно верный. Но на мой взгляд — это способ получить более реалистичную физику, и такой подход в чем-то сильно упрощает реализацию физики. Конечно, появляются сложности выдерживания этого постоянного шага. Потому я и писал, что тред с обновлением состояния (или только тред физики) должен иметь наивысший приоритет.
0
Ну возможно для разных задач имеет смысл использовать разный подход. Я копался на форумах и основной подход был в воспроизводимости результатов. При использовании переменного шага воспроизводимость не может быть 100%, ибо существуют ошибки округления и прочий квантовый шум. А идея как раз была в 100% воспроизводимости (те же реплеи, но уже с применением реальной физики).
Вот, что я использую в своём прототипе:
-(void) update: (ccTime) dt // Using FIXED timestep
{
static double timeAccumulator = 0;
timeAccumulator += dt;
if (timeAccumulator > (MAX_CYCLES_PER_FRAME * UPDATE_INTERVAL)) {
timeAccumulator = UPDATE_INTERVAL;
}
while (timeAccumulator>=UPDATE_INTERVAL) {
timeAccumulator -= UPDATE_INTERVAL;
world->Step(UPDATE_INTERVAL, velocityIterations, positionIterations);
for (b2Body *b = world->GetBodyList(); b; b=b->GetNext()) {
if (b->GetUserData() != NULL) {
}
}
//
self.hero.jumpTimeout--;
[self.hero update];
//
[self processContacts];
[self addBody];
[self removeBody];
}
}
Не работает разметка что-то…
Вот, что я использую в своём прототипе:
-(void) update: (ccTime) dt // Using FIXED timestep
{
static double timeAccumulator = 0;
timeAccumulator += dt;
if (timeAccumulator > (MAX_CYCLES_PER_FRAME * UPDATE_INTERVAL)) {
timeAccumulator = UPDATE_INTERVAL;
}
while (timeAccumulator>=UPDATE_INTERVAL) {
timeAccumulator -= UPDATE_INTERVAL;
world->Step(UPDATE_INTERVAL, velocityIterations, positionIterations);
for (b2Body *b = world->GetBodyList(); b; b=b->GetNext()) {
if (b->GetUserData() != NULL) {
}
}
//
self.hero.jumpTimeout--;
[self.hero update];
//
[self processContacts];
[self addBody];
[self removeBody];
}
}
Не работает разметка что-то…
0
Как я понял в этом коде вычисляется сколько заданных квантов времени укладывается в прошедшем промежутке времени. Однако в аккумуляторе опять будет накапливаться значительная ошибка, т.к. при малом «dt» ошибка округления (особенно при хранении dt во float) будет достаточно велика.
0
Прим. перев.: в движке NeoAxis для физического объекта можно выставлять флаг Continuous Collision Detection; подозреваю, что по нему как раз и выполняется обработка, подобная описанной выше реализации игрового цикла.
CCD используется для детектирования коллизий быстрых объектов. Насколько я помню, есть 2 основных подхода:
1. Увеличить TICKS_PER_SECOND;
2. Быстрый объект растянуть по всему пути его следования.
0
Как я понял в NeoAxis делается интерполяция все-таки — просчитываются два положения между стандартными тиками и вычисляется позиция в промежутке.
>1. Увеличить TICKS_PER_SECOND;
В шутерах будет напряжно.
>2. Быстрый объект растянуть по всему пути его следования.
Сложно реализовать при криволинейных траекториях (особенно при действии силовых полей).
>1. Увеличить TICKS_PER_SECOND;
В шутерах будет напряжно.
>2. Быстрый объект растянуть по всему пути его следования.
Сложно реализовать при криволинейных траекториях (особенно при действии силовых полей).
0
Как я понял в NeoAxis делается интерполяция все-таки — просчитываются два положения между стандартными тиками и вычисляется позиция в промежутке.
>1. Увеличить TICKS_PER_SECOND;
В шутерах будет напряжно.
Поэтому частота обработки увеличивается только для объекта, для которого CCD включен (для пули). Получается как раз то, о чем вы написали — вычисляются промежуточные позиции. Иначе пуля за один тик может преодолеть большое расстояние и пролететь сквозь тонкую стенку, а не столкнуться с ней.
>2. Быстрый объект растянуть по всему пути его следования.
Сложно реализовать при криволинейных траекториях (особенно при действии силовых полей).
Да. Но при прямолинейном движении иногда можно получить выигрыш.
0
> Поэтому частота обработки увеличивается только для объекта, для которого CCD включен (для пули). Получается как раз то, о чем вы написали — вычисляются промежуточные позиции.
Интерполяция и увеличение частоты обработки — суть разные подходы. Интерполяция может выполняться на стандартных тиках. Просто делается проверка могла ли произойти коллизия быстрого объекта с медленным между последними двумя тиками или произойдет ли она между текущим тиком и следующим. В шутерах обрабатывать позиции пуль с частотами кратными главной частоте тиков тяжеловато, как я понимаю.
Интерполяция и увеличение частоты обработки — суть разные подходы. Интерполяция может выполняться на стандартных тиках. Просто делается проверка могла ли произойти коллизия быстрого объекта с медленным между последними двумя тиками или произойдет ли она между текущим тиком и следующим. В шутерах обрабатывать позиции пуль с частотами кратными главной частоте тиков тяжеловато, как я понимаю.
0
Насколько я понял статью gafferongames.com/post/fix_your_timestep там предлагается совсем не экстраполяция к предполагаемому будущему положению объектов, а наоборот остаток используется как коэффициент для интерполяции между текущим и предыдущим состоянием объектов. Т.е. объект на экране не опережает свое время, а наоборот отстает от рассчитанного положения (сильно отстает или не очень — зависит от величины остатка в аккумуляторе). Это сглаживает скачки + нет ошибки экстраполяции. Это отставание не так влияет на игровой процесс, так как в худшем случае это будет отставание в один кадр
0
Sign up to leave a comment.
Игровые циклы или ЭлектроКардиоГама