Как мы переосмыслили работу со сценами в Unity
Unity, как движок, имеет ряд недостатков, но которые благодаря возможностям для кастомизации и инструментам для кодогенерации, можно решить.
Сейчас я вам расскажу о том, как мы написали плагин для Unity на основе пост-процессинга проектов и кодогенератора CodeDom.
Проблема
В Unity загрузка сцен происходит через строковой идентификатор. Он не стабильный, а это означает, что он легко изменяем без явных последствий. Например, при переименовании сцены всё полетит, а выяснится это только в самом конце на этапе выполнения.
Проблема обнаруживается быстро на часто используемых сценах, но может быть трудно обнаруживаемая, если речь идёт о небольших additive сценах или сценах, которые используются редко.
Решение
При добавлении сцены в проект, генерируется одноимённый класс с методом Load.
Если мы добавим сцену Menu, то в проекте сгенерируется класс Menu и в дальнейшем мы можем запустить сцену следующим образом:
Menu.Load();
Да, статический метод — это не лучшая компоновка. Но мне показалось это лаконичным и удобным дизайном. Генерация происходит автоматически, исходный код такого класса:
//------------------------------------------------------------------------------ // // This code was generated by a tool. // Runtime Version:4.0.30319.42000 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // //------------------------------------------------------------------------------ namespace IJunior.TypedScenes < public class Menu : TypedScene < private const string GUID = "a3ac3ba38209c7744b9e05301cbfa453"; public static void Load() < LoadScene(GUID); >> >
По-хорошему, класс должен быть статическим, так как не предполагается создание экземпляра из него. Это ошибка, которую мы исправим. Как видно уже из этого фрагмента, кода мы цепляемся не к имени, а к GUID сцены, что является более надежным. Сам базовый класс выглядит вот так:
namespace IJunior.TypedScenes < public abstract class TypedScene < protected static void LoadScene(string guid) < var path = AssetDatabase.GUIDToAssetPath(guid); SceneManager.LoadScene(path); >protected static void LoadScene(string guid, T argument) < var path = AssetDatabase.GUIDToAssetPath(guid); UnityActionhandler = null; handler = (from, to) => < if (to.name == Path.GetFileNameWithoutExtension(path)) < SceneManager.activeSceneChanged -= handler; HandleSceneLoaders(argument); >>; SceneManager.activeSceneChanged += handler; SceneManager.LoadScene(path); > private static void HandleSceneLoaders(T loadingModel) < foreach (var rootObjects in SceneManager.GetActiveScene().GetRootGameObjects()) < foreach (var handler in rootObjects.GetComponentsInChildren()) < handler.OnSceneLoaded(loadingModel); >> > > >
В этой реализации видна ещё одна фишка — передача параметров сценам.
Параллельно с решением первой проблемы — избавить код от строковых идентификаторов сцены (в том числе и от констант, которые нужно обновлять вручную), мы добавили еще и параметризацию сцен.
Теперь сцена может задекларировать список параметров, а вызывающий код должен передать для них аргументы.
Например, сцена Game хочет получить перечисление, содержащее всех игроков и при инициализации добавить для них аватаров.
В таком случае мы можем сами создать такой компонент.
using IJunior.TypedScenes; using System.Collections.Generic; using UnityEngine; public class GameLoadHandler : MonoBehaviour, ISceneLoadHandler> < public void OnSceneLoaded(IEnumerableplayers) < foreach (var player in players) < //make avatars >> >
После размещения его на сцене и последующим её сохранением, генератор добавит в класс сцены метода запуска сцены с обязательной передачей перечисления игроков.
//------------------------------------------------------------------------------ // // This code was generated by a tool. // Runtime Version:4.0.30319.42000 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // //------------------------------------------------------------------------------ namespace IJunior.TypedScenes < public class Game : TypedScene < private const string GUID = "976661b7057d74e41abb6eb799024ada"; public static void Load(System.Collections.Generic.IEnumerableargument) < LoadScene(GUID, argument); >> >
В данный момент реализована возможность перегрузки обработчиков. Т.е. если на сцене будет N обработчиков с разными параметрами, под них создастся N методов запуска. Также не запрещается наличие нескольких компонентов-обработчиков с одинаковыми параметрами.
Это не фишка, а скорее недоработка, так как такой функционал быстрее создаст путаницу, нежели будет полезен.
А почему не сделать через N?
Первую версию плагина я осветил на своём YouTube канале в этом видео.
Там задали ряд вопросов и предложили альтернативные решения. Есть интересные подходы, которые имеют право на жизнь, а есть откровенно идиотские, которые я хочу разобрать.
Чем плох статический класс с полями, через которые передаются данные для сцены?
Нередко встречаю и такое. Речь идёт о классе по типу этого:
public class GameArguments < public IEnumerablePlayers < get; set; >>
А уже внутри сцены, вероятно, будет группа компонентов, которая достаёт данные из свойств.
Основная проблема в том, что нет формализации: при загрузки сцены не совсем ясно, что нам нужно предоставить для корректной работы. Получателей в целом определить будет проще, так как мы можем воспользоваться поиском по ссылке.
Ну и опять же сцену придётся запускать по ID или имени.
Чем плох PlayerPerfs
Предлагали и такой экзотический вариант. Можно было бы начать с того, что PlayerPrefs вообще не предназначен для передачи значений внутри одного инстанса. Этим можно было бы и закончить, но продолжим критику тем, что вам также придётся работать с неформальными строковыми идентификаторами параметров.
Параллель с ASPNet
Мне хотелось получить что-то схожее с строго типизированными View из ASPNet Core. Мы считаем плохим тоном использовать ViewData и стараемся определять ViewModel. В Unity хочется что-то такого же толка с теми же преимуществами.
Отличие Unity в первую очередь в том, что сцена — это обычно более громоздкое предприятие, нежели View в ASPNet. Это решается разбивкой одной сцены на несколько подсцен с режимом загрузки Additive (наш плагин, к слову, его поддерживает), что позволяет скомпоновать сцену из сцен поменьше со своими более атомарными моделями.
Но такой подход не очень распространён, к сожалению, и на это, я думаю, есть свои причины.
Где скачать
Плагин мы сделали в паре с Владиславом Койдо в рамках Proof-of-concept. Он ещё не стабилен и не обкатан как следует, но с ним уже можно поиграться.
Если вам интересно, я попрошу Владислава в следующей статье рассказать, как он работал с Code Dom в Unity и как работать с пост-процессингом на примере того, что мы сегодня обсуждали.
Сочетания клавиш Unity
Это обзор горячих клавиш Unity, установленных по умолчанию. Вы также можете скачать таблицу в формате PDF для Windows и MacOSX. Если сочетание клавиш содержит CTRL/CMD, это означает, что нужно использовать клавишу Control на Windows или клавишу Command на MacOSX.
| Tools | |
| “Горячая” клавиша | Действие |
| Q | Pan (перемещение камеры сцены) |
| W | Move (перемещение) |
| E | Rotate (вращение) |
| R | Scale (масштабирование) |
| T | Rect Tool |
| Z | Pivot Mode toggle (переключение Pivot-режима) |
| X | Pivot Rotation Toggle (переключение поворота пивота) |
| V | Vertex Snap (привязка вершин) |
| CTRL/CMD+ЛКМ | Snap (привязка) |
| GameObject | |
| CTRL/CMD+SHIFT+N | Создать новый игровой объект |
| ALT+SHIFT+N | New empty child to selected game object |
| CTRL/CMD+ALT+F | Переместить объект к точке вида |
| CTRL/CMD+SHIFT+F | Выровнить по виду |
| SHIFT+F или двойное F | Привязывает камеру окна Scene View к выбранному игровому объекту |
| Window (окно) | |
| CTRL/CMD+1 | Scene (сцена) |
| CTRL/CMD+2 | Game (игра) |
| CTRL/CMD+3 | Inspector (инспектор) |
| CTRL/CMD+4 | Hierarchy (иерархия) |
| CTRL/CMD+5 | Project (проект) |
| CTRL/CMD+6 | Animation (анимация) |
| CTRL/CMD+7 | Profiler (профайлер) |
| CTRL/CMD+9 | Магазин ассетов |
| CTRL/CMD+0 | Version Control |
| CTRL/CMD+SHIFT+C | Console (консоль) |
| Edit | |
| CTRL/CMD+Z | Отменить |
| CTRL+Y (только на Windows) | Повторить |
| CMD+SHIFT+Z (только на Mac) | Повторить |
| CTRL/CMD+X | Вырезать |
| CTRL/CMD+C | Копировать |
| CTRL/CMD+V | Вставить |
| CTRL/CMD+D | Дублировать |
| SHIFT+Del | Удалить |
| F | Центрировать камеру сцены на объекте |
| CTRL/CMD+F | Найти |
| CTRL/CMD+A | Выбрать все |
| CTRL/CMD+P | Играть |
| CTRL/CMD+SHIFT+P | Пауза |
| CTRL/CMD+ALT+P | Шаг |
| Selection (выделение) | |
| CTRL/CMD+SHIFT+1 | Загрузить выделение 1 |
| CTRL/CMD+SHIFT+2 | Загрузить выделение 2 |
| CTRL/CMD+SHIFT+3 | Загрузить выделение 3 |
| CTRL/CMD+SHIFT+4 | Загрузить выделение 4 |
| CTRL/CMD+SHIFT+5 | Загрузить выделение 5 |
| CTRL/CMD+SHIFT+6 | Загрузить выделение 6 |
| CTRL/CMD+SHIFT+7 | Загрузить выделение 7 |
| CTRL/CMD+SHIFT+8 | Загрузить выделение 8 |
| CTRL/CMD+SHIFT+9 | Загрузить выделение 9 |
| CTRL/CMD+ALT+1 | Сохранить выделение 1 |
| CTRL/CMD+ALT+2 | Сохранить выделение 2 |
| CTRL/CMD+ALT+3 | Сохранить выделение 3 |
| CTRL/CMD+ALT+4 | Сохранить выделение 4 |
| CTRL/CMD+ALT+5 | Сохранить выделение 5 |
| CTRL/CMD+ALT+6 | Сохранить выделение 6 |
| CTRL/CMD+ALT+7 | Сохранить выделение 7 |
| CTRL/CMD+ALT+8 | Сохранить выделение 8 |
| CTRL/CMD+ALT+9 | Сохранить выделение 9 |
| Assets (ассеты) | |
| CTRL/CMD+R | Обновить |
Customizing Your Workspace (Настройка рабочего пространства)
Переход между сценами
Приветствую! В данной статье мы разберём несколько способов перехода между сценами, а именно:
- Смена сцен по названию.
- Смена сцен по индексам.
- Смена сцен, используя параметры.
Смена сцен по названию
Данный способ самый простой, и встречается он очень часто. Он используется тогда, когда нам необходимо перейти точно на определённую сцену. Для этого создадим C# скрипт, с названием, например, Scenes. И пропишем в нём следующий код:
using UnityEngine; using UnityEngine.SceneManagement; public class Scenes : MonoBehaviour < public void OpenMenu() < SceneManager.LoadScene("Menu"); >public void OpenGame() < SceneManager.LoadScene("Game"); >>
Разбор кода
Обратите внимание, что при работе со сценами, нам необходимо обязательно подключить библиотеку:
using UnityEngine.SceneManagement;
Дальше мы создаём функцию OpenMenu(), которая будет загружать сцену с названием Menu. Вторая функция с названием OpenGame() загружаем сцену с названием Game. Убедитесь, что сцены с такими названиями существуют в Вашем проекте.
Как видите, всё очень просто. Условия, при которых Вы будете запускать данные функции, могут быть совсем разные. Например: клик на клавишу «Esc», здоровье персонажа меньше нуля, и тд. Пример:
if(HealthUser
Смена сцен по индексам
Данный способ аналогичен предыдущему, за исключением того, что мы вместо названий сцен, используем их индексы. Данный способ полезен в тех случаях, когда нам необходимо перейти на следующую или предыдущую сцену. Создадим C# скрипт, с названием, например, Scenes. И пропишем в нём следующий код:
using UnityEngine; using UnityEngine.SceneManagement; public class Scenes : MonoBehaviour < public void OpenMenu() < SceneManager.LoadScene(0); >public void OpenGame() < SceneManager.LoadScene(1); >>
Как видите, код аналогичен предыдущему, поэтому разбирать мы его не будем. Единственное что хотелось бы отметить, что благодаря функциям, у нас открываются чуть больше возможностей перехода на сцены. Например, мы можем перейти на следующую или предыдущую функцию:
using UnityEngine; using UnityEngine.SceneManagement; public class Scenes : MonoBehaviour < public void NextLevel() < var index = SceneManager.GetActiveScene().buildIndex; SceneManager.LoadScene(index + 1); >public void PreviousLevel() < var index = SceneManager.GetActiveScene().buildIndex; SceneManager.LoadScene(index - 1); >>
Разбор кода
Строчки в функции NextLevel():
var index = SceneManager.GetActiveScene().buildIndex; SceneManager.LoadScene(index + 1);
В первой строчке мы в переменную index мы заносим тот индекс сцены, в котором мы находимся в данный момент. А уже во второй строчке, мы увеличиваем индекс нашей сцены на единицу, и запускаем таким образом следующую сцену.
В функции PreviousLevel() всё аналогично, только индекс мы не увеличиваем на единицу, а уменьшаем. Тем самым запускаем предыдущую сцену.
Обратите внимание! Перед тем как работать со сценами по их индексам, нам необходимо добавить все сцены в Build Settings. Для этого жмём на вкладку File — Build Settings. Далее перетаскиваем все свои сцены в область Scenes in Build, и расставляем их по порядку. После чего просто закрываем данное окно. Теперь можно спокойно работать с индексами.

Смена сцен, используя параметры
Данный способ аналогичен предыдущему, только здесь мы переходим на определённую сцену исходя из переданного аргумента в нашу функцию.
using UnityEngine; using UnityEngine.SceneManagement; public class Scenes : MonoBehaviour < public void GoToLevel(int number) < SceneManager.LoadScene(number); >>
Разбор кода
Функция GoToLevel() принимает в качестве аргумента номер индекса, который нам необходимо открыть, и заносит его в переменную number. Этот метод защитит нас от дублирования однотипных функций, которые по идее выполняют одно и тоже.
Давайте теперь попробуем выполнить функцию GoToLevel(), передав ей аргумент «1» следующим образом:
GoToLevel(1)
Конечно же, в качестве аргумента мы можем передать не только индекс, но и название. Поэтому используя открытие сцены через аргумент функции, Вы можете работать как с индексами, так и с названиями сцен.
Если Вам понравилась сцена, буду рад Вашим лайкам.
Как переименовать сцену в unity
Искать на сайте Unity
Переименование сериализованых полей
3 февраля 2015 г. через Engine & platform | 2 мин. читать

Расширить

Расширить
Охваченные темы
Поделиться
Is this article helpful for you?
Thank you for your feedback!
Некоторые из вас, возможно, знают, что мы создали большую часть новой системы UI как пользовательские скрипты. Мы столкнулись с большинством ограничений, с которыми сталкиваются наши пользователи при разработке в Unity и расширении редактора. В процессе разработки мы исправили проблемы, с которыми столкнулись. Сегодня мы хотели бы рассказать об одной из них.
Проблема заключалась в том, что, когда мы переименовывали поля, наши пользователи теряли данные. Во время бета-тестирования мы хотели изменить наш код, не портя при этом проекты пользователей бета-версии, чтобы они могли продолжить тестирование каждой новой версии, не беспокоясь о потере данных. Чтобы решить эту проблему, мы ввели атрибут [FormerlySerializedAs].
Что это вам дает? Давайте рассмотрим некоторые варианты использования!
Переименование переменной
Допустим, у вас есть следующий класс:
[csharp]
using UnityEngine;
class MyClass : MonoBehaviour
[SerializeField]
private string m_MyVariable;
>
[/csharp]
Но вы хотели бы переименовать m_MyVariable, например, на m_ABetterName, и вы не хотите, чтобы ваши пользователи повторно заполняли данные для этого скрипта во всех своих сценах и/или префабах. Сделать это можно следующим образом:
[csharp]
using UnityEngine;
using UnityEngine.Serialization;
class MyClass : MonoBehaviour
[FormerlySerializedAs(«m_MyVariable»)]
[SerializeField]
private string m_ABetterName;
>
[/csharp]
Инкапсуляция публичного API
В этом примере у вас есть публичное поле, которое является частью вашего API, но хотелось бы оформить его как метод доступа. Итак, давайте предположим у нас есть класс MyClass:
[csharp]
using UnityEngine;
class MyClass : MonoBehaviour
public string myValue;
>
[/csharp]
Для инкапсуляции этого значения в метод доступа без потери существующих данных в ваших ассетах вы можете сделать что-то вроде этого:
[csharp]
using UnityEngine;
using UnityEngine.Serialization;
class MyClass : MonoBehaviour
[FormerlySerializedAs(«myValue»)]
[SerializeField]
private string m_Value;
public string myValue
get < return m_Value; >
set < m_Value = value; >
>
>
[/csharp]
Несколько переименований
Поддерживается переименование полей несколько раз, просто добавьте атрибут для каждого предыдущего имени поля:
[csharp]
using UnityEngine;
using UnityEngine.Serialization;
class MyClass : MonoBehaviour
[FormerlySerializedAs(«m_MyVariable»)]
[FormerlySerializedAs(«m_ABetterName»)]
[SerializeField]
private string m_EvenBetterName;
>
[/csharp]
Когда можно удалить атрибут?
Вы можете удалить атрибуты после того, как вы повторно сохранили все сцены и ассеты, после переименования. Конечно, это означает, что вы должны “контролировать” ваших пользователей. Но, например, для продавцов в Asset Store этот “контроль” невозможен. В этом случае вам придется оставить это атрибут до тех пор, пока вы хотите, чтобы люди с любой версией кода могли обновиться до новой версии кода без потери данных.
Надеюсь, вы найдете полезной эту новую маленькую особенность!
Scenes
Сцены содержат объекты вашей игры. Они могут использоваться для создания главного меню, отдельных уровней и для других целей. Можно считать каждый файл сцены отдельным игровым уровнем. В каждой сцене можно разместить объекты окружения, заграждения, декорации, по кусочкам создавая дизайн и саму игру.

When you create a new Unity project, your scene view will show a new Scene. This is an untitled and unsaved scene. The scene will be empty except for defult objects — either an orthographic camera, or a perspective camera and a directional light, depending on whether you started the project in 2D or 3D mode.
To save the scene you’re currently working on, choose File > Save Scene from the menu, or pres Ctrl/Cmd + S.
Scenes are saved as assets, into your project’s Assets folder. Therefore they appear in the Project Window, just like any other asset.

To open a scene, in order to begin or continue working within that scene, double-click the scene asset in the Project Window.
If your current scene contains unsaved changes, you will be prompted to save or discard the changes.
Multi-Scene Editing.
It is possible to have multiple scenes open for editing at one time. For more information about this, see Multi-Scene Editing.
Переход между сценами
Приветствую! В данной статье мы разберём несколько способов перехода между сценами, а именно:
- Смена сцен по названию.
- Смена сцен по индексам.
- Смена сцен, используя параметры.
Смена сцен по названию
Данный способ самый простой, и встречается он очень часто. Он используется тогда, когда нам необходимо перейти точно на определённую сцену. Для этого создадим C# скрипт, с названием, например, Scenes. И пропишем в нём следующий код:
using UnityEngine; using UnityEngine.SceneManagement; public class Scenes : MonoBehaviour < public void OpenMenu() < SceneManager.LoadScene("Menu"); >public void OpenGame() < SceneManager.LoadScene("Game"); >>
Разбор кода
Обратите внимание, что при работе со сценами, нам необходимо обязательно подключить библиотеку:
using UnityEngine.SceneManagement;
Дальше мы создаём функцию OpenMenu(), которая будет загружать сцену с названием Menu. Вторая функция с названием OpenGame() загружаем сцену с названием Game. Убедитесь, что сцены с такими названиями существуют в Вашем проекте.
Как видите, всё очень просто. Условия, при которых Вы будете запускать данные функции, могут быть совсем разные. Например: клик на клавишу «Esc», здоровье персонажа меньше нуля, и тд. Пример:
if(HealthUser
Смена сцен по индексам
Данный способ аналогичен предыдущему, за исключением того, что мы вместо названий сцен, используем их индексы. Данный способ полезен в тех случаях, когда нам необходимо перейти на следующую или предыдущую сцену. Создадим C# скрипт, с названием, например, Scenes. И пропишем в нём следующий код:
using UnityEngine; using UnityEngine.SceneManagement; public class Scenes : MonoBehaviour < public void OpenMenu() < SceneManager.LoadScene(0); >public void OpenGame() < SceneManager.LoadScene(1); >>
Как видите, код аналогичен предыдущему, поэтому разбирать мы его не будем. Единственное что хотелось бы отметить, что благодаря функциям, у нас открываются чуть больше возможностей перехода на сцены. Например, мы можем перейти на следующую или предыдущую функцию:
using UnityEngine; using UnityEngine.SceneManagement; public class Scenes : MonoBehaviour < public void NextLevel() < var index = SceneManager.GetActiveScene().buildIndex; SceneManager.LoadScene(index + 1); >public void PreviousLevel() < var index = SceneManager.GetActiveScene().buildIndex; SceneManager.LoadScene(index - 1); >>
Разбор кода
Строчки в функции NextLevel():
var index = SceneManager.GetActiveScene().buildIndex; SceneManager.LoadScene(index + 1);
В первой строчке мы в переменную index мы заносим тот индекс сцены, в котором мы находимся в данный момент. А уже во второй строчке, мы увеличиваем индекс нашей сцены на единицу, и запускаем таким образом следующую сцену.
В функции PreviousLevel() всё аналогично, только индекс мы не увеличиваем на единицу, а уменьшаем. Тем самым запускаем предыдущую сцену.
Обратите внимание! Перед тем как работать со сценами по их индексам, нам необходимо добавить все сцены в Build Settings. Для этого жмём на вкладку File — Build Settings. Далее перетаскиваем все свои сцены в область Scenes in Build, и расставляем их по порядку. После чего просто закрываем данное окно. Теперь можно спокойно работать с индексами.

Смена сцен, используя параметры
Данный способ аналогичен предыдущему, только здесь мы переходим на определённую сцену исходя из переданного аргумента в нашу функцию.
using UnityEngine; using UnityEngine.SceneManagement; public class Scenes : MonoBehaviour < public void GoToLevel(int number) < SceneManager.LoadScene(number); >>
Разбор кода
Функция GoToLevel() принимает в качестве аргумента номер индекса, который нам необходимо открыть, и заносит его в переменную number. Этот метод защитит нас от дублирования однотипных функций, которые по идее выполняют одно и тоже.
Давайте теперь попробуем выполнить функцию GoToLevel(), передав ей аргумент «1» следующим образом:
GoToLevel(1)
Конечно же, в качестве аргумента мы можем передать не только индекс, но и название. Поэтому используя открытие сцены через аргумент функции, Вы можете работать как с индексами, так и с названиями сцен.
Если Вам понравилась сцена, буду рад Вашим лайкам.
Редактирование нескольких объектов.
Начиная с версии Юнити 3.5 Вы можете выбрать несколько объектов одного типа и редактировать их одновременно в Инспекторе. Любые изменения свойств будут применены ко всем выбранным объектам. Это сохраняет массу времени, если нужно сделать одинаковые изменения на многих объектах.
Если выбрано несколько объектов, то в Инспекторе будут отображаться только те компоненты, которые есть на всех выбранных объектах. Если какие то компоненты есть не на всех объектах, то в Инспекторе будет выведено сообщение, что компоненты есть только на некоторых объектах и не могут быть редактированы.
Свойства — значения [ ]
Когда выбрано несколько объектов каждое свойство отображаемое в Инспекторе представляет свойство на каждом выбранном объекте. Если значение свойства одинаково на всех объектах, значение будет показано как обычно, так же как на одном объекте. Если значения свойства отличаются оно не отображается, а выводится тире или нечто подобное, что индицирует что значения разные.

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

Мультиредактирование Префабов или экземпляров объекта [ ]
Префабы также можно редактировать сразу несколько за раз как и объекты в сцене. Экземпляры префабов также можно редактировать сразу несколько, но с некоторыми ограничениями. Когда редактируется один префаб, или экземпляр модели любое свойство которое отличается от того, которое в префабе или модели будет выводиться жирным шрифтом и при правом клике на свойство будет возвращено к значению из префаба или модели. Также Объект игры имеет опции применить или отменить все изменения. Ни одна из этих вещей не доступны при мультиредактировании. Свойства не могут применены или отменены, они не отображаются жирным шрифтом. Для напоминания об этом Инспектор показывает замечание Instance Management Disabled где обычно выводятся кнопки Select, Revert и Apply.

Не поддерживаемые объекты [ ]
Некоторые типы объектов не поддерживают мультиредактирование. Когда Вы выбираете несколько объектов будет выведено сообщение «Мультиредактирование этих объектов не поддерживается» («Multi-object editing not supported»).
Если Вы сделали пользовательский редактор для одного из Ваших скриптов также будет выведено это сообщение, если он не поддерживает мультиредактирование. Смотри в справочнике скриптов Класс Редактор, чтобы узнать как реализовать поддержку редактирования нескольких объектов в своем редакторе.
Руководство по программированию облачных сценариев
В этом руководстве описывается использование API облачных сценариев Сетки и средств разработчика для создания сред (они начинаются как проекты в Unity, а затем передаются в коллекцию Mesh). Рекомендуется сначала ознакомиться с разделом Настройка инфраструктуры облачных сценариев в Azure , чтобы ознакомиться с основными понятиями и базовой архитектурой облачного скрипта Mesh.
В этом разделе описываются функции и интерфейс API cloud scripting Mesh, который используется для написания скриптов, которые управляют поведением в средах.
Базовая структура DOM
Структура DOM отражает структуру сцены Unity. Элемент «Сцена» приложения соответствует игровому объекту, к которому подключен компонент mesh Cloud Scripting. Следующие классы API облачных сценариев Mesh сопоставляют один к одному с объектами Unity, созданными в редакторе:
- GameObject (& Transform Component) —> TransformNode
- Компонент освещения —> PointLightNode, SpotLightNode, DirectionalLightNode
- Компонент Animator —> AnimationNode (и производные классы, см. ниже)
- Компонент Box Collider —> BoxGeometryNode
- Компонент Sphere Collider —> SphereGeometryNode
- Компонент капсульного коллайдера —> CapsuleGeometryNode
- Компонент сетчатого коллайдера —> MeshGeometryNode
- Компонент Text Mesh Pro —> TextNode
- Компонент Rigidbody —> RigidBodyNode
Например, если вы создаете сцену с игровым объектом, который имеет компонент Light (задается в точечный свет) и подключенный шарный коллайдер, сцена будет содержать TransformNode с двумя дочерними элементами: PointLightNode и SphereGeometryNode.
Кроме того, некоторые объекты API облачных сценариев Mesh не имеют соответствующих встроенных компонентов Unity. Это дополнительные компоненты, которые можно создать в Unity, которые входят в пакет набора средств сетки.
- Компонент cloud scripting Mesh (описано выше)
- Компонент WebSlate
Сопоставление модели Unity DOM с моделью DOM сетки
Вы можете создать сцену с компонентами, о которые API облачных сценариев сетки не знает. Они просто не будут существовать в модели модели сценариев Mesh Cloud для сцены. Однако полная структура сцены GameObjects будет отражена в API DOM как TransformNodes.
Unity имеет форму API GameObject/component; однако модель DOM облачных сценариев Mesh имеет одну древовидную структуру. TransformNode в API облачных сценариев mesh имеет дочерние элементы, которые могут быть другими transformNodes или другими узлами, которые сопоставляются с компонентами. Мы можем рассматривать это как объединенный список компонентов связанного игрового объекта и дочерних элементов его компонента преобразования.
Преобразование rect
При добавлении компонента, использующего RectTransform, например компонента Text Mesh Pro, игровой объект не будет отображаться как Node в графе сцены сценариев Mesh Cloud. Такой компонент по-прежнему можно перемещать, включать и отключать, но для этого необходимо заключить игровой объект с помощью RectTransform в другой игровой объект с помощью обычного компонента Преобразования.
События изменения свойств
Вы можете подписаться на события изменения свойств, вызвав вызов AddPropertyChangedEventHandler на любом узле в иерархии. Необходимо передать имя свойства в виде строки.
Также можно подписаться на все события, подписавшись на DomObjectPropertyChanged событие. Вызывается при изменении любого свойства в модели DOM.
Жизненный цикл объекта
При создании узлы не являются неродимыми. Это означает, что они не будут видны в сцене, пока они не будут явно добавлены в качестве дочернего элемента в сцену или одного из ее потомков. Аналогичным образом установка для родительского узла значения NULL приведет к удалению его и его потомков из сцены.
Иногда требуется временно отключить узел, но не вести запись о том, где он находился в сцене. По этой причине каждый узел имеет флаг «активный». Если задано значение false, узел и его потомки будут отключены.
В Unity можно создавать игровые объекты и компоненты, которые являются частью сцены, но отключены. Они будут отображаться как узлы в иерархии сцены mesh Cloud Scripting, но для активного флага будет установлено значение false. Если установить для активного флага значение true, они будут активированы в сцене Unity.
Клонирование и повторное развертывание
Узлы могут быть клонированы и повторно преобразованы в API облачных сценариев сетки; соответствующая сцена Unity обновится соответствующим образом. Если клонировать узел, он клонирует этот узел и все его дочерние элементы (включая дочерние элементы, которые могут находиться в соответствующих объектах Unity, но не видны для Cloud Scripting Mesh).
Можно клонировать или повторно напарачивать узлы, соответствующие компонентам Unity. Это реализуется путем повторного создания этих компонентов Unity на основе представлений узла сценариев в облаке сетки. Клонировать или повторно создать только узлы, которые можно создать с помощью API облачных сценариев mesh. Если вы создали компонент в Unity и задали поля, которые не отражены в соответствующем узле cloud scripting Node, эти поля будут сброшены до значений по умолчанию, если клонирован сам узел. По этой причине рекомендуется клонировать или повторно создавать узлы преобразования, в которых вы управляете объектами, созданными в Unity. Они всегда будут правильно сохранять все исходные параметры Unity.
Пользователи
В API есть различные места, предоставляющие свойства пользователя. Свойство User.Identifier является строкой постоянного идентификатора, которая является постоянной для пользователя, даже если пользователь покидает и повторно присоединится. Отображаемое имя пользователя также доступно через User.DisplayName . Идентификатор события, из которого пользователь подключился, доступен через User.ConnectedEventId .
Во время разработки отображаемое имя пользователя, идентификатор и идентификатор события можно имитировать в редакторе компонента Mesh Cloud Scripting в разделе «Параметры разработчика», как показано ниже.

Аватары
Аватары — это представление пользователей в сцене. Их можно использовать для телепортирования пользователей в заданное расположение, перемещения между сценами и обнаружения столкновений с триггерными томами.
Информационные диалоги
В cloud Scripting можно открыть диалоговое окно пространства экрана в приложении Microsoft Mesh с пользовательским сообщением. SceneNode содержит функцию для этого , ShowMessageToParticipants(string message, IReadOnlyCollection participants) . Теги форматированного текста можно использовать в сообщении для управления свойствами текста (цветом, полужирным шрифтом и т. д.).
Классы
CloudApplication
Интерфейс ICloudApplication является отправной точкой для разработки приложений сетки. Он доступен в app.cs в качестве переменной _app. Помимо сцены, ICloudApplication имеет функции создания для всех доступных типов. Он также имеет ряд других методов, но они используются для внутреннего использования.
InteractableNode
MeshInteractableSetup — это пользовательский компонент Unity, который входит в пакет набора средств сетки. При присоединении к игровому объекту в Unity он вызывает события щелчка, когда любой пользователь щелкает любой из активных конфликтующих объектов в этом игровом объекте или его дочерних элементах.
Ниже показан простой пример, в котором компонент MeshInteractableSetup добавляется в тот же игровой объект, что и коллайдер box:

WebSlateNode
WebSlate — это пользовательский компонент Unity, который входит в пакет набора средств сетки. Чтобы добавить заготовку WebSlate в сцену, выберите GameObject>Mesh Toolkit>WebSlate в строке меню. Веб-сайт, назначенный свойству URL-адреса экземпляра WebSlate, отображается на четырехугольнике этого заготовки.
Ниже показан пример, в котором в сцену добавлена префаб WebSlate и назначен URL-адрес:
var webSlateNode = Root.FindFirstChild(true); webSlateNode.Url = new System.Uri("https://en.wikipedia.org/wiki/Color");

Прослушивание щелчков
Ниже приведен простой скрипт Mesh Cloud Scripting, который поворачивает куб при каждом щелчке. Замените метод-заглушку StartAsync внутри App.cs этим кодом.
private float _angle = 0; public Task StartAsync(CancellationToken token) < // First we find the TransformNode that corresponds to our Cube gameobject var transform = _app.Scene.FindFirstChild(); // Then we find the InteractableNode child of that TransformNode var sensor = transform.FindFirstChild(); // Handle a button click sensor.Selected += (_, _) => < // Update the angle on each click _angle += MathF.PI / 8; transform.Rotation = new Rotation < X = 1, Y = 0, Z = 0, Angle = _angle >; >; return Task.CompletedTask; >
Сведения о попадании
Можно узнать, какой пользователь щелкнул коллайдер, просмотрев аргументы события изменения свойства. Вы также можете прочитать обычный контакт и положение щелчка из аргументов события. Эти координаты будут относительно локального пространства координат Объекта InteractableNode.
Аниматоры
Вы можете создать и добавить аниматор Unity в сцену и управлять им с помощью облачных сценариев Mesh. Подключаемый модуль набора средств сетки будет просматривать ресурсы в проекте Unity, и для каждого найденного animator он создаст класс в папке AnimationScripts в проекте Mesh Cloud Scripting. Этот класс является производным от AnimationNode и может использоваться для управления аниматором из облачного скрипта Mesh. При добавлении Animator в качестве компонента к игровому объекту в Unity вы найдете соответствующий экземпляр созданного класса в качестве дочернего элемента соответствующего TransformNode. С помощью API этого класса можно управлять аниматором.
Модель программирования облачных сценариев Mesh является заслуживающей доверия на сервере, и мы поддерживаем только небольшое подмножество функций аниматора. Это связано с тем, что мы моделироваем аниматор на сервере и ожидаем, что все клиенты будут точно синхронизированы с серверной моделью. По этой причине в настоящее время поддерживается только следующий API:
- Параметр состояния (для каждого слоя в классе есть соответствующее свойство, которое можно задать в перечисление на основе доступных состояний в аниматоре). Состояния задаются немедленно, а не через переходы.
- Параметр переменной float: предоставляются только переменные float и только для привязки к времени движения в аниматоре.
- Параметр скорости слоя
В состоянии можно создать клип анимации без ограничений на значения, которые можно задать в сцене Unity. Кроме того, поддерживаются клипы анимации в цикле. В AnimationNodes не поддерживаются следующие функции аниматоров:
- Переходы. Если вы добавите переходы в аниматор, вы не сможете активировать их с помощью API облачных сценариев сетки (сервер не моделировает переходы).
- Переменные (кроме с плавающей запятой для перемещения времени движения). Переменные, используемые для управления логикой перехода или множителями скорости, не поддерживаются.
- Зеркальные состояния, смещение цикла и IK ноги.
Позднее присоединение и аниматоры
Когда клиенты присоединяются к событию сетки, они синхронизируются с текущим состоянием и местным временем всех запущенных узлов анимации. Если у вас есть долго выполняющаяся анимация, воспроизводимая в состоянии, для времени воспроизведения будет задано правильное текущее время анимации при позднем соединении. Однако если ваше состояние вызывает события, они НЕ будут запускаться в клиенте с поздним присоединением. Некоторые другие сценарии могут работать неправильно; Например, если вы активируете звук, включив AudioSource в начале состояния, этот AudioSource по-прежнему будет включен в клиенте позднего присоединения, но начнет воспроизводиться в начале аудиоклипа.
Начальное состояние аниматора
Рекомендуется создавать аниматоры с состояниями по умолчанию, которые ничего не делают. Когда сцена начинает воспроизводиться в Unity, она активирует все аниматоры и начнет воспроизводить их анимации по умолчанию. Это может произойти до подключения к облачной службе сценариев сетки. следовательно, нет способа синхронизировать эти состояния, и поведение может быть не так, как требуется.
Повторное создание и клонирование аниматора
AnimationNodes нельзя создать с помощью API облачных сценариев Mesh. Единственный способ создать AnimationNode — экспортировать сцену Unity, содержащую компонент Animator. При попытке клонирования или повторного распространения AnimationNode вы получите сообщение об ошибке, так как это действие не поддерживается. По-прежнему можно клонировать или перенародить родительский объект AnimationNode, так как он соответствует объекту Unity Game Object, который может быть клонирован и воспитан.
Заметки о созданном коде
Созданный код удалит пробелы из имен аниматоров, слоев, состояний и переменных; Например, имя переменной my var в коде становится myVar. По этой причине можно создать аниматоры, которые не будут создавать допустимый код. Например, если у вас есть две переменные с именами «my var» и «myVar», вы получите ошибку во время создания и сообщение с просьбой переименовать переменные.
LightNode
PointLightNode, DirectionalLightNode и SpotLightNode сопоставляется с компонентом Unity Light (для которого будет задано соответствующее значение типа). Можно задать основные параметры этих источников с помощью API LightNode. Кроме того, можно создать lights вручную с помощью API. При создании легких узлов с помощью API параметры, которые не могут быть заданы с помощью API облачных сценариев сетки, останутся равными значениям по умолчанию.
GeometryNode
BoxGeometryNode, SphereGeometryNode, CapsuleGeometryNode и MeshGeometryNode сопоставляют с Unity Box Collider Component, Sphere Collider Component, Capsule Collider Component и Mesh Collider Component, соответственно. Их также можно создать с помощью API облачных сценариев сетки. Включение и отключение геометрических узлов приведет к их добавлению и удалению из кандидатов на попадание, если MeshInteractableSetup подключен к их игровому объекту или одному из его родительских объектов.
При создании геометрических узлов с помощью API параметры, которые не могут быть заданы с помощью API сетки, будут использоваться по умолчанию (например, для физического материала будет задано значение none, а для параметра isTrigger — значение false).
RigidBodyNode
Добавление компонента Жесткого тела к объекту поставит его движение под контроль физики сетки. Без добавления кода объект Rigidbody будет потянуться вниз гравитацией и будет реагировать на столкновения с другими объектами.
Примечание. GeometryNode.Friction Возвращает staticFriction . Однако если этот параметр задан на стороне Cloud Scripting Mesh, он будет обновляться как на клиентах, так staticFriction и dynamicFriction на клиентах.
Активация томов
Геометрические узлы могут выступать в качестве томов триггеров, если для их IsTrigger свойства задано значение true. Этот флаг соответствует свойству IsTrigger коллайдера в Unity и не может быть изменен во время выполнения. Если геометрия является триггером, она создает Entered и Exited для любого Аватары, которые запускают или прекращают перекрываться с ним.
Примечание. Объект Unity необходимо добавить TriggerVolume в слой, чтобы позволить телепортации луча игнорировать его, так как коллайдеры в Default слое блокируют луч телепортации.
TextNode
TextNode сопоставляется с компонентом TextMeshPro в Unity. Если вы добавите компонент TextMeshPro в сцену, в иерархии сцены для cloud scripting mesh будет соответствующий Объект TextNode. Это позволяет задавать текст компонента во время выполнения. Вы также можете изменить основные свойства текста с помощью API TextNode: полужирное, курсивное, подчеркивание, зачеркив и цвет. В настоящее время создать TextNode с помощью API невозможно. Их необходимо создать, добавив в сцену в Unity. Кроме того, вы не можете клонировать TextNode напрямую. Вместо этого необходимо клонировать родительский объект TranformNode TextNode.
Сетки
В настоящее время сетки являются «скрытыми» компонентами в API облачных сценариев сетки. Ими можно создавать в редакторе Unity и управлять ими, управляя родительскими игровыми объектами или компонентами преобразования, но их нельзя создавать программными средствами, а также изменять их свойства во время выполнения с помощью API сетки.
Другие разделы, посвященные облачным сценариям сетки
Добавление ресурсов в облачную службу сценариев Mesh
Если вам нужно добавить ресурс для использования облачной службы сценариев Mesh, необходимо добавить его в качестве внедренного ресурса в файл проекта C#. Это можно сделать с помощью пользовательского интерфейса проекта в Visual Studio или путем непосредственного добавления следующей строки в CSPROJ-файл:
" CopyToOutputDirectory="PreserveNewest" />
Обратите внимание, что именно так упаковано scene.map, которое можно просмотреть в CSPROJ-файле для справки.
Работа с физикой сетки
Mesh Physics позаботится о синхронизации движения жестких тел между клиентами. Облачные скрипты сетки TransformNode.Position , TransformNode.Rotation RigidBody.Velocity и RigidBody.AngularVelocity не будут обновлены с учетом последнего состояния моделирования. Однако клиенты будут применять изменения, если они заданы в облачной службе сценариев mesh. Обратите внимание, что при изменении одного свойства другие свойства останутся без изменений. Например, если задано только положение, скорость не будет изменена, а жесткое тело продолжит движение со старой скоростью из нового положения. Учитывая, что служба облачных сценариев mesh не обновляется с учетом последнего состояния движения для жестких тел, рекомендуется устанавливать их только для новых жестких тел.
Если TransformNode параметр with RigidBodyNode клонирован, клонированные тела будут зарегистрированы и переданы Mesh Physics в для синхронизации между клиентами. Примечание: Клонированные жесткие тела будут иметь положение, вращение и скорости от начала сцены исходного жесткого тела. Если они должны отличаться, они должны быть явно заданы в облачных скриптах сетки.