Использовать Viewports
Введение
Представьте себе Viewport как экран, на который проецируется игра. Чтобы увидеть игру, нам нужна поверхность, на которой её можно отобразить. Эта поверхность и есть корневой Root Viewport.
SubViewports представляют собой своего рода область просмотра, которую можно добавить к сцене, чтобы иметь несколько поверхностей для рисования. Когда мы рисуем в SubViewport, мы называем его целью рендеринга. Мы можем получить доступ к содержимому цели рендеринга, обратившись к ее соответствующему texture. Используя SubViewport в качестве цели рендеринга, мы можем либо визуализировать несколько сцен одновременно, либо выполнить рендеринг в ViewportTexture, которая применяется к объекту в сцене, например, к динамическому скайбоксу.
SubViewports имеют множество вариантов использования, включая:
Рендеринг 3D-объектов в 2D-игре
Рендеринг 2D-элементов в 3D-игре
Рендеринг динамических текстур
Генерация процедурных текстур во время выполнения
Рендеринг нескольких камер в одной сцене
Общим для всех этих вариантов использования является то, что вам предоставляется возможность рисовать объекты на текстуре, как если бы это был еще один экран, а затем выбирать, что делать с получившейся текстурой.
Другой тип Viewports в Godot — это Windows. Они позволяют проецировать своё содержимое на окно. Хотя корневой Viewports — это окно, он менее гибок. Если вы хотите использовать текстуру Viewports, большую часть времени вам придётся работать с SubViewports.
Вход
Viewports также отвечают за доставку правильно настроенных и масштабированных событий ввода своим дочерним узлам. По умолчанию SubViewports не получают входные данные автоматически, если только они не получают их от своего непосредственного родительского узла SubViewportContainer. В этом случае ввод можно отключить с помощью свойства Disable Input.
Более подробную информацию о том, как Godot обрабатывает ввод, можно найти в Input Event Tutorial.
Listener
Godot поддерживает 3D-звук (как в 2D-, так и в 3D-узлах). Подробнее об этом можно узнать в Audio Streams Tutorial. Для воспроизведения этого типа звука необходимо включить Viewport в качестве прослушивателя (для 2D или 3D). Если вы используете SubViewport для отображения World3D или World2D, не забудьте включить его!
Камеры (2D и 3D)
При использовании Camera3D или Camera2D, он всегда будет отображаться на ближайшем родительском Viewport (по направлению к корню). Например, в следующей иерархии:
CameraA будет отображаться на Root Viewport и будет отображать MeshA. CameraB будет захвачена SubViewport вместе с MeshB. Несмотря на то, что MeshB находится в иерархии сцены, она всё равно не будет отображена на Root Viewport. Аналогично, MeshA не будет отображаться на SubViewport, поскольку SubViewport захватывает только узлы, расположенные ниже в иерархии.
На каждый Viewport может быть только одна активная камера, поэтому если их несколько, убедитесь, что у нужной камеры установлено свойство current, или сделайте ее текущей камерой, вызвав:
camera.make_current()
camera.MakeCurrent();
По умолчанию камеры визуализируют все объекты в своём мире. В 3D камеры могут использовать свойство cull_mask в сочетании со свойством layer объекта VisualInstance3D's, чтобы ограничить отображаемые объекты.
Масштаб и растяжение
У SubViewports есть свойство size, которое задаёт размер SubViewport в пикселях. Для SubViewports, являющихся дочерними элементами SubViewportContainers, эти значения переопределяются, но для всех остальных это задаёт разрешение.
Также можно масштабировать 2D-контент и сделать разрешение SubViewport отличным от указанного в size, вызвав:
sub_viewport.set_size_2d_override(Vector2i(width, height)) # Custom size for 2D.
sub_viewport.set_size_2d_override_stretch(true) # Enable stretch for custom size.
subViewport.Size2DOverride = new Vector2I(width, height); // Custom size for 2D.
subViewport.Size2DOverrideStretch = true; // Enable stretch for custom size.
Информацию о масштабировании и растяжении с помощью Root Viewport см. в Multiple Resolutions Tutorial
Миры
Для 3D Viewport будет содержать World3D. По сути, это вселенная, которая связывает физику и рендеринг. Узлы на основе Node3D регистрируются, используя World3D ближайшего Viewport. По умолчанию вновь создаваемые Viewport не содержат World3D, а используют тот же, что и родительский Viewport. Корневой Viewport всегда содержит World3D, в котором объекты рендерятся по умолчанию.
World3D можно задать в Viewport с помощью свойства World 3D, которое отделит все дочерние узлы этого Viewport и предотвратит их взаимодействие с World3D родительского Viewport. Это особенно полезно в сценариях, где, например, требуется отобразить отдельного трёхмерного персонажа, наложенного на игровое поле (например, в StarCraft).
В качестве вспомогательного инструмента для ситуаций, когда вы хотите создать Viewports, отображающие отдельные объекты, и не хотите создавать World3D, Viewport позволяет использовать свой Own World3D. Это полезно, когда вы хотите создать экземпляры 3D-персонажей или объектов в World2D.
В 2D-пространстве каждый Viewport всегда содержит свой собственный World2D. В большинстве случаев этого достаточно, но если требуется их совместное использование, это можно сделать, установив world_2d на Viewport через код.
Пример того, как это работает, можно увидеть в демонстрационных проектах 3D in 2D и 2D in 3D соответственно.
Capture (Захват)
Можно запросить снимок содержимого Viewport. Для корневого Viewport это фактически снимок экрана. Это делается с помощью следующего кода:
# Retrieve the captured Image using get_image().
var img = get_viewport().get_texture().get_image()
# Convert Image to ImageTexture.
var tex = ImageTexture.create_from_image(img)
# Set sprite texture.
sprite.texture = tex
// Retrieve the captured Image using get_image().
var img = GetViewport().GetTexture().GetImage();
// Convert Image to ImageTexture.
var tex = ImageTexture.CreateFromImage(img);
// Set sprite texture.
sprite.Texture = tex;
Но если использовать это в _ready() или в первом кадре инициализации Viewport's, вы получите пустую текстуру, поскольку получать текстуру нечего. Решить эту проблему можно, например, так:
# Wait until the frame has finished before getting the texture.
await RenderingServer.frame_post_draw
# You can get the image after this.
// Wait until the frame has finished before getting the texture.
await ToSignal(RenderingServer.Singleton, RenderingServer.SignalName.FramePostDraw);
// You can get the image after this.
Viewport Container (Контейнер Области просмотра)
Если SubViewport является дочерним элементом SubViewportContainer, он станет активным и отобразит всё, что находится внутри. Макет выглядит следующим образом:
SubViewport будет полностью покрывать область своего родительского SubViewportContainer, если Stretch установлено в true в SubViewportContainer.
Примечание
Размер SubViewportContainer не может быть меньше размера SubViewport.
Отрисовка
Поскольку Viewport является точкой входа в другую поверхность рендеринга, он предоставляет несколько свойств рендеринга, которые могут отличаться от настроек проекта. Вы можете выбрать разный уровень MSAA для каждого Viewport. По умолчанию установлено значение Disabled (Отключено).
Если вы знаете, что Viewport (область просмотра) будет использоваться только для 2D, вы можете Disable 3D. Godot ограничит способ отрисовки области просмотра. Отключение 3D немного ускоряет работу и потребляет меньше памяти по сравнению с включённым 3D. Рекомендуется отключить 3D, если область просмотра не отображает ничего в 3D.
Примечание
Если вам нужно визуализировать 3D-тени в области просмотра, обязательно установите для свойства области просмотра positional_shadow_atlas_size значение больше 0. В противном случае тени не будут визуализироваться. По умолчанию эквивалентное значение параметра проекта равно 4096 на настольных компьютерах и 2048 на мобильных устройствах.
Godot также предоставляет возможность настраивать отрисовку внутри Viewports с помощью Debug Draw. Debug Draw позволяет указать режим, определяющий, как Viewport будет отображать объекты внутри него. Debug Draw по умолчанию Disabled (отключен). Другие варианты: Unshaded, Overdraw, и Wireframe. Полный список см. в документации Viewport Documentation.
Debug Draw = Disabled (по умолчанию): сцена отрисовывается в обычном режиме.
Debug Draw = Unshaded: Unshaded рисует сцену без использования информации об освещении, поэтому все объекты выглядят плоско окрашенными в цвете альбедо.
Debug Draw = Overdraw: Overdraw рисует сетки полупрозрачными с аддитивным смешиванием, чтобы можно было видеть, как перекрываются сетки.
Debug Draw = Wireframe: Каркас рисует сцену, используя только края треугольников в сетках.
Примечание
Режимы отладки отрисовки в настоящее время не поддерживаются при использовании метода совместимого рендеринга. Они будут отображаться как обычные режимы отрисовки.
Цель рендеринга
При рендеринге в SubViewport всё, что находится внутри, не будет видно в редакторе сцены. Чтобы отобразить содержимое, необходимо где-то нарисовать ViewportTexture этого SubViewport. Это можно сделать с помощью кода, например:
# This gives us the ViewportTexture.
var tex = viewport.get_texture()
sprite.texture = tex
// This gives us the ViewportTexture.
var tex = viewport.GetTexture();
sprite.Texture = tex;
Или его можно назначить в редакторе, выбрав "New ViewportTexture"
и затем выберите Viewport, который вы хотите использовать.
В каждом кадре текстура Viewport's очищается цветом по умолчанию (или прозрачным, если Transparent BG установлено в значение true). Это можно изменить, установив для Clear Mode значение Never или Next Frame. Как следует из названия, Never означает, что текстура никогда не будет очищена, в то время как в следующем кадре текстура будет очищена в следующем кадре и затем сама примет значение Never.
По умолчанию повторная визуализация SubViewport происходит, когда его ViewportTexture отрисовывается в кадре. Если он видим, он будет отрисован, в противном случае — нет. Это поведение можно изменить, установив Update Mode на Never, Once, Always или When Parent Visible. Never и Always будут никогда или всегда перерисовываться соответственно. Once перерисовывает следующий кадр и впоследствии меняется на Never. Это можно использовать для ручного обновления области просмотра. Такая гибкость позволяет пользователям отрисовывать изображение один раз, а затем использовать текстуру, не неся затраты на рендеринг каждого кадра.
Примечание
Обязательно ознакомьтесь с демонстрациями Viewport. Они доступны в папке viewport архива демонстраций или по адресу https://github.com/godotengine/godot-demo-projects/tree/master/viewport.