как привязать скрипт к объекту unity

Погружение в скрипты игрового движка Unity3d, ч.1

Игровой объект

Создание нового игрового объекта с именем MyObject.
Созданный объект будет доступен по ссылку myObject.

Поиск объекта по его имени.

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

Возвращает список всех объектов с указанным тегом:

Проверка на наличие у объекта требуемого тега. Возвращает true, если у указанного объекта имеется тег MyTag:

Уничтожение объекта через минуту, после его создания:

Возвращает компонент component, привязанный к объекту GameObject, либо null, если объект не содержит данного компонента. Может использоваться, например, для доступа к другим скриптам, привязанным к объекту.

Возвращает все имеющиеся у объекта компоненты типа componentType.

Привязать компонент myComponent к объекту GameObject и получить ссылку на него.

Положение игрового объекта

Свойство transform объекта GameObject содержит в себе данные о положении объекта в игровом мире.

Возвращает глобальные координаты объекта в игровом мире. Возвращаемая величина имеет тип Vector3, который представляет из себя список из 3 координат — x, y и z:

Переместить объект в точку 0, 10, 0 игрового мира.

Тоже самое, что и в случае глобальных координат, но с локальными. Локальные координаты расситываются относительно родительского объекта. В случае отсутствия родительского объекта локальные координаты совпадают с глобальными:

Поворот объекта в углах Эйлера. Метод также возвращает координаты в виде объекта Vector3:

Тоже самое, что и предыдущий пример, но поворот объекта рассчитывается относительно родительского объекта:

Текущий угол поворота объекта, основанный на кватернионах. Возвращает объект типа Quaternion.

Текущий поворот объекта, основанный на кватернионах, но относительно родительского объекта:

Сброс угла поворота объекта:

Вращаем наш объект в указанную сторону со скоростью 1 градус в секунду. Принимает в качестве координат объект типа Vector3. Метод deltaTime объекта Time содержит время в секундах, затраченное на выполнение предыдущего кадра:

Тоже самое, что и предыдущий пример, но вращение объекта относительно координат родителя:

Перемещаем наш объект в указанном направлении со скоростью 1 юнит в секунду. Также принимает в качестве координат объект класса Vector3:

Физические свойства игрового объекта

Метод rigidbody объекта GameObject хранит в себе его физические свойства. Прежде, чем использовать метод rigidbody, его необходимо добавить к игровому объекту.

Получаем/задаем вектор скорости объекта:

Сила противодействия объекта. Может использоваться для замедления скорости, в среде с отсутствующей силой трения. Наиболее часто используется для замедления падающих объектов, например при создании парашюта. Принимает в качестве параметра целое число:

Задание массы объекту. Рекомендуется использовать массу в пределах от 0.1 до 10. Использование слишком больших значений может привести к непредсказуемым результатам при расчете физики:

Влияние на объект гравитации. Принимает в качестве параметра булево значение. Позволяет отключить влияние гравитации на отдельные объекты:

Влияние физики на игровой объект. Позволяет отключить частично, либо полностью влияние физических законов на объект:

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

Указание координат точки центра массы объекта. Применяет координаты в виде уже знакомого нам объекта Vector3.

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

Режим определения столкновений между объектами. Можно указать несколько разных режимов:
CollisionDetectionMode.ContinuousDynamic для быстро движущихся объектов;
CollisionDetectionMode.Continuous для столкновений с быстро движущимися объектами;
CollisionDetectionMode.Discrete (по умолчанию) для обычных столкновений;
В случае отсутствия проблем с определением столкновений рекомендуется использовать свойство по умолчанию.

Задать плотность объекта:

Применить импульс к объекту с указанным вектором. В результате применения импульса объект придет в движение пропорционально силе импульса.

Применить импульс к объекту с вектором в его (объекта) системы координат:

Добавить объекту крутящий момент. Применение данного метода заставит объект вращаться вокруг своего центра масс GameObject.rigidbody.centerOfMass.

Тоже самое, что и предыдущий пример, но относительно координат объекта:

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

Для полноценной симуляции объемных взрывов в Unity3D есть отдельный метод. Первый параметр метода позволяет указать мощность импульса, второй параметр — точку, из которой исходит импульс, третий параметр — радиус распространения импульса, четвертый параметр — модификатор сжатия сферы распространения силы, пятый, необязательный, параметр указывает тип используемого импульса:

Заставить объект «уснуть», и запретить дальнейший расчет физических показателей для него:

Проверить «заснул» ли объект:

«Разбудить» объект для возможности дальнейшего применения влияния физики на него:

Трассировка лучей

Один из самых часто используемых в разработке на Unity3D объект, это Ray. Данный объект позволяет выпустить луч из указанной точки, в указанном направлении, и вернуть некоторые свойства объектов, которых он смог достичь.

Создаем объект класса RaycastHit, который содержит информацию об объекте, с которым столкнулся луч:

Отправляем луч длиной в 50 юнитов из позиции rayPosition в направлении rayVector, и заносим объект, с которым столкнулся луч в переменную hit:

Получаем дистанцию до объекта, с которым столкнулся луч. Дистанция не может быть больше, чем протяженность луча:

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

Для получения тега объекта используем следующий способ:

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

Источник

Unity3D: архитектура игры, ScriptableObjects, синглтоны

Сегодня речь пойдет о том, как хранить, получать и передавать данные внутри игры. О замечательной вещи под названием ScriptableObject, и почему она замечательна. Немного затронем пользу от синглтонов при организации сцен и переходов между ними.

Данная статья описывает частичку долгого и мучительного пути разработки игры, различные примененные в процессе подходы. Скорее всего, здесь будет много полезной информации для новичков и ничего нового для «ветеранов».

Связи между скриптами и объектами

Первый вопрос, встающий перед начинающим разработчиком — как связать все написанные классы вместе и настроить взаимодействия между ними.

Самый простой способ — указать ссылку на класс напрямую:

А затем — вручную привязать скрипт через инспектор.

У этого подхода как минимум один существенный недостаток — когда количество скриптов переваливает за несколько десятков, и каждый из них требует две-три ссылки на друг друга, игра быстро превращается в паутину. Одного взгляда на неё достаточно, чтобы вызвать головную боль.

Гораздо лучше (на мой взгляд) организовать систему сообщений и подписок, внутри которой наши объекты будут получать нужную им информацию — и только её! — не требуя при этом полудюжины ссылок друг на друга.

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

ScriptableObject

Знать о ScriptableObject надо, по сути, две вещи:

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

Теперь мне не нужно указывать бесконечное количество ссылок между скриптами! Для каждого скрипта я могу один раз указать ссылку на моё хранилище — и он получит всю информацию оттуда.

Таким образом, вычисление скорости персонажа принимает весьма элегантный вид:

А если, скажем, ловушка должна срабатывать только на бегущего персонажа:

Причем персонажу совсем не нужно знать ничего ни о заклинаниях, ни о ловушках. Он просто получает данные из хранилища. Неплохо? Неплохо.

Но почти сразу я сталкиваюсь с проблемой. ScriptableOnject’ы не умеют хранить в себе ссылки на объекты сцены напрямую. Иными словами, я не могу создать ссылку на игрока, привязать её через инспектор и забыть про вопрос координат игрока навсегда.

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

Долгое время у меня работал костыль: создается публичная ссылка в хранилище, а затем каждый объект, ссылку на который нужно запомнить, эту ссылку заполнял:

Таким образом, независимо от сцены, моё хранилище первым делом получает ссылку на игрока и запоминает её. Теперь любой, скажем, враг не должен хранить в себе ссылку на игрока, не должен искать его через FindWithTag() (что довольно ресурсоёмкий процесс). Всё, что он делает — обращается к хранилищу:

Казалось бы: система идеальна! Но нет. У нас остаётся 2 проблемы.

И это порождает ряд реакций:

Можно для каждого объекта, заинтересованного в огоньке, прямо в Update() и написать, мол, так и так, каждый фрейм следи за огоньком (if (database.spellController.light.isActive)), а когда зажжется — реагируй! И плевать, что 90% времени эта проверка будет работать вхолостую. На нескольких сотнях объектов.

Или организовать все это в виде готовеньких ссылок. Получается, простенькая функция CastSpell() должна иметь доступ к ссылкам и на игрока, и на огонек, и на список врагов. И это в лучшем случае. Многовато ссылок, а?

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

Singleton

Вот тут в игру вступает синглтон. По сути, это объект, который существует (и может существовать) только в единственном экземпляре.

Я привязываю его к пустому объекту сцены. Назовем его GameController.

Таким образом, у меня в сцене есть объект, хранящий в себе всю информацию об игре. Более того — он может перемещаться между сценами, уничтожать своих двойников (если на новой сцене уже есть другой GameController), переносить данные между сценами, а при желании — реализовать сохранение/загрузку игры.

Из всех уже написанных скриптов можно удалить ссылку на хранилище данных. Ведь теперь мне не нужно её настраивать вручную. Из хранилища удаляются все ссылки на объекты сцены и переносятся в наш GameController (они все равно нам скорее всего понадобятся для сохранения состояния сцены при выходе из игры). А дальше я заливаю в него всю необходимую информацию удобным мне способом. Например, в Awake() игрока и врагов (и важных объектов сцены) прописывается добавление в GameController ссылки на самих себя. Так как теперь я работаю с Monobehaviour, ссылки на объекты сцены в него весьма органично вписываются.

Что у нас получается?

Любой объект может получить любую информацию об игре, которая ему нужна:

При этом совершенно не нужно настраивать ссылки между объектами, все хранится в нашем GameController.

Теперь не будет ничего сложного в сохранении состояния сцены. Ведь у нас уже есть вся необходимая информация: враги, предметы, положение игрока, хранилище данных. Достаточно выбрать ту информацию о сцене, которую нужно сохранить, и записать её в файл с помощью FileStream при выходе из сцены.

Опасности

Если вы дочитали до этого места, мне следует вас предостеречь об опасностях такого подхода.

Очень нехорошая ситуация складывается, когда много скриптов ссылаются на одну переменную внутри нашего ScriptableObject. В получении значения ничего нехорошего нет, а вот когда на переменную начинают воздействовать из разных мест — это потенциальная угроза.

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

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

Также не стоит забывать о том, что синглтоны — существа нежные. Не стоит ими злоупотреблять.

Многое было почерпнуто из официальных туториалов по Unity, что-то — из неофициальных. До чего-то мне пришлось доходить самому. А значит, у вышеизложенных подходов могут быть свои опасности и недостатки, которые я упустил.

Источник

Методы организации взаимодействия между скриптами в Unity3D

Вступление

Подход 1. Назначение через редактор Unity3D

Пусть у нас в проекте есть два скрипта. Первый скрип отвечает за начисление очков в игре, а второй за пользовательский интерфейс, который, отображает количество набранных очков на экране игры.
Назовем оба скрипта менеджерами: ScoresManager и HUDManager.
Каким же образом менеджеру, отвечающему за меню экрана можно получить текущее количество очков от менеджера, отвечающего за начисление очков?
Предполагается, что в иерархии объектов(Hierarchy) сцены существуют два объекта, на один из которых назначен скрипт ScoresManager, а на другой скрипт HUDManager.
Один из подходов, содержит следующий принцип:
В скрипте UIManager определяем переменную типа ScoresManager:

Но переменную ScoresManager необходимо еще инициализировать экземпляром класса. Для этого выберем в иерархии объектов объект, на который назначен скрипт HUDManager и в настройках объекта увидим переменную ScoresManager со значением None.

как привязать скрипт к объекту unity. 900ef2696d5182d43a87f20483dfe6c1. как привязать скрипт к объекту unity фото. как привязать скрипт к объекту unity-900ef2696d5182d43a87f20483dfe6c1. картинка как привязать скрипт к объекту unity. картинка 900ef2696d5182d43a87f20483dfe6c1. Создание нового игрового объекта с именем MyObject. Созданный объект будет доступен по ссылку myObject.

Далее, из окна иерархии перетаскиваем объект, содержащий скрипт ScoresManager в область, где написано None и назначаем его объявленной переменной:

как привязать скрипт к объекту unity. 6dc5a7da31258029d78f692b51778e73. как привязать скрипт к объекту unity фото. как привязать скрипт к объекту unity-6dc5a7da31258029d78f692b51778e73. картинка как привязать скрипт к объекту unity. картинка 6dc5a7da31258029d78f692b51778e73. Создание нового игрового объекта с именем MyObject. Созданный объект будет доступен по ссылку myObject.

После чего, у нас появляется возможность из кода HUDManager обращаться к скрипту ScoresManager, таким образом:

Все просто, но игра, не ограничивается одними набранными очками, HUD может отображать текущие жизни игрока, меню доступных действия игрока, информацию о уровне и многое другое. Игра может насчитывать в себе десятки и сотни различных скриптов, которым нужно получать информацию друг от друга.
Чтобы получить в одном скрипте данные из другого скрипта нам каждый раз придется описывать переменную в одном скрипте и назначать (перетаскивать вручную) ее с помощью редактора, что само по себе нудная работа, которую легко можно забыть сделать и потом долго искать какая из переменных не инициализирована.
Если мы захотим что-то отрефакторить, переименовать скрипт, то все старые инициализации в иерархии объектов, связанные с переименованным скриптом, сбросятся и придется их назначать снова.
В то же время, такой механизм не работает для префабов (prefab) — динамического создания объектов из шаблона. Если какому-либо префабу нужно обращаться к менеджеру, расположенному в иерархии объектов, то вы не сможете назначить самому префабу элемент из иерархии, а придется сначала создать объект из префаба и после этого программно присвоить экземпляр менеджера переменной только что созданного объекта. Не нужная работа, не нужный код, дополнительная связанность.
Следующий подход решает все эти проблемы.

Подход 2. «Синглтоны»

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

Примеры

Как правило, в единственном экземпляре существуют скрипты, отвечающие за общую логику пользовательского интерфейса, за проигрывание музыки, за отслеживание условий завершения уровня, за управление системой заданий, за отображение спецэффектов и так далее.
В то же время, скрипты игровых объектов существуют в большом количестве экземпляров: каждая птичка из «Angry Birds» управляется экземпляром скрипта птички со своим уникальным состоянием; для любого юнита в стратегии создается экземпляр скрипта юнита, содержащий его текущее количество жизней, позицию на поле и личную цель; поведение пяти разных иконок обеспечивается различными экземплярами одних и тех же скриптов, отвечающих за это поведение.
В примере из предыдущего шага скрипты HUDManager и ScoresManager всегда существуют в единственном экземпляре. Для их взаимодействия друг с другом применим паттерн «синглтон» (Singleton, он же одиночка).
В классе ScoresManager опишем статическое свойство типа ScoresManager, в котором будет храниться единственный экземпляр менеджера очков:

Осталось инициализировать свойство Instance экземпляром класса, который создает среда Unity3D. Так как ScoresManager наследник MonoBehaviour, то он участвует в жизненном цикле всех активных скриптов в сцене и во время инициализации скрипта у него вызывается метод Awake. В этот метод мы и поместить код инициализации свойства Instance:

После чего, использовать ScoresManager из других скриптов можно следующим образом:

Теперь нет необходимости в HUDManager описывать поле типа ScoresManager и назначать его в редакторе Unity3D, любой «скрипт-менеджер» может предоставлять доступ к себе через статическое свойство Instance, которое будет инициализировать в функции Awake.

Плюсы

— нет необходимости описывать поле скрипта и назначать его через редактор Unity3D.
— можно смело рефакторить код, если что и отвалится, то компилятор даст знать.
— к другим «скриптам-менеджерам» теперь можно обращаться из префабов, через свойство Instance.

Минусы

— подход обеспечивает доступ только к «скриптам-менеджерам», существующим в единственном экземпляре.
— сильная связанность.
На последнем «минусе» остановимся подробнее.
Пусть мы разрабатываем игру, в которой есть персонажи (unit) и эти персонажи могут погибать (die).
Где-то находится участок кода, который проверяет не погиб ли наш персонаж:

Получается, что персонаж после совей смерти должен разослать всем компонентам, которые в ней заинтересованы этот печальный факт, он должен знать о существовании этих компонентов и должен знать, что они им интересуются. Не слишком ли много знаний, для маленького юнита?
Так как игра, по логике, очень связанная структура, то и события происходящие в других компонентах интересуют третьи, юнит тут ничем не особенный.
Примеры таких событий (далеко не все):
— Условие прохождение уровня зависит от количества набранных очков, набрали 1000 очков – прошли уровень (LevelConditionManager связан с ScoresManager).
— Когда набираем 500 очков, достигаем важную стадию прохождения уровня, нужно проиграть веселую мелодию и визуальный эффект (ScoresManager связан с EffectsManager и SoundsManager).
— Когда персонаж восстанавливает здоровье, нужно проиграть эффект лечения над картинкой персонажа в панели персонажа (UnitsPanel связан с EffectsManager).
— и так далее.
В результате таких связей мы приходим к картине похожей на следующую, где все про всех все знают:

как привязать скрипт к объекту unity. . как привязать скрипт к объекту unity фото. как привязать скрипт к объекту unity-. картинка как привязать скрипт к объекту unity. картинка . Создание нового игрового объекта с именем MyObject. Созданный объект будет доступен по ссылку myObject.

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

Подход 3. Мировой эфир (Event Aggregator)

Введем специальный компонент «EventAggregator», основная функция которого хранить список событий, происходящих в игре.
Событие в игре — это функционал, предоставляющий любому другому компоненту возможность как подписаться на себя, так и опубликовать факт совершения этого события. Реализация функционала события может быть любой на вкус разработчика, можно использовать стандартные решения языка или написать свою реализацию.
Пример простой реализации события из прошлого примера (о смерти юнита):

Добавляем это событие в «EventAggregator»:

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

А любой компонент, которому интересно это событие, может отреагировать на него следующим образом (на примере менеджера отвечающего за количество набранных очков):

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

как привязать скрипт к объекту unity. . как привязать скрипт к объекту unity фото. как привязать скрипт к объекту unity-. картинка как привязать скрипт к объекту unity. картинка . Создание нового игрового объекта с именем MyObject. Созданный объект будет доступен по ссылку myObject.

Замечание

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

Плюсы

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

Минусы

— нужно постоянно описывать новые события и добавлять их в мир.
— нарушение функциональной атомарности.

Последний минус рассмотрим более детально

Представим, что у нас есть объект «ObjectA», в котором вызывается метод «MethodA». Метод «MethodA», состоит из трех шагов и вызывает внутри себя три других метода, которые выполняют эти шаги последовательно («MethodA1», «MethodA2» и «MethodA3»). Во втором методе «MethodA2» происходит публикация какого-то события. И тут происходит следующее: все кто подписан на это событие начнут его обрабатывать, выполняя какую-то свою логику. В этой логике тоже может произойти публикация других событий, обработка которых также может привести к публикации новых событий и так далее. Дерево публикаций и реакции в отдельных случаях может очень сильно разрастись. Такие длинные цепочки крайне тяжело отлаживать.
Но самая страшная проблема, которая тут может произойти, это когда одна из веток цепочки приводит обратно в «ObjectA» и начинает обрабатывать событие путем вызова какого-то другого метода «MethodB». Получается, что метод «MethodA» у нас еще не выполнил все шаги, так как был прерван на втором шаге, и содержит сейчас в себе не валидное состояние (в шаге 1 и 2 мы изменили состояние объекта, но последнее изменение из шага 3 еще не сделали) и при этом начинается выполняться «MethodB» в этом же объекте, имея это не валидное состояние. Такие ситуации порождают ошибки, очень сложно отлавливаются, приводят к тому, что надо контролировать порядок вызова методов и публикации событий, когда по логике этого делать нет необходимости и вводят дополнительную сложность, которую хотелось бы избежать.

Решение

Решить описанную проблему не сложно, достаточно добавить функционал отложенной реакции на событие. В качестве простой реализации такого функционала мы можем завести хранилище, в которое будем складывать произошедшие события. Когда событие произошло, мы не выполняем его немедленно, а просто сохраняем где-то у себя. И в момент наступления очереди выполнения функционала какой-то компоненты в игре (в методе Update, например) мы проверяем на наличие произошедших событий и выполняем обработку, если есть такие события.
Таким образом, при выполнении метода «MethodA» не происходит его прерывание, а опубликованное событие все заинтересованные записывают себе в специальное хранилище. И только после того как к заинтересованным подписчикам дойдет очередь, они достанут из хранилища событие и обработают его. В этот момент весь «MethodA» будет завершен и «ObjectA» будет иметь валидное состояние.

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *