Как сделать полную синхронизацию объекта в юнити photon pun

Обновлено: 05.07.2024

Всем привет. Продолжаю изучать фотон. Может-ли кто-то подсказать, допустим у меня есть объект объединяющий в себе другие объекты. (Танк).
Я создаю этот объект через Photon Istantiance. И он отображается у других игроков. Подскажите как отображать еще у других клиентов вращение его башни (т.е его children GOs) ? Если повесить PhotonView-'ничего не произойдет. Неужели надо отдельно спавнить через Photon Instantiance каждый его дочерний элемент, а потом объединять?

Надо писать скрипт сериализации, указывать этот скрипт как observer для PhotonView. Там есть пример с сантехником и передачей его положения и вращения таким способом на сколько я помню.

Грубо примерно так (без интерполяции):

protected void OnPhotonSerializeView ( PhotonStream stream , PhotonMessageInfo info )
if ( stream . isWriting )
stream . SendNext ( transform . position );
stream . SendNext ( transform . rotation );
// передача поворота башни
stream . SendNext ( turretTransform . localRotation );
>
else
transform . position = ( Vector3 ) stream . ReceiveNext ();
transform . rotation = ( Quaternion ) stream . ReceiveNext ();
// прием поворота башни
turretTransform . localRotation = ( Quaternion ) stream . ReceiveNext ();
>
>

При заданном компоненте Network View Вы можете включить синхронизацию состояний State Synchronization, выбрав Reliable Delta Compressed или Unreliable из раскрывающегося меню State Synchronization . После этого, при помощи свойства Observed (предмет наблюдений) вы должны выбрать тип данных, которые будут синхронизироваться.

Юнити может синхронизировать компоненты Transform, Animation, Rigidbody и MonoBehaviour.

Transforms сериализуются, сохраняя position (позицию), rotation (вращение) и scale (масштаб). Информация о наследовании не передаётся по сети.

Animation сериализует состояние каждой запущенной анимации, такие как time (время), weight (вес), speed (скорость) и включенные свойства.

Rigidbody сериализует position (позицию), rotation (вращение), velocity (скорость) и angular velocity (угловую скорость).

Скрипты (MonoBehaviours) вызывают функцию OnSerializeNetworkView().

Надежность и пропускная способность

Компоненты Network View на данный момент поддерживают два способа передачи данных с разными уровнями надежности: Reliable Delta Compressed и Unreliable.

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

Для большей информации о минимизации сетевого трафика, изучите Minimizing Bandwidth page.

Reliable Delta Compressed

Режим Reliable Delta Compressed (надежная сжатая передача изменений) автоматически будет сравнивать последние полученные клиентом данные с текущим состоянием. Если никаких изменений данных с последнего обновления не было, никаких данных передаваться не будет. Однако, данные будут сравниваться по каждому свойству. Например, если position (позиция) Transform изменилась, а rotation (вращение) - нет, то только position будет передан по сети. Передавая только изменяющиеся данные, вы сохраняете сетевой трафик.

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

Unreliable

В режиме Unreliable (ненадёжный), Юнити будет отправлять пакеты, не проверяя, что они были получены. Это означает, что неизвестно какая информация была получена, так что не безопасно отправлять только измененные данные - будут отправляться все состояния целиком при каждом обновлении.

Как решить, какой метод использовать

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

В общем, вам следует использовать ненадежную Unreliable отправку пакетов, когда быстрые частые обновления важнее пропущенных пакетов. Если же данные, напротив, меняются нечасто, вы можете использовать Reliable Delta Compression, чтобы сохранить пропускную способность.

Прогнозирование

В случаях, когда сервер обладает полной властью full authority над состоянием мира, клиенты изменяют состояние игры только в соответствии с обновлениями, которые они получают от сервера. Одна из проблем такого подхода в том, что задержка, обусловленная ожиданием ответа от сервера, может оказывать влияние на ход игры (геймплей). Например, если игрок нажмёт кнопку, чтобы пойти вперёд, в действительности он не сдвинется с места, пока не получит от сервера обновленное состояние. Задержки зависят от задержек соединения, так что чем хуже связь, тем менее отзывчивой становится система управления.

Одним из возможных решений этой проблемы является прогнозирование на стороне клиента Client-side Prediction , означающее, что клиент прогнозирует ожидаемое обновление движения от сервера, используя приблизительно такую же внутреннюю модель. Так что игрок незамедлительно реагирует на ввод, но сервер видит его позицию из последнего обновления. Когда, наконец-то, обновление состояния приходит от сервера, клиент сравнивает свой прогноз с тем, что в действительности произошло. Они могут различаться, так как сервер может знать больше о том, что происходит вокруг игрока, а клиент знает только то, что ему положено знать. Ошибки в прогнозах исправляются, как только случаются и если проводится постоянная корректировка, то результат будет более гладким и менее заметным.

Навигационное счисление или интерполяция/экстраполяция

Возможно применение базовых принципов прогнозирования на стороне клиента по отношению к оппонентам игрока. Экстраполяция Extrapolation это процесс сохранения нескольких последних известных значений position (позиции), velocity (скорость) и направления оппонента, после чего эти данные используются для прогнозирования, где он появится в ближайшем будущем. Когда придёт следующее обновление состояния с корректировкой позиции, состояние клиента будет обновлено с этой точной информацией, что может привести к внезапным скачкам персонажа, если прогнозирование было неудачным. В FPS играх поведение игроков может быть весьма непредсказуемым, следовательно описанный тип прогнозирования имеет ограниченную область применения. Если сетевые задержки становятся достаточно большими, оппонент будет совершать сильные скачки в соответствии с накоплением ошибки прогнозирования.

Интерполяция Interpolation может использоваться, когда пакеты теряются по пути к клиенту. Обычно это вызывает остановку движения NPC, после чего прыжок на новое место, как только пакет в конце концов приходит. Задерживая состояние мира на некое значение времени (примерно 100 мс) и интерполируя между последней известной позицией и новой, можно сделать движение между этими двумя точками гладким, даже если пакет потеряется.

Я долго думал о чём можно написать статью, бороздил просторы интернета и почти не нашёл русских статей по созданию мультиплеера в unity3d по средства Photon Unity Network.
В данной статье мы сделаем подключение к photon и я расскажу о комнатах, лобби, и о самом Photon cloud. И так, приступим.
Что же такое Photon cloud? На этот вопрос я постараюсь дать вам краткий, но понятный ответ.

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

Лобби.
Лобби хранит в себе номера комнат на главном сервере. Photon unity network автоматически присоединяется к этому лобби и получает список комнат, позже уже присоединяется к ним. Лобби не обязательно иметь, можно подключиться к случайной комнате или к конкретной комнате, если вы знаете её номер.
Комната.
Комната – место, где собираются игроки, она имеет характеристики (например, вы можете выставить ограничение игроков, её номер и прочее). Весь список комнат с их номерами находится в лобби, и вы можете подключаться к конкретной комнате непосредственно из лобби. Но можно и подключиться к случайной комнате, тут решать вам.

Подключение к фотону.

Сперва нам нужно подключиться к фотону, для этого мы используем функцию PhotonNetwork.ConnectUsingSettings() ,которая позволит нам подключиться к фотону и указать версию нашей игры.
Теперь создаём новый скрипт, называем его любым удобным вам именем, я назову его “Connect”. Содержимое скрипта будет выглядеть так:

Впрочем, тут нет ничего сложно, что нужно объяснять тут мы просто подключаемся к фотону, и задаём версию нашей игры, в моём случае это: "1.0 TestGame".

Далее, для удобности мы будем выводить весь процесс в GUI. Для этого в этом же скрипте вызовем метод OnGUI() и выведем через него процесс нашего подключения.

Далее мы проверяем - подключились ли мы к лобби, если да, то мы подключаемся к случайной комнате. Код будет выглядеть следующим образом:

Метод OnJoinedLobby() вызывается после того, как мы подключились к лобби. Мы сразу выводим в лог, что мы подключились к лобби, это для удобности. Во второй строчке мы заходим в случайную комнату из списка комнат фотона, но комнат может и не быть вообще, тогда мы должны будем её создать. Для этого используем метод OnPhotonRandomJoinFailed(), он будет срабатывать если мы не зашли в случайную комнату, далее в самом методе мы выводим в лог то, что мы не смогли зайти в комнату и создаём комнату. Код будет выглядеть так:

Теперь немного подробнее о создании комнаты. Первый параметр – уникальное имя комнаты, второй – видима или невидима комната, третий – закрыта или открыта комната, четвёртый – максимальное количество игроков.
Теперь мы создали комнату, нам нужно проверить : подключились ли мы к ней, для этого есть метод OnJoinedRoom() и мы выведем в лог, что мы удачно вошли в комнату, на этом первая статья подойдёт к концу, во второй я расскажу об установке игрока на сцену и сделаем ему базовое управление.

В нашем многопользовательском проекте на Unity каждый пользователь импортирует модель во время выполнения, затем присоединяется к комнате и затем настраивает импортируемый объект времени выполнения как сетевой объект. Проблема в том, что импортированная модель уничтожается при выходе из хоста. Вот мой фрагмент кода:

Как избежать разрушения объекта в других системах при выходе из хоста?

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

Читайте также: