Как реализовать детерминированный / тиковый игровой цикл? - PullRequest
0 голосов
/ 04 июля 2018

Прежде всего, я пытаюсь сделать «простую» 3D-игру, используя Three.js, и в будущем какую-то сетевую среду, чтобы сделать ее многопользовательской, так как в будущем я планирую выполнить сетевую часть, я немного искал и обнаружил, что в большинстве «экшн» игр используется игровой цикл, основанный на «тиках», чтобы можно было синхронизировать клиенты и сервер, а затем интерполировать тики, чтобы сделать его гладким.

У меня уже есть некоторый «рабочий» код функции tick (handle input, update, draw), я хочу знать, верна ли моя реализация, и как должен работать этот «детерминированный» цикл, предполагая, что моя реализация работает, когда я увеличиваю «частоту тиков», игра становится быстрее (функция обновления запускается больше раз), верно?

this.loops = 0;
this.tick_rate = 20;
this.skip_ticks = 1000 / this.tick_rate;
this.max_frame_skip = 10;
this.next_game_tick = performance.now();

Эта первая часть кода находится внутри конструктора класса Game

Game.prototype.run = function () {
    this.handle_input();

    this.loops = 0;

    while (performance.now() > this.next_game_tick && this.loops < this.max_frame_skip){
        this.up_stats.update();
        this.update();
        this.next_game_tick += this.skip_ticks;
        this.loops++;
    }

    this.draw();
    //monitor performance
    this.stats.update();

    //next update
    requestAnimationFrame(this.run.bind(this));
};

Полный код по адресу: https://github.com/derezzedex/first_three_js/blob/master/js/game/main.js

1 Ответ

0 голосов
/ 06 июля 2018

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

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

редактировать: немного подробнее ...

Да, он работает одинаково, за исключением того, что this.dt всегда одинаков. т. е. 1000 / желаемый FPS для игрового цикла.

Если вы хотите выполнить сглаживание / интерполяцию между кадрами ... вам также нужно будет записать предыдущее состояние вашего объекта ... и вы, вероятно, не захотите использовать вращения Эйлера, поскольку Эйлер не не интерполировать хорошо. потому что угол 360 градусов, переворачивается на 0, поэтому логика интерполяции становится странной ...

Но вместо этого ... вы можете записать состояние до и после обновления ...

и вместо этого интерполируйте .quaternion .. который при небольших изменениях вращения прекрасно работает только с линейной интерполяцией .. Если изменения слишком велики, вы можете использовать quaternion.slerp (), который может обрабатывать интерполяцию на большие расстояния.

Итак, у вас есть lastTickTime, currentTime и nextTickTime ....

каждый кадр .. вы делаете что-то вроде:

Для интерполяции вы делаете что-то вроде:

var alpha= (currentTime-lastTickTime) / (nextTickTime-lastTickTime);//nextTickTime-lastTickTime = your framerate delta so for 60fps = 1000/60 = 16.666666666666668

var recip = 1.0 - alpha;

object.position.x = (object.lastPosition.x * recip)+(object.nextPosition.x*alpha)
object.position.y = (object.lastPosition.y * recip)+(object.nextPosition.y*alpha)
object.position.z = (object.lastPosition.z * recip)+(object.nextPosition.z*alpha)

object.scale.x = (object.lastScale.x * recip)+(object.nextScale.x*alpha)
object.scale.y = (object.lastScale.y * recip)+(object.nextScale.y*alpha)
object.scale.z = (object.lastScale.z * recip)+(object.nextScale.z*alpha)

object.quaternion.x = (object.lastQuaternion.x * recip)+(object.nextQuaternion.x*alpha)
object.quaternion.y = (object.lastQuaternion.y * recip)+(object.nextQuaternion.y*alpha)
object.quaternion.z = (object.lastQuaternion.z * recip)+(object.nextQuaternion.z*alpha)
object.quaternion.w = (object.lastQuaternion.w * recip)+(object.nextQuaternion.w*alpha)

В правильных трех приложениях вам, вероятно, не следует хранить lastPosition и nextPosition непосредственно на объекте, а вместо этого помещать его в object.userData, но как бы то ни было ... оно, вероятно, все еще будет работать ..

...