А расскажите пожалуйста, как вы реализовали Client-Side Prediction/Server Reconciliation, какие есть проблемы синхронизации игроков (с разным пингом), и как вы их решали. Есть ли это в решении Mirror или писали сами. В общем, интересует техническая часть мультиплеера на Unity :)
Лет пять назад пытались делать мультиплеер в игре на Unity, как мы тогда бомбили от того, что библиотек много, но нигде не было решения просто подключи и играй, без лагов, телепортаций и прочих проблем мультиплеера. А в самом движке есть какие-то механизмы, но тоже не работают без дополнительного кода (может сейчас изменилось). Так и не сделали...
Да, думаю что с тех времён многое кардинально поменялось. Если честно, я не до конца понимаю, что конкретно вы имеете в виду под Client-Side Prediction/Server Reconciliation в кооперативном мультиплеере типа "Хост-Клиент", но как я могу предположить - это ряд мер, которые нужно предпринять, чтобы на стороне удалённых клиентов всё работало и выглядело плавно при условии, что ключевая логика обрабатывается на хосте. Т.е. всё, что касается ввода, передвижения персонажа и т.д.
Начнём с интерполяции. По-моему ещё года полтора назад в Mirror, например, не было нормальной интерполяции передвижения. Сейчас она есть и очень крутая.
Коротко об интерполяции. Если пинг в игре составляет 100 ms, это означает, что клиент получает 10 сетевых пакетов в секунду. Если мы перемещаем персонажа только тогда, когда он получает пакет - то его движение будет дёрганым, как будто у нас в игре 10 FPS. Интерполяция передвижения - это предсказание направления движения персонажа на клиенте. Таким образом, пакетов по-прежнему приходит 10 в секунду, но на стороне клиента всё работает плавно. Например, если вы играете в Albion Online, ваш персонаж куда-то бежит и в этот момент вас резко убивают - ваш персонаж резко отбросится немного назад. К своему настоящему местоположению на сервере.
Так вот, в Mirror - интерполяция на данный момент работает очень плавно и реализуется автоматически при добавлении на сетевой объект компонента NetworkTransform.
Также в Final Foe есть ряд механик, в которых клиент сразу должен получать отклик, не дожидаясь ответа от хоста.
Например, при добивании мобов - моб должен превращаться в рэгдолл и откидываться сразу, не дожидаясь ответа от хоста. У хоста, понятное дело, это происходит сразу по умолчанию. Но то, как это будет происходить у удалённого клиента - зависит от того, как будет устроена сетевая логика.
Т.к. Final Foe - это не соревновательная ММО-игра, а кооператив для друзей - у меня в данном случае больше свободы действия. Например, я могу реализовывать ключевую логику не только на сервере, но и на клиенте. В данном случае - если игрок-клиент наносит монстру смертельный урон, то он не посылает запрос на сервер мол "Я его точно убил? Можно превратить этого монстра в рэгдолл?". Он сразу отправляет его в рэгдолл, потому что нанесение урона сначала обрабатывается на клиенте. А потом на сервер отправляется уведомительная информация о нанесённом уроне. И сервер синхронизирует нанесение урона и смерть этого моба для всех остальных клиентов, включая самого хоста.
Такая схема позволяет всем смертям монстров на удалённом клиенте выглядеть плавно, без задержек.
Вообще в Final Foe довольно много подобных вещей. Если вы посмотрите видеоролик из данной статьи - вы увидите, что там есть маг в белой мантии. Он создаёт различные предметы и убивает ими противников. Так вот, эти предметы синхронизируются не с помощью NetworkTransform + интерполяция. Мне нужно было, чтобы на всех клиентах, этот созданный предмет был абсолютно точно привязан к магу и следовал бы за ним постоянно. Интерполяция не даёт такой абсолютной плавности и точности позиции и ротации. Это было реализовано по-другому. Клиент, играющий за мага, передаёт всем остальным игрокам данные мага и данные предмета. И на каждом клиенте этот предмет привязывается к этому магу. И его следование за этим магом обсчитывается на клиенте, а не синхронизируется по сети. У предметов в Final Foe вообще нет компонента NetworkTransform.
Сообщение получилось длинным. Надеюсь, я ответил на ваш вопрос)
А расскажите пожалуйста, как вы реализовали Client-Side Prediction/Server Reconciliation, какие есть проблемы синхронизации игроков (с разным пингом), и как вы их решали. Есть ли это в решении Mirror или писали сами. В общем, интересует техническая часть мультиплеера на Unity :)
Лет пять назад пытались делать мультиплеер в игре на Unity, как мы тогда бомбили от того, что библиотек много, но нигде не было решения просто подключи и играй, без лагов, телепортаций и прочих проблем мультиплеера. А в самом движке есть какие-то механизмы, но тоже не работают без дополнительного кода (может сейчас изменилось). Так и не сделали...
Да, думаю что с тех времён многое кардинально поменялось. Если честно, я не до конца понимаю, что конкретно вы имеете в виду под Client-Side Prediction/Server Reconciliation в кооперативном мультиплеере типа "Хост-Клиент", но как я могу предположить - это ряд мер, которые нужно предпринять, чтобы на стороне удалённых клиентов всё работало и выглядело плавно при условии, что ключевая логика обрабатывается на хосте. Т.е. всё, что касается ввода, передвижения персонажа и т.д.
Начнём с интерполяции. По-моему ещё года полтора назад в Mirror, например, не было нормальной интерполяции передвижения. Сейчас она есть и очень крутая.
Коротко об интерполяции. Если пинг в игре составляет 100 ms, это означает, что клиент получает 10 сетевых пакетов в секунду. Если мы перемещаем персонажа только тогда, когда он получает пакет - то его движение будет дёрганым, как будто у нас в игре 10 FPS. Интерполяция передвижения - это предсказание направления движения персонажа на клиенте. Таким образом, пакетов по-прежнему приходит 10 в секунду, но на стороне клиента всё работает плавно. Например, если вы играете в Albion Online, ваш персонаж куда-то бежит и в этот момент вас резко убивают - ваш персонаж резко отбросится немного назад. К своему настоящему местоположению на сервере.
Так вот, в Mirror - интерполяция на данный момент работает очень плавно и реализуется автоматически при добавлении на сетевой объект компонента NetworkTransform.
Также в Final Foe есть ряд механик, в которых клиент сразу должен получать отклик, не дожидаясь ответа от хоста.
Например, при добивании мобов - моб должен превращаться в рэгдолл и откидываться сразу, не дожидаясь ответа от хоста. У хоста, понятное дело, это происходит сразу по умолчанию. Но то, как это будет происходить у удалённого клиента - зависит от того, как будет устроена сетевая логика.
Т.к. Final Foe - это не соревновательная ММО-игра, а кооператив для друзей - у меня в данном случае больше свободы действия. Например, я могу реализовывать ключевую логику не только на сервере, но и на клиенте. В данном случае - если игрок-клиент наносит монстру смертельный урон, то он не посылает запрос на сервер мол "Я его точно убил? Можно превратить этого монстра в рэгдолл?". Он сразу отправляет его в рэгдолл, потому что нанесение урона сначала обрабатывается на клиенте. А потом на сервер отправляется уведомительная информация о нанесённом уроне. И сервер синхронизирует нанесение урона и смерть этого моба для всех остальных клиентов, включая самого хоста.
Такая схема позволяет всем смертям монстров на удалённом клиенте выглядеть плавно, без задержек.
Вообще в Final Foe довольно много подобных вещей. Если вы посмотрите видеоролик из данной статьи - вы увидите, что там есть маг в белой мантии. Он создаёт различные предметы и убивает ими противников. Так вот, эти предметы синхронизируются не с помощью NetworkTransform + интерполяция. Мне нужно было, чтобы на всех клиентах, этот созданный предмет был абсолютно точно привязан к магу и следовал бы за ним постоянно. Интерполяция не даёт такой абсолютной плавности и точности позиции и ротации. Это было реализовано по-другому. Клиент, играющий за мага, передаёт всем остальным игрокам данные мага и данные предмета. И на каждом клиенте этот предмет привязывается к этому магу. И его следование за этим магом обсчитывается на клиенте, а не синхронизируется по сети. У предметов в Final Foe вообще нет компонента NetworkTransform.
Сообщение получилось длинным. Надеюсь, я ответил на ваш вопрос)