Перейти к основному содержимому

Мультиплеер

Пусть несколько человек находятся в одной сцене одновременно. Отправляйте сообщения, делитесь значениями, создавайте объекты для всех и показывайте, как плавно двигаются другие игроки.

Эти вызовы работают, только когда в проекте включён мультиплеер и открыта комната. В остальное время они ничего не делают. Поэтому тот же скрипт работает и в предпросмотре на одного человека, и двух версий делать не нужно.

Кто в комнате​

ctx.net.id;        // id этого участника
ctx.net.peers(); // все, кто сейчас в комнате
ctx.net.onJoin((peer) => {});
ctx.net.onLeave((peer) => {});

Что синхронизируется само​

Только положение физических тел с настройкой shared. Всё остальное вы отправляете сами.

Так задумано. Ящик, дверь или мяч должны быть одним объектом, который все видят в одном и том же месте. А персонаж игрока общим быть не должен: иначе, когда идёт один человек, двигаются аватары всех. → кому принадлежит тело

Сообщения​

Сообщения нужны для того, что происходит: кто-то набрал очки, начался раунд, открылась дверь.

ctx.net.send('score', { points: 10 });
ctx.net.on('score', ({ from, data }) => {
/* … */
});

Сообщение уходит всем, кроме вас. Подтверждения в ответ не приходит.

Общее состояние​

Общее состояние нужно для значений, о которых все должны договориться. Это простой словарь, и у каждого есть его копия. Если два человека запишут один и тот же ключ, останется последняя запись.

ctx.net.state.set(ctx.net.id, { name, colour, ready: true });
ctx.net.state.get(somePeerId);
ctx.net.state.all();
ctx.net.state.on(ctx.net.id, (value) => {});

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

Создать объект для всех​

const id = ctx.net.spawn(props.bullet, { position, rotation });
ctx.net.despawn(id);

id вы получаете сразу, а объект появляется чуть позже. id у всех одинаковый.

Удалить объект может только тот, кто его создал. Когда человек уходит из комнаты, созданные им объекты тоже удаляются.

Плавное движение других игроков​

Здесь чаще всего что-то идёт не так, поэтому для этого есть отдельный вызов.

ctx.net.follow(otherPlayerObject, { position, rotation });
ctx.net.unfollow(otherPlayerObject);

Вызывайте follow с каждым полученным положением. Десять–двадцать раз в секунду — это нормально. Дальше объект движется плавно, немного отставая от последнего положения.

Повторный вызов follow с тем же положением ничего не стоит. Так что можно вызывать его каждый кадр с последним положением, которое у вас есть.

Не записывайте полученные положения прямо в трансформацию

Обновления приходят гораздо реже, чем кадры, поэтому объект стоит, а потом прыгает: другие игроки не идут, а двигаются рывками. Плавное приближение к последнему положению тоже не помогает. Между обновлениями двигаться не к чему, поэтому объект останавливается, а потом дёргается.

Схема, которая работает​

Если начинаете с нуля, этой схемы хватит надолго:

  • Каждый игрок управляет своим персонажем — локальным телом, которое двигается от его ввода.
  • Его положение рассылается через net.state или сообщения, десять–двадцать раз в секунду.
  • Персонажи всех остальных двигаются через follow.
  • Объекты мира — общие тела, поэтому физика сама держит их в синхронизации.
  • Счёт и состояние раунда хранятся в net.state, и записывает их тот, кто отвечает за это событие.

Дальше: Решение проблем — что делать, если что-то не работает.