WaitForEndOfFrame в Unity C# — практическое руководство
WaitForEndOfFrame в Unity приостанавливает корутину до самого конца кадра — после того, как отрисованы все камеры и интерфейс, но ещё до вывода кадра на экран. Именно в этой точке содержимое экрана полностью готово, и с ним можно работать: читать пиксели, кодировать в изображение, отправлять на сервер.
Сигнатура
- Класс:
UnityEngine.WaitForEndOfFrame, наследуется отYieldInstruction - Параметры: нет — объект создаётся пустым:
new WaitForEndOfFrame() - Использование: только через
yield returnвнутри корутины
Почему нельзя прочитать экран раньше
Порядок кадра в Unity: обновление логики → физика → отрисовка камер → интерфейс → вывод на экран. Если вызвать ReadPixels в Update или в начале корутины, буфер содержал бы кадр прошлого раза или пустоту — рисование ещё не произошло. WaitForEndOfFrame как раз и закрывает эту разницу.
Пример: скриншот и его загрузка на сервер
Классический сценарий из документации Unity:
using System.Collections;
using UnityEngine;
using UnityEngine.Networking;
public class ScreenshotUploader : MonoBehaviour
{
IEnumerator Start()
{
UploadPNG();
yield return null;
}
IEnumerator UploadPNG()
{
// Ждём, пока все камеры и GUI отрисованы
yield return new WaitForEndOfFrame();
// Текстура размером с экран, формат RGB24
int width = Screen.width;
int height = Screen.height;
Texture2D tex = new Texture2D(width, height, TextureFormat.RGB24, false);
// Чтение содержимого экрана в текстуру
tex.ReadPixels(new Rect(0, 0, width, height), 0, 0);
tex.Apply();
// Кодирование в PNG
byte[] bytes = tex.EncodeToPNG();
Destroy(tex);
// Отправка на сервер
WWWForm form = new WWWForm();
form.AddField("frameCount", Time.frameCount.ToString());
form.AddBinaryData("fileUpload", bytes);
var request = UnityWebRequest.Post("http://localhost/upload", form);
yield return request.SendWebRequest();
if (request.result != UnityWebRequest.Result.Success)
Debug.LogError(request.error);
else
Debug.Log("Скриншот загружен");
}
}
Обратите внимание на порядок: сначала WaitForEndOfFrame, затем ReadPixels. Условие в if на request.result — актуальный синтаксис UnityWebRequest.
Пример: отрисовка поверх сцены через GL
Вторая область применения — код, который должен выполниться после всей отрисовки: например, показ альфа-канала экрана в режиме игры. Unity держит встроенный шейдер Hidden/Internal-Colored, который для этого подходит:
using System.Collections;
using UnityEngine;
public class AlphaViewer : MonoBehaviour
{
Material mat;
void CreateMaterial()
{
var shader = Shader.Find("Hidden/Internal-Colored");
mat = new Material(shader);
mat.hideFlags = HideFlags.HideAndDontSave;
// Режим смешивания: показываем альфа-канал назначения
mat.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.DstAlpha);
mat.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.Zero);
// Отключаем отсечение задних граней, запись и тест глубины
mat.SetInt("_Cull", (int)UnityEngine.Rendering.CullMode.Off);
mat.SetInt("_ZWrite", 0);
mat.SetInt("_ZTest", (int)UnityEngine.Rendering.CompareFunction.Always);
}
IEnumerator Start()
{
CreateMaterial();
while (true)
{
// Ждём полной отрисовки кадра вместе с интерфейсом
yield return new WaitForEndOfFrame();
GL.PushMatrix();
GL.LoadOrtho();
mat.SetPass(0);
GL.Begin(GL.QUADS);
GL.Vertex3(0, 0, 0);
GL.Vertex3(1, 0, 0);
GL.Vertex3(1, 1, 0);
GL.Vertex3(0, 1, 0);
GL.End();
GL.PopMatrix();
}
}
}
Ограничения, о которых знает не каждый
Замерзание при переключении на Scene view
В редакторе Unity, если переключиться из Game view в Scene view, WaitForEndOfFrame «замерзает» — корутина возобновится только при возврате в Game view. На собранной игре этого нет, это чисто редакторское поведение.
Батч-режим редактора
В batch mode WaitForEndOfFrame не выполняется — даже если на скрипте стоят ExecuteInEditMode или ExecuteAlways. Это важно для автоматизации сборки скриншотов и рендеринга.
WaitForEndOfFrame или WaitForFixedUpdate
Частая путаница из-за похожих имён. Решающий вопрос — что нужно дождаться:
| Инструкция | Ждёт | Типичная задача |
|---|---|---|
WaitForEndOfFrame |
Конца кадра, после отрисовки камер и GUI | Скриншот, ReadPixels, GL-оверлей |
WaitForFixedUpdate |
Следующего фиксированного обновления физики | Работа с Rigidbody после расчёта физики |
Подробно про второй вариант — WaitForFixedUpdate в Unity C#.
Частые ошибки
1. ReadPixels без ожидания конца кадра
Самая частая. Симптом — чёрная или прозрачная текстура вместо скриншота. Лечится добавлением yield return new WaitForEndOfFrame() перед чтением.
2. Ожидание в Update
Update — обычный метод, там yield невозможен. WaitForEndOfFrame работает только в корутине (IEnumerator).
3. Утечка текстуры
Texture2D, созданная под скриншот, не освобождается сама. После EncodeToPNG вызывайте Destroy(tex) — иначе каждая попытка добавит мегабайты в память.
4. Ожидание в Editor-корутине без Play mode
Вне режима Play WaitForEndOfFrame не сработает (за исключением Editor coroutines пакета). Для редакторских задач это ограничение нужно учитывать заранее.