В настоящее время я использую Phaser 3 для представления состояния моего сервера.
Каждый раз, когда мне отправляется игровое состояние сервера, клиент выглядит так:
var t1 = Date.now();
var serverUpdateDelta = 0;
Client.socket.on('usersPool', usersPool => {
// usersPool is an object containing all the user data of sockets connected on the server. Looks something like this:
/*
usersPool = {
"user1234": { x: 0, y: 0, direction: "right", moving: true },
"testuser": { x: 200, y: 250, direction: "down", moving: false }
}
*/
// keeping count of milliseconds between updates (usually around 500m)
serverUpdateDelta = Date.now() - t1;
// for every user connected on the server...
for(id in usersPool) {
let data = usersPool[id]; // this is the user's data
if(/* the player exists as a sprite in the game...*/) {
// THIS IS WHERE THE MAGIC SHOULD HAPPEN
} else {
genSprite(player);
}
}
});
Данные игрока содержат movementQueue
, который является просто массивом координат, в которых был пользователь. Это может выглядеть примерно так:
[
{ x: 0, y: 0, direction: 'down', moving: false },
{ x: 5, y: 0, direction: 'right', moving: true },
{ x: 6, y: 0, direction: 'right', moving: false }
]
Это рассчитывается на сервере, но каждый movementStack
(элемент в движенииQueue`) генерируется каждые 25 миллисекунд или около того на сервере.
Теперь при получении этого движенияQueue задание заключается в интерполяции значений и соответствующем перемещении спрайта ...
Попытка 1
Сначала я попытался создать функцию, которая будет интерполировать после получения обновления, например:
// THIS IS WHERE THE MAGIC SHOULD HAPPEN
// user's state on the client is set to an interpolated version of the one on the server
player.movementQueue = buffer(data.movementQueue);
Буфер просто сгенерирует интерполированный массив на основе serverUpdateDelta и game.loop.actualFps
затем в функции Game.update я запустил следующее:
for(id in spawnedPlayers) {
// this will remove the first movementStack from the queue and returns in
movementStack = spawnedPlayers[id].movementQueue.shift();
// we then take this movementStack and update the user to that position (and play the walking animation)
spawnedPlayers[id].update(movementStack);
}
Таким образом, при каждом игровом цикле мы удаляем стек из очереди и устанавливаем для него пользователя.
Это не сработало. Похоже, что игровой цикл выполнялся намного чаще, чем количество кадров в очереди, из-за чего игрок выглядел так, как будто он двигался на небольшое расстояние очень медленно ... *:
player.movementQueue = player.movementQueue.concat(buffer(data.movementQueue));
Но затем произошло нечто странное, когда игровой цикл не успевал за ходом Queue и игрок двигался очень медленно ...
Попытка 2
Затем я попытался использовать анимацию, которую было бы легко реализовать, просто запустив:
// THIS IS WHERE THE MAGIC SHOULD HAPPEN
_this.tweens.timeline({
targets: player.sprite,
tweens: data.movementQueue, // [{x, y}, {x, y}, {x, y}]
duration: serverDeltaTime/movementQueue.length, // duration between each tween in ms
});
Это сработало ПОЧТИ отлично, за исключением одной маленькой детали:
Раньше мы запускали метод для игрока на каждом стоке движения: player.update(movementStack)
, этот метод принимал бы направление пользователя и соответственно анимировал спрайт. Теперь у нас нет возможности сделать это ...
SO
Какие методы или приемы я могу использовать? Чего мне не хватает? Что я мог реализовать? Я спрашиваю об этом, потому что я застрял в этой точке.
Заранее благодарю за помощь.