Какие существуют события в unity. Порядок событий в Unity3D, и использование Coroutine


Доброго времени суток!

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

Разбирать все это дело мы будем на примере системы управления звуком. Данная система будет позволять нам включать/отключать музыку и звуки в настройках игры.

Прежде чем мы создадим класс-менеджер, который будет управлять настройками звука и музыки, мы создадим простой сериализуемый класс. Он будет использоваться в качестве модели данных для сохранения в JSON.

Using System.Collections; using System.Collections.Generic; using UnityEngine; //============================================= // AudioSettingsModel // @usage model for audio settings // // Developed by CodeBits Interactive // https://cdbits.net/ //============================================= public class AudioSettingsModel { public bool music = true; // Флаг, отвечающий за музыку public bool sounds = true; // Флаг, отвечающий за звуки }
Теперь можно приступить к написанию класса-менеджера. Его мы будем устанавливать на самой первой сцене. Данный менеджер будет глобальным объектом и не будет удаляться при переходе со сцены на сцену.

Using System.Collections; using System.Collections.Generic; using UnityEngine; using System.IO; //============================================= // Audio Manager // @usage works with audio settings // // Developed by CodeBits Interactive // https://cdbits.net/ //============================================= public class AudioManager: MonoBehaviour{ // Публичные параметры public static AudioManager instance = null; // Инстанс менеджера public static AudioSettingsModel settings = null; // Модель аудио настроек private static string _settings_path = ""; // Путь к файлу настроек аудио // Инициализация менеджера void Awake(){ // Устанавливаем путь для сохранения настроек _settings_path = Application.persistentDataPath + "/audioSettings.gdf"; // Проверяем, задан ли инстанс нашего менеджера if (instance == null){ // Инстанс не задан instance = this; // Установить в инстанс текущий объект } // Устанавливаем параметр, который указывает на то, // что данный объект не должен удаляться при выгрузке // уровня DontDestroyOnLoad(gameObject); // Инициализируем настройки нашего менеджера InitializeSettings(); } // Инициализация менеджера private void InitializeSettings(){ // Если модель настроек не задана if (settings == null) settings = new AudioSettingsModel(); // Создаем новую модель if (File.Exists(_settings_path)){ // Если существует файл с настройками loadSettings(); // Загружаем файл настроек звука } } // Загрузить аудио настройки public void loadSettings(){ string _data = File.ReadAllText(_settings_path); // Считываем весь текст из файла settings = JsonUtility.FromJson(_data); // Десериализуем его в текущую модель } // Сохранить аудио настройки public void saveSettings(){ string _json_data = JsonUtility.ToJson(settings); // Сериализуем текущие настройки из модели File.WriteAllText(_settings_path, _json_data); // Сохраняем в наш файл } // Создаем делегаты для нашего события, которое в дальнейшем // будет использоваться для отслеживания изменений настроек аудио public delegate void AudioSettingsChanged(); // Добавить новый делегат public event AudioSettingsChanged OnAudioSettingsChanged; // Создать на его основе событие // Включить/выключить звуки public void toggleSounds(bool enabled){ settings.sounds = enabled; // Изменить настройки звуков в текущей модели saveSettings(_settings_path, settings); // Сохранить настройки if (OnAudioSettingsChanged != null) OnAudioSettingsChanged(); // Вызвать наше событие } // Включить/выключить музыку public void toggleMusic(bool enabled){ settings.music = enabled; // Изменить настройки музыки в текущей модели saveSettings(_settings_path, settings); // Сохранить настройки if (OnAudioSettingsChanged != null) OnAudioSettingsChanged(); // Вызвать наше событие } }
Теперь, когда менеджер готов, вы можете создать на вашей начальной сцене пустой объект и назвать его, к примеру "_AUDIO_MANAGER" , после чего добавить на него наш класс-менеджер. Сделать это можно просто вызвав меню добавления компонента на объекте и выбрав «Game Managers» => «Audio Manager» .

После этого, нам необходимо написать компонент, который мы будем пристыковывать к каждому объекту с AudioSource.

Using System.Collections; using System.Collections.Generic; using UnityEngine; //============================================= // Audio Muter // @usage on/off audio sources on objects // // Developed by CodeBits Interactive // https://cdbits.net/ //============================================= public class AudioMuter: MonoBehaviour { // Публичные параметры компонента public bool is_music = false; // Данный флаг дает понять нашему классу, является ли AudioSource звуком или музыкой. // Приватные параметры private AudioSource _as; // AudioSource private float _base_volume = 1F; // Базовая громкость AudioSource // Инициализация объекта void Start(){ // Получаем компонент AudioSource и его изначальную громкость _as = this.gameObject.GetComponent(); // Получить компонент _base_volume = _as.volume; // Получить базовую громкость // Здесь мы добавляем слушатель, который будет выполнять метод _audioSettingsChanged, // когда настройки музыки/звуков были изменены AudioManager.instance.OnAudioSettingsChanged += _audioSettingsChanged; // Установить // Ну и на старте мы должны проверить текущее состояние звуков/музыки _audioSettingsChanged(); } // При уничтожении объекта void OnDestroy(){ AudioManager.instance.OnAudioSettingsChanged -= _audioSettingsChanged; // Уничтожаем слушатель } // Данный метод служит для включения/отключения громкости AudioSource private void _audioSettingsChanged(){ if (is_music) _as.volume = (AudioManager.settings.music) ? _base_volume: 0F; if (!is_music) _as.volume = (AudioManager.settings.sounds) ? _base_volume: 0F; } }
Таким образом мы можем управлять звуками/музыкой в игре. Данный пример не в коем случае не говорит, как следует делать это правильно, а лишь демонстрирует работу системы событий и слушателей в Unity3D.

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

Public delegate void AudioSettingsChanged(); public event AudioSettingsChanged OnAudioSettingsChanged;
Вы можете установить выполнение слушателя при определенных условиях и цеплять к ним определенные методы, которые будут выполняться при достижении этих условий.

А при помощи делегатов, на основе которого мы создали слушатель, вы можете создавать Callback-функции. Особенно полезно это может быть для асинхронных методов (к примеру при отправке асинхронных POST-запросов).

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

Вы можете помочь и перевести немного средств на развитие сайта

Всех приветствую. Сегодня я хочу рассказать о том, как можно реализовать игровые события, представить их, а также, как в два клика сделать вполне приемлемую систему игровых событий в Unity не прибегая к разным UnityAction"s.

Примечание : Эта статья не истина истин. Это лишь моё представление и мой опыт.

нет времени делать картинки к статье, а надо! Поэтому картинка из гугла!

Приведу пример с лифтом. Что мы знаем о нём? Он может передвигаться вверх или вниз, зависимо от нажатия кнопки. Однако стоит помнить,
что также он открывает/закрывает двери, воспроизводит звук и может что нибудь ещё. Я уже давно привык рассматривать многие вещи абстрактно,
поэтому могу сказать, что игровые события можно разделить на два компонента. Первый - Обработчики. Второй - события.
Обработчики - скрипты, вызывающие игровые события. Я разделяю вызов событий на несколько вариантов:

  1. Линейный . Этот вариант означает, что N"ое кол-во событий выполняется после следующего.
  2. Параллельный . Вызов более одного события на одном обработчике.
  3. Смешанный . Это смесь первого и второго варианта.

Вернёмся к лифту. Можно сразу сказать, что лифт относится к варианту №3. Поясню. Когда мы нажимаем кнопку, срабатывает событие движения лифта. Далее, когда он находится на вашем этаже, открываются двери и, допустим, воспроизводится звук. Открытие дверей и звук происходят параллельно, однако после движения. Можно это сразу представить в реализации игры. Но позже.

Чтобы вы понимали, что я имею в виду под обработчиками, вот примеры: Триггеры, нажатые кнопки (клавиши клавиатуры), лучи (Raycast) и т.п.
Ну а под триггерами: Воспроизведение звуков, движение объектов и т.п.

Абстрактно, я создал следующую архитектуру:

Кажется вполне простой, не так ли? Даже в программном плане не нужно нагружать извилины (хотя придумать реализацию мне составило труда)

Вот как выглядит базовый класс всех событий:

Using UnityEngine; // System.Collections.Generics; public abstract class CoreEvent: MonoBehaviour { public CoreEvent nextEvent; // Следующее событие // public List listEvents; public abstract void Action(); }

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

Using UnityEngine; public class LookEvent: CoreEvent { public override void Action() { //if(nextEvent != null) nextEvent.Action(); //else print("Look"); } }

В этом примере я вызываю лишь одно следующее событие. Для списка/массива использовать цикл. Если должно сработать одно событие,
ничего не указываем в поле nextEvent и всё, т.к. в реализации мы уже позаботились об обработке на null"ое значение.

Один момент касательно реализации. Изначально, базовый класс событий не предназначался быть абстрактным. Объясню почему. Как вы и увидели
сами, условие на null"ое значение находится в одном из событий. Т.е. такое условие делается для каждого события. На самом деле это не обязательно, если вы не используете параллельную обработку. Изначально, класс был не абстрактным и метод Action() был виртуальным (virtual),
что позволяло сделать проверку в базовом классе, прописав один раз, а далее делать так:

Public ovveride void Action() { base.Action(); //Какая-то функция }

Однако, тут кому как удобнее. Я решил сделать его абстрактным так как это существенно уменьшило объём кода в базовом классе события + это,
с точки зрения программирования, более логично, если знать определение ключевого слова abstract.

Public class HandlerEvents: MonoBehaviour { public List events; // Указываем базовый класс. public virtual void Launch() { for (byte countEvent = 0; countEvent < events.Count; countEvent++) { if (events != null) events.Action(); // Вызываем только те события, которые указаны в листе (не null). } } } public enum TriggerType { Enter, Stay, Exit } public class TriggerHandler: HandlerEvents { public TriggerType triggerType; private void OnTriggerEnter2D(Collider2D other) { if (triggerType == TriggerType.Enter && other.GetComponent()) Launch(); } private void OnTriggerExit2D(Collider2D other) { if (triggerType == TriggerType.Exit && other.GetComponent()) Launch(); } private void OnTriggerStay2D(Collider2D other) { if (triggerType == TriggerType.Stay && other.GetComponent()) Launch(); } }

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

Вот вроде бы и всё. Если есть вопросы, задавайте!

В скором времени расскажу о многих интересных принципах, которые я использую, которые могут помочь при работе с Unity.
Ну там организация проекта, чтение с XML, организация кода, и т.д. Скоро!

Cобытия в Unity3D делятся на три большие группы:

События, вызываемые по событиям масло масляное (загрузка сцены, выход пользователя)
Данная группа событий выполняется на нерегулярной основе

События, вызываемые при прорисовке кадра
В этом случае все используемые скрипты вызываются в цикле прорисовки экрана, а значит, будут непосредственно влиять на FPS (частоту кадров в секунду). Поэтому здесь нужно очень аккуратно работать с функциями, которые требуют много времени на обработку.

События, вызываемые при расчёте физики
И последняя группа. Для расчёта физики создаётся отдельная, независимая нить, события в которой вызываются с определённым интервалом времени. Размер этого интервала можно настроить в пункте меню: Edit -> Project Settings -> Time -> Fixed Timestep.

Зная эту разбивку Вы уже можете принимать решения о том, где какой код лучше расположить.
Однако, все вычисления, производимые как при расчёте физики, так и при прорисовке, влияют на «отзывчивость» игры. Поэтому, при разработке приложения, наиболее ресурсоёмкие вычисления желательно оформлять в сопрограммах (Coroutine).

Теперь перейдём непосредственно к переводу раздела справки.

Порядок выполнения функций событий
В Unity3D, существует целый ряд событий, выполняемых в определенном порядке. Этот порядок мы опишем ниже:

Awake: Эта функция всегда вызывается до начала любых функций, а также сразу после инициализации префаба.
OnEnable: (вызывается, если объект является активным): Эта функция вызывается только после того, как объект будет включен.

До первого обновления кадров

Start: вызывается перед прорисовкой первого фрейма, только если сценарий определён.

В промежутке между кадрами

OnApplicationPause: Это событие вызывается в конце кадра, когда обнаружена пауза, фактически между обычными обновлениями кадров. После OnApplicationPause прорисовывается один дополнительный кадр для того, чтобы показать окно, которое отображается во время паузы.

Порядок обновления
Для отслеживания логики игры, взаимодействия и анимации объектов, положения камеры и т.д., есть несколько различных событий, которые Вы можете использовать. Общий механизм для выполнения большинства задач находится в функции Update(), но есть и другие функции.

FixedUpdate: FixedUpdate() не зависит от Update(), и может вызываться как чаще него так и реже (обычно вызывается реже, если FPS достаточно высок). Это событие может быть вызвано несколько раз в кадре, если FPS низкий а может быть и вообще не вызвано между кадрами, если FPS высокий. Все физические расчеты движка и обновление происходит сразу после FixedUpdate(). При применении расчетов движения внутри FixedUpdate(), вам не нужно умножать ваше значение на Time.deltaTime. Это потому, что FixedUpdate() вызывается из таймера, независимого от частоты кадров.
Update: Update() вызывается один раз за кадр. Это основное событие для прорисовки кадра.
LateUpdate: LateUpdate() вызывается один раз в кадре, после завершения Update(). Любые расчеты, которые осуществляются в Update() будет завершены, при вызове LateUpdate(). Основным использованием LateUpdate() обычно является слежение за камерой от третьего лица. Если Вы осуществите движение Вашего персонажа в событии Update(), то движения камеры и расчётов её месторасположения можете вести в событии LateUpdate(). Это будет гарантировать, что персонаж прошел полностью перед камерой, и закрепил свое расположение.

Отрисовка сцены (Rendering)

OnPreCull: Вызывается перед сборкой сцены на камере. Сборка определяет, какие объекты видны камере. OnPreCull вызывается, только если будет происходить «обрезка» сцены от невидимых объектов.
OnBecameVisible / OnBecameInvisible: Вызывается, когда объект становится видимым / невидимым для любой камеры.
OnWillRenderObject: Вызывается один раз для каждой камеры, если объект является видимым.
OnPreRender: Вызывается перед тем, как на камеру начинается отрисовка сцены
OnRenderObject: Вызывается, когда все объекты сцены прорисованы. Вы можете использовать функции GL или Graphics.DrawMeshNow, что-бы создать свои рисунки на этой камере.
OnPostRender: Вызывается после завершения отрисовки сцены на камере.
OnRenderImage (только Pro версия): Вызывается после прорисовки сцены, для постобработки изображения на экране.
OnGUI: вызывается несколько раз в кадре в ответ на события интерфейса. События расположения и заполнения цветом обрабатываются в первую очередь, а затем события ввода с клавиатуры / мыши.
OnDrawGizmos: Используется для рисования Gizmo на сцене.

Сопрограммы
Обычно вызов сопрограммы выполняется после возвращения функции Update(). Сопрограмма это функция, которая может приостановить исполнение (yield), пока не будет выполнена. Различные виды использования Сопрограмм:

yield: сопрограмма будет продолжена после всех функций Update(), которые будут вызваны в следующем кадре.
yield WaitForSeconds(2): Продолжить после указанного времени задержки, когда все функции Update() уже были вызваны в кадре
yield WaitForFixedUpdate(): Продолжается, когда все функции FixedUpdate() уже были вызваны
yield WWW: Продолжается, когда загрузка WWW-контента завершена.
yield StartCoroutine(MyFunc): Связи сопрограмм, вызов сопрограммы будет ожидать завершения функции MyFunc.

Разрушение объектов

OnDestroy: Эта функция вызывается для последнего кадра существования объекта (объект может быть уничтожен в ответ на Object.Destroy или при закрытии сцены).

При выходе
Эти функции вызываются для всех активных объектов в сцене:

OnApplicationQuit: Эта функция вызывается для всех игровых объектов перед закрытием приложения. В редакторе это происходит, когда пользователь прекращает PlayMode. В веб-плеер это происходит при закрытии веб-плеера.
OnDisable: Эта функция вызывается, когда объект отключается или становится неактивным.

Таким образом, происходит такой порядок выполнения скриптов:

Все события Awake
Все события Start
цикл (с шагом в переменной delta time)
-все функции FixedUpdate
-отработка физического движка
-события триггеров OnEnter/Exit/Stay
-события столкновений OnEnter/Exit/Stay
Rigidbody преобразования, согласно transform.position и вращения
OnMouseDown/OnMouseUp др. события ввода
Все события Update()
Анимация, смешение и трансформация
Все события LateUpdate
Прорисовка (Rendering)

Советы
Если Вы запускаете сопрограммы в LateUpdate, то они также будут вызваны после LateUpdate непосредственно перед рендерингом.
Сопрограммы выполняются после всех функций Update().

Скрипт в Unity не похож на традиционную идею программы, где код работает постоянно в цикле, пока не завершит свою задачу. Вместо этого, Unity периодически передаёт управление скрипту при вызове определённых объявленных в нём функций. Как только функция завершает исполнение, управление возвращается обратно к Unity. Эти функции известны как функции событий, т.к. их активирует Unity в ответ на события, которые происходят в процессе игры. Unity использует схему именования, чтобы определить, какую функцию вызвать для определённого события. Например, вы уже видели функцию Update (вызывается перед сменой кадра) и функцию Start (вызывается прямо перед первым кадром объекта). В Unity доступно гораздо большее количество функций событий; полный список с дополнительной информацией по их применению можно найти на странице документации класса MonoBehaviour. Далее указаны одни из самых важных и часто встречающихся событий.

Обычные Update события

Игра - это что-то вроде анимации, в которой кадры генерируются на ходу. Ключевой концепт в программировании игр заключается в изменении позиции, состояния и поведения объектов в игре прямо перед отрисовкой кадра. Такой код в Unity обычно размещают в функции Update . Update вызывается перед отрисовкой кадра и перед расчётом анимаций.

Void Update() { float distance = speed * Time.deltaTime * Input.GetAxis("Horizontal"); transform.Translate(Vector3.right * distance); }

Физический движок также обновляется фиксированными по времени шагами, аналогично тому как работает отрисовка кадра. Отдельная функция события FixedUpdate вызывается прямо перед каждым обновлением физических данных. Т.к. обновление физики и кадра происходит не с одинаковой частотой, то вы получите более точные результаты от кода физики, если поместите его в функцию FixedUpdate, а не в Update.

Void FixedUpdate() { Vector3 force = transform.forward * driveForce * Input.GetAxis("Vertical"); rigidbody.AddForce(force); }

Также иногда полезно иметь возможность внести дополнительные изменения в момент, когда у всех объектов в сцене отработали функции Update и FixedUpdate и рассчитались все анимации. В качестве примера, камера должна оставаться привязанной к целевому объекту; подстройка ориентации камеры должна производиться после того, как целевой объект сместился. Другим примером является ситуация, когда код скрипта должен переопределить эффект анимации (допустим, заставить голову персонажа повернуться к целевому объекту в сцене). В ситуациях такого рода можно использовать функцию LateUpdate .

Void LateUpdate() { Camera.main.transform.LookAt(target.transform); }

События инициализации

Зачастую полезно иметь возможность вызвать код инициализации перед любыми обновлениями, происходящими во время игры. Функция Start вызывается до обновления первого кадра или физики объекта. Функция Awake вызывается для каждого объекта в сцене в момент загрузки сцены. Учтите, что хоть для разных объектов функции Start и Awake и вызываются в разном порядке, все Awake будут выполнены до вызова первого Start. Это значит, что код в функции Start может использовать всё, что было сделано в фазе Awake.

События GUI

В Unity есть система для отрисовки элементов управления GUI поверх всего происходящего в сцене и реагирования на клики по этим элементам. Этот код обрабатывается несколько иначе, нежели обычное обновление кадра, так что он должен быть помещён в функцию OnGUI , которая будет периодически вызываться.

Void OnGUI() { GUI.Label(labelRect, "Game Over"); }

Вы также можете определять события мыши, которые срабатывают у GameObject’а, находящегося в сцене. Это можно использовать для наведения орудий или для отображения информации о персонаже под указателем курсора мыши. Существует набор функций событий OnMouseXXX (например, OnMouseOver , OnMouseDown), который позволяет скрипту реагировать на действия с мышью пользователя. Например, если кнопка мыши нажата в то время, когда курсор мыши находится над определённым объектом, то, если в скрипте этого объекта присутствует функция OnMouseDown, она будет вызвана.

События физики

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

Работая с игровым движком Unity3D одним из первых понятий, которое необходимо изучить — это порядок вызова событий в игре и обработка этих самых событий.

Хотя информации на русском языке об этом движке довольно много, но корректного описания событий я не нашёл. Зато нашёл англоязычную статью Execution Order of Event Functions из справки Unity3D.

Предисловие переводчика

Но, перед началом официальной справки, хочу отметить, что события в Unity3D делятся на четыре большие группы:

  1. События, вызываемые при прорисовке кадра
    В этом случае все используемые скрипты вызываются в цикле прорисовки экрана, а значит, будут непосредственно влиять на FPS (частоту кадров в секунду). Поэтому здесь нужно очень аккуратно работать с функциями, которые требуют много времени на обработку.
  2. События, вызываемые при расчёте физики
    Для расчёта физики создаётся отдельная, независимая нить, события в которой вызываются с определённым интервалом времени. Размер этого интервала можно настроить в пункте меню: Edit -> Project Settings -> Time -> Fixed Timestep.
  3. Сопрограммы (Корутины).
    И последняя группа. Если очень отдалённо, то их можно сравнивать с отдельными процессами, но поскольку в Unity3D не допускается создание отдельных потоков, то это некий компромисс. Он позволяет прерывать вычисления, отдавать ресурсы в основной поток, а потом возобновлять следующую часть вычислений. Яркий пример — постоянный перерасчёт стоимости товаров на разных торговых точках. Он будет идти сам по себе, не будет тормозить процесс игры, и цены в магазинах всегда будут актуальными.

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

Однако, все вычисления, производимые как при расчёте физики, так и при прорисовке, влияют на «отзывчивость» игры. Поэтому, при разработке приложения, не забывайте наиболее ресурсоёмкие вычисления делить на этапы и оформлять в сопрограммах (Coroutine).

Теперь перейдём непосредственно к переводу раздела справки.

Порядок выполнения функций событий

В Unity3D, существует целый ряд событий, выполняемых в определенном порядке. Этот порядок мы опишем ниже:

Первая загрузка сцены

Эти функции вызываются, когда сцена стартует (по одному разу для каждого объекта в кадре).

  • Awake: Эта функция всегда вызывается до начала любых функций, а также сразу после инициализации префаба.
  • OnEnable: (вызывается, если объект является активным): Эта функция вызывается только после того, как объект будет включен.

До первого обновления кадров

  • Start: вызывается перед прорисовкой первого фрейма, только если сценарий определён.

В промежутке между кадрами

  • OnApplicationPause: Это событие вызывается в конце кадра, когда обнаружена пауза, фактически между обычными обновлениями кадров. После OnApplicationPause прорисовывается один дополнительный кадр для того, чтобы показать окно, которое отображается во время паузы.

Порядок обновления

Для отслеживания логики игры, взаимодействия и анимации объектов, положения камеры и т.д., есть несколько различных событий, которые Вы можете использовать. Общий механизм для выполнения большинства задач находится в функции Update(), но есть и другие функции.

  • FixedUpdate: FixedUpdate() не зависит от Update(), и может вызываться как чаще него так и реже (обычно вызывается реже, если FPS достаточно высок). Это событие может быть вызвано несколько раз в кадре, если FPS низкий а может быть и вообще не вызвано между кадрами, если FPS высокий. Все физические расчеты движка и обновление происходит сразу после FixedUpdate(). При применении расчетов движения внутри FixedUpdate(), вам не нужно умножать ваше значение на Time.deltaTime. Это потому, что FixedUpdate() вызывается из таймера, независимого от частоты кадров.
  • Update: Update() вызывается один раз за кадр. Это основное событие для прорисовки кадра.
  • LateUpdate: LateUpdate() вызывается один раз в кадре, после завершения Update(). Любые расчеты, которые осуществляются в Update() будет завершены, при вызове LateUpdate(). Основным использованием LateUpdate() обычно является слежение за камерой от третьего лица. Если Вы осуществите движение Вашего персонажа в событии Update(), то движения камеры и расчётов её месторасположения можете вести в событии LateUpdate(). Это будет гарантировать, что персонаж прошел полностью перед камерой, и закрепил свое расположение.

Отрисовка сцены (Rendering)

  • OnPreCull: Вызывается перед сборкой сцены на камере. Сборка определяет, какие объекты видны камере. OnPreCull вызывается, только если будет происходить «обрезка» сцены от невидимых объектов.
  • OnBecameVisible / OnBecameInvisible: Вызывается, когда объект становится видимым / невидимым для любой камеры.
  • OnWillRenderObject: Вызывается один раз для каждой камеры, если объект является видимым.
  • OnPreRender: Вызывается перед тем, как на камеру начинается отрисовка сцены
  • OnRenderObject: Вызывается, когда все объекты сцены прорисованы. Вы можете использовать функции GL или Graphics.DrawMeshNow, что-бы создать свои рисунки на этой камере.
  • OnPostRender: Вызывается после завершения отрисовки сцены на камере.
  • OnRenderImage (только Pro версия): Вызывается после прорисовки сцены, для постобработки изображения на экране.
  • OnGUI: вызывается несколько раз в кадре в ответ на события интерфейса. События расположения и заполнения цветом обрабатываются в первую очередь, а затем события ввода с клавиатуры / мыши.
  • OnDrawGizmos: Используется для рисования Gizmo на сцене.

Сопрограммы

Обычно вызов сопрограммы выполняется после возвращения функции Update(). Сопрограмма это функция, которая может приостановить исполнение (yield), пока не будет выполнена. Различные виды использования Сопрограмм:

  • yield: сопрограмма будет продолжена после всех функций Update(), которые будут вызваны в следующем кадре.
  • yield WaitForSeconds(2): Продолжить после указанного времени задержки, когда все функции Update() уже были вызваны в кадре
  • yield WaitForFixedUpdate(): Продолжается, когда все функции FixedUpdate() уже были вызваны
  • yield StartCoroutine(MyFunc): Связи сопрограмм, вызов сопрограммы будет ожидать завершения функции MyFunc.

Разрушение объектов

  • OnDestroy: Эта функция вызывается для последнего кадра существования объекта (объект может быть уничтожен в ответ на Object.Destroy или при закрытии сцены).

При выходе

Эти функции вызываются для всех активных объектов в сцене:

  • OnApplicationQuit: Эта функция вызывается для всех игровых объектов перед закрытием приложения. В редакторе это происходит, когда пользователь прекращает PlayMode. В веб-плеер это происходит при закрытии веб-плеера.
  • OnDisable: Эта функция вызывается, когда объект отключается или становится неактивным.

Таким образом, при завершении происходит вызов событий в следующем порядке:

  • Все события Awake
  • Все события Start
  • цикл (с шагом в переменной delta time)
    • Все функции FixedUpdate
    • отработка физического движка
    • события триггеров OnEnter/Exit/Stay
    • события столкновений OnEnter/Exit/Stay
  • Rigidbody преобразования, согласно transform.position и вращения
  • OnMouseDown/OnMouseUp др. события ввода
  • Все события Update()
  • Анимация, смешение и трансформация
  • Все события LateUpdate
  • Прорисовка (Rendering)

Если Вы запускаете сопрограммы в LateUpdate, то они также будут вызваны после LateUpdate непосредственно перед рендерингом.

Сопрограммы выполняются после всех функций Update().

P.S. дополнение от пользователя Leopotam

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

Например:

IEnumerator FindBozons() { var isFound = false ; var colliderSectionID = 0 ; var colliderSectionCount = 10 ; while (! isFound) { // Обрабатываем только одну секцию за раз, чтобы снизить нагрузку isFound = ProcessDataFromSection(colliderSectionID) ; colliderSectionID = (colliderSectionID ++ ) % colliderSectionCount; yield return null; } // Покупаем яхты / пароходы // Сопрограмма завершается } void Start() { StartCoroutine(FindBozons() ) ; }

Механизм сопрограмм обеспечит автоматическое сохранение состояние контекста исполнения функции и возврат в место прерывания (yield).