Контроль последовательности спрайтов через DeltaTime
Ранее в моем основном цикле игры время было установлено на уровне 60 кадров в секунду и соответствующей задержки для временной задержки.
Последовательность Спрайта была анимирована следующим образом:
<pre>
if(++ciclos > 10){
siguienteSprite++;
ciclos = 0;
}
</pre>
Учитывая, что я использую Smooth Motion с DeltaTime, поэтому я исключил задержку из основного цикла; Делая это, циклы анимации спрайтов быстрее, и не только это, но также и время между каждой последовательностью меняется.
Кто-то может мне помочь, только с логикой этой проблемы, заранее спасибо.:)
1 ответ
delay
в основном цикле не очень хороший способ для этого (так как он не учитывает время, затрачиваемое другими вещами в основном цикле). Когда вы удаляете задержку, тогда скорость увеличивается и изменяется больше, потому что другие элементы в синхронизации основного цикла более значительны и обычно не постоянны по многим причинам, таким как:
- Зернистость ОС
- синхронизация с картой / драйвером gfx
- непостоянное время обработки
Есть больше способов, как справиться с этим:
измерять время
<pre> t1=get_actual_time(); while (t1-t0>=animation_T) { siguienteSprite++; t0+=animation_T; } // t0=t1; // this is optional and change the timing properties a bit </pre>
где
t0
некоторая глобальная переменная, содержащая "последнее" измеренное время изменения спрайта.t1
фактическое время иanimation_T
постоянная времени между изменениями анимации. Для измерения времени вам нужно использовать OS API какPerformanceCounter
на окнах илиRDTSC
в asm или любой другой, вы получили под рукой, но с достаточно маленьким разрешением.Таймер ОС
просто увеличить
siguienteSprite
в каком-то таймере сanimation_T
интервал. Это просто, но таймеры ОС не точны и обычно составляют около 1 мс или более + степень детализации ОС (аналогичноSleep
точность).Таймер потока
Вы можете создать один поток для целей синхронизации, например, что-то вроде этого:
for (;!threads_stop;) { Delay(animation_T); // or Sleep() siguienteSprite++; }
Не забудь этого
siguienteSprite
должно бытьvolatile
и буферизуется во время рендеринга, чтобы избежать мерцания и / или ошибок нарушения доступа. Этот подход немного более точен (если только у вас нет одноядерного процессора).Вы также можете увеличить некоторую переменную времени и использовать ее в качестве фактического времени в своем приложении с любым разрешением, которое хотите. Но будьте осторожны, если
delay
не возвращает управление процессором в ОС, тогда этот подход будет использовать ваш процессор для100%/CPU_cores
, Для этого есть средство, которое заменяет вашdelay
с этим:Sleep(0.9*animation_T); for (;;) { t1=get_actual_time(); if (t1-t0>=animation_T) { siguienteSprite++; t0=t1; break; }
Если вы используете измеренное время, вы должны справиться с переполнением (t1<t0)
потому что любой счетчик будет переполнен со временем. Например, используя 32-битную часть RDTSC на 3.2 GHz
Ядро процессора будет переполняться каждый 2^32/3.2e9 = 1.342 sec
так что это реальная возможность. Если у меня хорошо работает память, то счетчики производительности в Windows обычно бегают 3.5 MHz
на старых системах ОС и около 60-120 MHz
на более новых (по крайней мере, в прошлый раз, когда я проверяю), и они 64-битные, так что переполнения не являются большой проблемой (если вы не запускаете 24/7). Также в случае использования RDTSC вы должны установить привязку процесса / потока к одному ядру ЦП, чтобы избежать проблем с синхронизацией на многоядерных ЦП.
Я выполнил свою часть сравнительного анализа и усовершенствовал синхронизацию с высоким разрешением на низком уровне за эти годы, поэтому вот несколько связанных с ними QA:
неправильные измерения тактового цикла с rdtsc - ОС Granularity
Измерение задержек кэша - измерение частоты процессора
Оценка размера кэша в вашей системе? - PerformanceCounter
пример
Вопросы по измерению времени с использованием тактовой частоты процессора - PIT как альтернативного источника синхронизации