Это мой код игрового шлейфа:
while (shouldUpdate)
{
timeSinceLastUpdate = 0;
startTime = clock();
while (timeAccumulator >= timeDelta)
{
listener.handle();
em.update();
timeAccumulator -= timeDelta;
timeSinceLastUpdate += timeDelta;
}
rm.beginRender();
_x->draw();
rm.endRender();
timeAccumulator += clock() - startTime;
}
Он работает почти идеально, но у него есть некоторое дрожание: несколько раз в секунду вместо _x (тестовая сущность, которую все, что он делает в обновлении — это x ++), перемещается на 1 пиксель вправо, фактически он перемещается на 2 пикселя вправо и это заметный эффект задержки / дрожания. Я предполагаю, что часы () недостаточно точны. Итак, что я мог сделать, чтобы улучшить этот игровой цикл?
Если это имеет значение, я использую SDL и SDL_image.
РЕДАКТИРОВАТЬНичего не изменилось после выполнения чего-то более точного, чем часы. НО, что я понял, это то, что все это благодаря TimeDelta. Вот как была определена timeDelta, когда я сделал этот пост:
double timeDelta = 1000/60;
но когда я определил это как что-то еще во время возни …
double timeDelta = 16.666666;
Я заметил, что первые несколько секунд игра началась, она была гладкой, как масло. Но всего через несколько секунд игра сильно заикалась, а затем снова стала гладкой и повторилась. Чем больше 6 (или что-либо после.), Которое я добавил, тем дольше игра была изначально гладкой и тем сложнее отставание, когда это происходило. Кажется, плавающие ошибки атакуют. Так что я могу сделать тогда?
EDIT2: я столько всего перепробовал, теперь это даже не смешно … может кто-нибудь помочь мне с математической частью цикла? Так как это то, что вызывает это …
РЕДАКТИРОВАТЬ 3: Я отправил тестовую программу для некоторых людей, некоторые сказали, что она была совершенно гладкой, а другие сказали, что это было нервно, как я описал. Для тех, кто хотел бы проверить это здесь (src): https://www.mediafire.com/?vfpy4phkdj97q9j
EDIT4: Я изменил ссылку на исходный код.
Это почти наверняка будет из-за точности clock()
Используйте либо std::chrono
или же SDL_GetTicks()
измерять время с эпохи.
Я бы порекомендовал использовать std::chrono
только потому, что я предпочитаю C ++ API, вот пример:
int main(){
using clock = std::chrono::high_resolution_clock;
using milliseconds = std::chrono::milliseconds;
using std::chrono::duration_cast;
auto start = clock::now(), end = clock::now();
uint64_t diff;while(running){
diff = duration_cast<milliseconds>(end - start).count();
start = clock::now();
// do time difference related things
end = clock::now();
}
}
Чтобы обновлять только после указанной дельты, вы должны сделать свой цикл следующим образом:
int main(){
auto start = clock::now(), end = clock::now();
uint64_t diff = duration_cast<milliseconds>(end - start).count();
auto accum_start = clock::now();
while(running){
start = clock::now();
diff = duration_cast<milliseconds>(end - start).count();
if(duration_cast<nanoseconds>(clock::now() - accum_start).count() >= 16666666){
// do render updates every 60th of a second
accum_start = clock::now();
}
end = clock::now();
}
}
start
а также end
будут оба типа std::chrono::time_point<clock>
где clock
был ранее определен нами как std::chrono::high_resolution_clock
,
Разница между 2 time_point
будет std::chrono::duration
которые могут быть наносекундами, миллисекундами или любой другой единицей, которая вам нравится. Мы приводим это к миллисекундам, затем получаем count()
назначить на наш uint64_t
, Вы можете использовать другой целочисленный тип, если хотите.
мой diff
как вы должны рассчитывать свой timeDelta
, Вам следует не установить его как константу. Даже если вы уверены, что это будет правильно, вы ошибаетесь. Каждый кадр будет иметь различную дельту, даже если это наименьшая доля секунды.
Если вы хотите установить постоянную разницу кадров, используйте SDL_GL_SetSwapInterval установить вертикальную синхронизацию.
Только для тебя я создал этот пример git repo. Обратите внимание на main.cpp
где я умножить на diff
чтобы получить скорректированную разницу на кадр. Эта корректировка (или, отсутствие), где вы получаете свою нервозность.