Рекомендации по работе с визуальными скриптами сетки для сети
Обзор
В Сетке большинство свойств сцен по умолчанию автоматически совместно используются для всех клиентов, подключенных к одной комнате. Например, позиция преобразования и поворот объекта сцены, состояние включенного компонента или текст TextMeshPro.
Как правило, свойства компонентов и переменные объекта, имеющие следующие типы значений, автоматически разделяются по умолчанию:
- Логические, целые числа, float и String
- Цвет
- Прямоугольник
- Vector 2, Vector 3 и Vector 4
- Кватернион
- Матрица 4x4
Типы коллекций (списки и наборы) и ссылки на объекты сцены не являются общими.
Узлы визуального скрипта, обращаюющиеся к свойствам или изменяющие свойства в сетке, помечены меткой, которая указывает, является ли они общими для всех клиентов или "Локальный для этого клиента":
Переменные объектов используются по умолчанию, а также, если вы объявили их одним из типов значений, перечисленных выше:
Сетка не поддерживает переменные сцены, но вы можете использовать автономные компоненты переменных в среде для размещения переменных , которые можно совместно использовать независимо от любого конкретного компонента компьютера скрипта .
Если вы не хотите автоматически предоставлять общий доступ к свойствам или переменным объекта, можно добавить компонент области локального скрипта в сцену. Это сделает все свойства сцены и переменные скрипта для этого игрового объекта и любого из его потомков локальными.
Совет. Вы можете увидеть несколько примеров использования компонента области локального скрипта в главе 3 руководства по Сетке 101, в котором основное внимание уделяется визуальному сценарию.
Для локальных переменных скрипта, которые используются только в одном компьютере скриптов, лучше всего использовать переменные Graph, которые никогда не используются между клиентами сеткой.
Совместное использование с помощью визуального скрипта сетки обеспечивает следующие гарантии:
Гарантированная согласованность: все клиенты в конечном итоге будут поступать в одно и то же общее состояние.
Гарантированная атомарность для каждого компонента: все обновления свойств одного компонента сцены (или одного компонента переменных ) в одном обновлении будут применяться атомарно к каждому клиенту.
Тем не менее
Нет гарантии заказа: обновления, примененные одним клиентом к нескольким различным компонентам сцены, могут поступать в разные заказы на разных клиентах.
Нет гарантии времени: Сетка попытается реплицировать изменения состояния между клиентами как можно быстрее, но сетевые условия могут отложить поступление любого обновления состояния на некоторых или всех клиентах.
Нет гарантии детализации: любой клиент не может видеть все отдельные добавочные обновления для общего состояния. Это может произойти, когда сетевые условия принудит сервер Сетки к ограничению скорости обновлений. Это также происходит, когда клиент поздно присоединяется к комнате.
Состояние — это события, не связанные с общим доступом
Вы не можете отправлять или получать явные сетевые сообщения с помощью визуального скрипта сетки. Это может быть удивительно сначала, но это помогает установить сетевую парадигму, которая позволяет легко обрабатывать изменения среды выполнения, а также поздно присоединение. Вместо сообщений в свойствах сцены и переменных скрипта есть общее состояние .
Скрипты могут отвечать на обновления общего состояния в едином режиме независимо от того, были ли эти обновления локальным скриптом или пользователем, другим клиентом, который делится опытом в той же комнате или другими клиентами, которые уже были в комнате, прежде чем вы даже присоединились к нему самостоятельно.
Невозможно явно отправлять сетевые сообщения означает, что вам придется начать думать о общем состоянии, которое получает обновления вместо общих событий, вызывающих обновления состояния. Общие события являются следствием обновления общего состояния, а не наоборот.
К счастью, визуальное скриптирование сетки упрощает реагирование на обновления состояния для визуальных скриптов. Используйте узел события On State Change и подключите входные данные слева с любой переменной скрипта или свойством компонента, которые вы хотите наблюдать за изменениями, и узел событий активирует скрипт (подключенный к правой части) всякий раз, когда любые переменные или свойства, подключенные к нему, изменяют их значение.
Это работает с общим состоянием, а также с локальным состоянием. Событие On State Changed активируется независимо от того, изменились ли переменные или свойства, которые он наблюдает, были изменены локальным клиентом, удаленным клиентом или даже удаленным клиентом до того, как локальный клиент даже присоединился к комнате.
Использование on State Changed для реагирования на изменения состояния эффективно: нет простой пропускной способности или затрат на производительность. Вы можете пассивно прослушивать любое количество визуальных скриптов для обновления состояния таким образом, не влияя на частоту кадров или использование пропускной способности среды.
Позднее присоединение
Позднее присоединение происходит, когда клиент присоединяется к комнате, к ней уже подключены другие клиенты.
При последнем присоединении Сетка получает текущее состояние комнаты от сервера, например, кто уже находится в комнате и где их аватары находятся в настоящее время и быстро подготавливает локальную версию клиента присоединения клиента, чтобы она соответствовала состоянию, совместно используемому всем в комнате.
В значительной части визуальное скриптирование сетки выполняет то же самое. Все свойства общих компонентов и переменные визуального скрипта, которые были изменены в комнате до того, как клиент только что присоединился, обновляются локально, чтобы соответствовать общему состоянию, а затем активируются все узлы событий On State Changed , наблюдающие за этими свойствами или переменными.
Поздние соединения не повторяют общие события- они получают общее состояние.
С точки зрения локального клиента среда всегда развивается с исходного состояния, которое оно было сразу после загрузки сцены, которую вы отправили в Сетку. В случае позднего присоединения первое изменение состояния может быть больше, чем то, что происходит, пока локальный пользователь взаимодействует с комнатой в текущем сеансе, но это точно то же самое в принципе.
Все это происходит, как среда загружается до того, как она даже исчезает из черного. Как только пользователь на самом деле может видеть и взаимодействовать с средой, последнее присоединение уже выполнено.
Создание локального состояния следовать общему состоянию
Очень часто пользователь может наблюдать "общее состояние" в среде— это сочетание состояния, совместно используемого сеткой и локальным состоянием, которое было установлено визуальными скриптами в ответ на событие, которое произошло в комнате. Например, когда пользователь перевернул переключатель в среде (общее состояние), визуальный скрипт может изменить цвет skybox (локальное состояние). Вы можете заманчиво применить локальное изменение (обновить цвет skybox) непосредственно в ответ на взаимодействие пользователя с коммутатором. Тем не менее, даже если событие взаимодействия происходит на всех клиентах в настоящее время в комнате, любой клиент, который присоединяется к комнате позже, не получит это событие просто потому, что они не были там, когда это произошло. Вместо этого следует сделать локальное состояние следующим образом:
- Когда пользователь взаимодействует (например, переворачивает переключатель), сделайте это событие локальным событием, которое обновляет общую переменную (например, состояние включено или выключение коммутатора).
- Используйте параметр On State Changed для наблюдения за общей переменной.
- Когда триггеры события On State Change (так как общая переменная изменила значение), примените любое локальное изменение (например, обновите цвет skybox).
Таким образом, локальное состояние (цвет skybox) соответствует общему состоянию (состоянию коммутатора). Что хорошо об этом заключается в том, что он работает без изменений для локального клиента, который перевернул коммутатор, для всех остальных удаленных клиентов, присутствующих в комнате одновременно, и для всех будущих клиентов, которые присоединятся к комнате позже.
Сделайте локальное состояние общим состоянием: рекомендации
Локальные события: например, узел события "Изменено состояние", наблюдающий за свойством "Выбран локально" компонента "Взаимодействие сетки":
- 🆗 Может изменить локальное состояние, которое является частным для клиента. Эти изменения состояния останутся строго на локальном клиенте, и они будут исчезнуты, когда клиент покидает сеанс.
- 🆗 Может изменить общее состояние.
- ❌Не удается изменить локальное состояние для согласованности между клиентами. Локальное событие выполняется только на одном клиенте, поэтому обновления, необходимые для поддержания согласованности локального состояния между клиентами, просто не будут происходить на любом другом клиенте.
Общие события: например, узел события On Trigger Enter , подключенный к общему триггеру физики, коллайдер:
- 🆗 Может изменить локальное состояние для моментарных эффектов: например, эффект частиц или короткий звуковой эффект. Только клиенты, присутствующих в комнате, когда происходит общее событие, смогут увидеть локальный эффект; все клиенты, присоединенные к комнате позже, не будут.
- ❌Не удается изменить локальное состояние для согласованности между клиентами. Общее событие выполняется только на клиентах, которые присутствуют во время его возникновения, но они не будут воспроизводиться для клиентов, которые присоединяются к сеансу позже.
- ⛔ Не следует изменять общее состояние. Так как общее событие выполняется на всех клиентах, все это делается всеми клиентами очень близко во времени. В зависимости от характера изменения может повторяться несколько раз (например, счетчик оценки может увеличиваться несколькими в ответ на одно событие).
В событиях изменения состояния, которые наблюдают общее состояние в общих переменных или свойствах общего компонента:
- 🆗 Может изменить локальное состояние на соответствие общему состоянию для клиентов. Чтобы это хорошо работало в повторяющемся и согласованном способе для всех клиентов, необходимо перевести каждое возможное новое значение наблюдаемого общего состояния в локальное состояние, а не только несколько переходов состояния с вишней (например , выбрано значение true).
Сделайте локальное состояние следующим общим состоянием: Пример
В этом примере в этой среде есть две интерактивные кнопки: одна с меткой "Звезда", другая с меткой "Sponge". Выбор любой из кнопок должен выполнять две действия:
- Сохраните соответствующую метку в общей строковой переменной ObjectKind.
- Сохраните ссылку на соответствующий объект сцены в локальной эталонной переменной GameObject с именем ObjectRef.
Ниже приведены два потока скрипта, по одному для каждой кнопки. Каждый прослушивает общее свойство "Выбрано" компонента "Сетка", взаимодействующего с сеткой, и обновляет ObjectKind и ObjectRef в зависимости от выбранной кнопки:
Все, кажется, работает нормально, но только для пользователей, которые уже находятся в комнате, когда выбран один из кнопок. Любой пользователь, присоединяющийся к сеансу позже, находит несогласованное состояние в локальной версии общей среды: только ObjectKind правильно задан в соответствии с последней выбранной кнопкой, но ObjectRef остается null.
Что не так с этими двумя потоками скриптов?
Во-первых, обратите внимание, что эти потоки скриптов активируются общим событием, так как они прослушивают общее свойство кнопки Is Selected . Это, кажется, имеет смысл, потому что это единственный способ получить локальную переменную ObjectRef для обновления на всех клиентах.
Тем не менее
- Общие события не должны изменять общее состояние, но эти потоки скриптов обновляют общую переменную ObjectKind .
- Общие события не могут изменить локальное состояние, чтобы быть согласованным между клиентами , но эти потоки скриптов обновляют локальную переменную ObjectRef , которую мы планируем обеспечить согласованность для всех клиентов, как и ObjectKind.
Таким образом, как это настроено в настоящее время, мы на самом деле не должны делать ни один из элементов, которые нам нужны кнопки.
Единственный очевидный способ выйти из этой проблемы заключается в том, чтобы сделать события, которые активируют эти потоки локальными. Это можно сделать, сделав узел события On State Changed наблюдать за свойством Is Selected Locally, а не "Выбрано".
С событием, которое теперь является локальным, это означает...
- Локальные события могут изменять общее состояние , чтобы теперь можно было безопасно обновить общую переменную ObjectKind , и ее значение будет автоматически использоваться для клиентов встроенными сетями Visual Scripting сетки.
- Локальные события не могут изменить локальное состояние, чтобы обеспечить согласованность между клиентами , поэтому мы по-прежнему не можем обновить локальную переменную ObjectRef в этих потоках скриптов. Нам придется найти другой способ.
Вот как два потока скрипта выглядят после этих изменений:
Что можно сделать, чтобы задать локальную переменную ObjectRef , чтобы она оставалась согласованной с этой? К счастью, эти два потока скрипта уже устанавливают общее состояние, которое можно выполнить: общая переменная ObjectKind . Все, что необходимо сделать, — использовать событие On State Changed , которое наблюдает эту переменную и обновляет локальную переменную ObjectRef в зависимости от его значения:
Это прекрасный способ сделать это, так как события изменения состояния, наблюдающие за общим состоянием, могут изменить локальное состояние на соответствие с ним. Это будет работать для клиента, который нажимал кнопку, для всех остальных клиентов, присутствующих в одной комнате одновременно, и для всех клиентов, которые присоединятся к сеансу позже.
Сетевые ловушки
Высокочастотные общие обновления
Почти все состояние сцены по умолчанию разделяется визуальным скриптом сетки. Это отлично подходит для совместного использования, но он также может подкрадыться случайно и вызвать ненужную сетевую нагрузку. Например, следующий поток скрипта потопит сеть избыточными обновлениями поворота преобразования. Однако так как все клиенты выполняют его одновременно, ни одно из удаленных обновлений никогда не будет иметь фактическое влияние на любой клиент локально:
В этом случае, вероятно, следует использовать область локального скрипта, чтобы компонент преобразования был локальным для каждого клиента. Кроме того, для начала следует использовать компонент аниматора , а не поток скрипта On Update .
Панель диагностики визуального скрипта сетки и Анализатор производительности содержимого (CPA) в наборе средств Сетки 5.2411 отображается предупреждение "Общее обновление с высокой частотой" для такого рода конструкции.
При запуске на каждом клиенте выполняется
Возможно, вы заманчивы думать о событии On Start как о том, что выполняется при запуске сеанса, но на самом деле активируется на каждом клиенте локально, когда они присоединяются к сеансу. Он идеально подходит для инициализации локального состояния:
Однако при попытке использовать on Start для инициализации общего состояния вы обнаружите, что общее состояние непреднамеренно будет повторно инициализировано для всех, когда любой пользователь присоединяется к сеансу:
На панели диагностики визуального скрипта сетки (в виде набора средств сетки 5.2410) и содержимого Анализатор производительности (CPA) (как в Наборе средств сетки 5.2411) отображается предупреждение "Общее обновление для соединения сеансов" при обнаружении этого.
Общий доступ является типизированным, но назначение переменной не является
По соображениям безопасности общие переменные визуального скрипта строго типизированы. Это означает, что тип, который вы выбираете в компоненте переменных переменных скрипта, которые вы объявили, определяет, какой точный тип значения будет синхронизирован между клиентами.
К сожалению, visual Scripting Unity полностью игнорирует объявленный тип переменной при обновлении его значения. Например, легко случайно хранить значение float-typed в переменной, объявленной для целочисленного типа. В локальном клиенте визуальные скрипты не замечают эту ошибку, так как визуальное скриптирование автоматически преобразует ошибочное значение Float в ожидаемое целое число по мере необходимости. Однако, когда дело доходит до синхронизации этого значения между клиентами, visual Scripting сетки не может принимать те же свободы: "итоговая согласованность" гарантирует, что любые преобразования значений в полете, а вопросы безопасности и безопасности не позволяют принимать другой тип значения от удаленного клиента, чем то, что было объявлено для переменной.
Например, рассмотрим это объявление общей переменной с именем MyIntegerVar:
Ниже приведен поток скрипта, который обновляет эту переменную:
какие проблемы могут возникнуть. К сожалению, узел скрипта случайного | диапазона, используемый в этом примере, представлен в двух вариантах: один, который создает случайное целочисленное значение, и тот, который создает случайное значение Float. Разница между этими двумя узлами скриптов на панели селектора узлов является тонкой:
Таким образом, если вы случайно выбрали неправильный узел скрипта случайного | диапазона там, ваш скрипт может в конечном итоге непреднамеренно хранить значение Float в переменной целочисленного типа, но это ошибочное значение Float не будет реплицировано на других клиентах.
Имейте в виду это в качестве потенциальной причины, из-за которой общая переменная, которую вы создали, может показаться, перестала делиться. Будущие выпуски Визуального скрипта сетки могут предупреждать об ошибке скрипта такого рода, когда они могут обнаружить его.