Как стать автором
Обновить

Комментарии 39

НЛО прилетело и опубликовало эту надпись здесь
Ответ:
1. спасибо, попробую учесть в будущих правках.
2. мне нет особого смысла брать мультиплатформенностью, движок узкопрофильный все же, к тому же пишу я его скорее для «чего-нибудь написать», нежели для особой пригодности его (впрочем, если так выйдет, буду рад). И еще, стараюсь отделить по возможности мух от котлет и по идее обеспечение кроссплатформенности не должно стать проблемой в будущем, т.к. сейчас я сосредоточен в основном на механизмах работы игровой логики, а все остальное, что было реализовано — инструмент для тестирования этой самой логики.
Уже стало проблемой. Таймер некроссплатформенный, привязан к вызову конкретной платформы. :)
err:module:attach_process_dlls «MSVCR90.dll» failed to initialize, aborting
err:module:LdrInitializeThunk Main exe initialization for L«D:\\Downloads\\de\\DustEngine.exe» failed, status c0000142
Можно подробней: где и при каких условиях ошибка вылезла?

Попробуйте скомпилить оригинальный, нераскрашенный код (по ссылкам http://s-c.me/5234/s и http://s-c.me/5232/s). Если заработает, тогда сменю раскрашивалку.

От автора: «компилилось под msvs2008sp1 redist».
скачал бинарник с сурсфоржа, попытался запустить и на тебе.

условия:
wine                                         1.1.35~winehq1-1
Ммм. Гляньте UPD к статье. Насчет wine ничего сказать не могу.
НЛО прилетело и опубликовало эту надпись здесь
Смотри апдейт к статье, мы с тобой признаны автором убогими и недостойными запуска таких приложений.
НЛО прилетело и опубликовало эту надпись здесь
>include <windows.h>
Ну госпади же ты ё моё… Ну неужели в мире нету готовых платформонезависисых решений данной проблемы? Вот в жизни не поверю. Зачем изобретать велосипеды да ещё и одноколёсные?
Надежных и точных (high resolution) кросс-платформенных решений НЕТ.
В геймдеве обычно используют оптимальное решение для каждой отдельной платформы (PC, PS3, 360, и т.д.), скрытое за общим интерфейсом.
сравним точность и скорость данного решения с кроссплатформенными аналогами — Qt и GLib?)
уверен, что последние как минимум не будут уступать в скорости
Qt и glib это довольно высоко-уровневые абстракции, а не стандартные кросс-платформенные решения.
Ждем статью «Основы создания игрового движка: переменные и константы»
Это так задумано?

В ф-ции deTimer::init() есть деление на frequency, значение которого получаем в deTimer::getFrequency(), которая в свою очередь может вернуть ноль.
Вполне возможно. В результате будет бесконечность.
Что будет с вашим таймером в многопоточной среде?
Работает на четырех ядрах, правда, я в кодинге не особо. Зато демку погонял. :)
Количество ядер и потоков никак между собой не связаны.
Ничего страшного не произойдет. Автор гарантирует исполнение QueryPerformanceCounter на одном ядре:
SetThreadAffinityMask(GetCurrentThread(), 0);
Лучше бы рассказали про идею своего движка: что он реализует такого, что должно быть в каждом игровом движке, но еще нет.
Проще создать поток, в котором отсчитывать заданный интервал при помощи WaitForSingleObject, в котором также ожидать срабатывания события остановки потока. Точность такого таймера регулируется приоритетом потока. Никаких проблем с загрузкой процессора, потенциально кроссплатформенное решение.

Для ObjectPascal уже лет 10 реализовано в виде TThreadedTimer.
Точность WaitForSingleObject гораздо хуже QPC.
Для отсчета равных интервалов он все равно предлагает использовать Sleep, точность которой в потоке с нормальным приоритетом будет никакая. Ну измерит он в одном кадре с точностью до микросекунды задержку 100 миллисекунд, в другом 150… Толку-то?

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

* что означает вот эта фраза: «сильно загружают процессор, имеет смысл ограничить чатоту кадров, выполняя Sleep»?

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

* «Самым логичным решением» почему? Нужно рассмотреть другие варианты, например: GetTickCount(), clock(), описать все плюсы и минусы.

* странно использование плавающей арифметики, чем обусловлен такой выбор? Почему не целочисленная арифметика?

* «обратите внимание: время здесь не замеряется» почему?

теперь по поводу стрельбы:
int shootTimer = clock();

void tick()
{
   if (shootTimer < clock())
  {
    shootTimer += interval;
    shoot();
  }
}

Всё.

P.S. C-style cast не есть тру.

P.P.S. а так молодец, больше хороших статей по C++
Комментарий автора:

* что означает вот эта фраза: «сильно загружают процессор, имеет смысл ограничить чатоту кадров, выполняя Sleep»?
В конце каждого кадра выполнять функцию Sleep(x), таким образом частота кадров никогда не превысит 1000/x.

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

* «Самым логичным решением» почему? Нужно рассмотреть другие варианты, например: GetTickCount(), clock(), описать все плюсы и минусы.
Поначитался геймдева да мсдн, везде для точного таймера рекомендовали именно эти функции

* странно использование плавающей арифметики, чем обусловлен такой выбор? Почему не целочисленная арифметика?
Физика игры (даже простейшие движения по прямой) базируется на реальных физических законах скорость+ускорение во времени, поэтому числа с плавающей запятой больше подходят, как «более физические», т.е. мы можем описывать скорость в пикс./сек. исходя из того, что 1.0 = 1 секунда, и не используя более точное, но слишком абстрактное для данных условий число тиков

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

теперь по поводу стрельбы:
Насчет этого могу сказать лишь то, что доступно много и разных способов реализации, а у меня это сделано через менеджер событий из-за мой попытки отделить события по таймеру от всего остального, дабы классы оружия, менеджера оружий и игрока не хранили лишнюю информацию насчет времени и не обновляли ее (у меня только 2 раза используется взятие времени — для обновления физики, и для обновления событий). В будущем возможно пересмотрю позицию.
> В конце каждого кадра выполнять функцию Sleep(x), таким образом частота кадров никогда не превысит 1000/x.

Все равно не понял. Попробую объяснить не понятно, что подразумевается под тем, что функция QueryPerformanceCounter загружает процессор. Если имеется в виду, что бесконечный цикл, нагружает процессор на 100 процентов, то причем тут QueryPerformanceCounter? Если же то, что на вызов QueryPerformanceCounter затрачивается много времени, то причем тут Sleep?

> «более физические»

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

> моем движке время течет дискретно

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

От автора:

Все равно не понял. Попробую объяснить не понятно, что подразумевается под тем, что функция QueryPerformanceCounter загружает процессор. Если имеется в виду, что бесконечный цикл, нагружает процессор на 100 процентов, то причем тут QueryPerformanceCounter? Если же то, что на вызов QueryPerformanceCounter затрачивается много времени, то причем тут Sleep?
В мейнлупе если останутся только обработчики сообщений — будет нормальная нагрузка на процессор. Если же туда добавить тяжелых функций типа QueryPerformanceCounter — загрузка будет 100%, слип будет давать возможность другим процессам пользоваться процессорным временем. Насчет того, что будет, если убрать только таймер, оставив все остальное в цикле, ничего не могу сказать, не пробовал, и пока что нет возможности попробовать (нахожусь вдали от компилятора), но разницы это не сделает — таймер нужен, и только он уже грузит на 100% (ибо с таймера начинал движок писать и эта проблема как раз и возникла).

в таком случае я вообще не вижу смысла в этом классе, т.к. тебе достаточно синхронизировать кадры, а все остальное будет базироваться на константе дельта t. Вся эта морока, с измерением разницы и т.д. и т.п.
Мы не до конца поняли друг друга: я имел в виду, что отрезки времени дискретны, но имеют разную длину, т.е. событие «через 0.5 секунды» выполняется не через 0.5 секунды (непрерывное время), а через 0.5+х секунд, где х зависит от величины последнего временного отрезка. Например, если временные отрезки будут следующие: 0.1, 0.2, 0.3, то событие выполнится только через 0.6 секунд. Проще говоря, дельта t не константа.
Основы создания игрового движка только для Виндоуз
А зачем делать таймер классом, да ещё и таким? Может понадобиться больше одного таймера? Который, к тому же, делает не самые дешёвые api-вызовы.
От автора:
Я считаю, что в классы нужно упаковывать как объекты, которых будет заведомо несколько, так и объект, который будет точно уникален. Насчет апи-вызовов — точность таймера требует жертв.
Эм… я вижу путаницу в терминологии, давайте разделим мухи и котлеты. Класс — это описание структуры данных и методов для работы с ними и не только. Объект — это конкретная сущность имеющая свой адрес в памяти.

Так вот, в данном случае, если таймеры размножить, то получим не очень оправданный расход памяти — зачем, например в каждой сущности хранить частоту и длину тика? Опять-таки, каждое обращение — это апи-вызов. Лучше сделать отдельно таймер, который опрашивает АПИ, и отдельно логические таймеры, которые будут работать на базе уже полученных на текущем кадре данных.

Если же хочется, чтобы был гарантированно один таймер, то есть смысл объявить все переменные и функции статическими.
А если хочется потратить ещё 4 байта и добавить лишних тактов из-за непрямых вызовов — то сделать «синглтон».

От автора:

Я отличаю классы от объектов, я просто неверно выразился, за что прошу извинить. Уточняю: таймер только один, других копий нет и не предусмотрено. насчет статических переменных и функций — спасибо, приму к сведению, я пока не до конца вник во все тонкости с++, и пользовался лишь простейшими классами да виртуальными методами.
В последнее время что-то особенно много заметок в стиле «как я учил язык такой-то». Дни нубов на хабре.
НЛО прилетело и опубликовало эту надпись здесь
лучше бы написали класс с поддержкой всех основных платформ. Хотя можно просто было взять класс таймера из какого-нибудь Orge. Вот какой способ точного измерения времени для игр лучше использовать на Юниксах?
В OGRE таймер устроен точно так-же как автор описал.
В юниксах обычно используют gettimeofday().
Рекомендую попробовать счастья с Waitable Timer'ом. На мой взгляд, под Виндой это самый надёжный и гибкий способ, когда нужен прецизионный таймер.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации