camera.md 22 KB


title: Компонент Camera

brief: В данном руководстве описаны функциональные возможности компонента Camera в Defold.

Камеры

Камера в Defold — это компонент, который изменяет область просмотра (viewport) и проекцию игрового мира. Компонент камеры определяет базовую перспективную или ортографическую камеру, которая предоставляет матрицы вида и проекции в рендер-скрипт.

Перспективная камера обычно используется для 3D-игр, где вид камеры и размер и перспектива объектов основаны на усечённой пирамиде (view frustum) и расстоянии и угле обзора от камеры до объектов в игре.

Для 2D-игр часто предпочтительно использовать ортографическую проекцию. Это означает, что вид камеры больше не определяется усечённой пирамидой, а ограничен параллелепипедом. Ортографическая проекция нереалистична, так как не изменяет размер объектов в зависимости от расстояния. Объект на расстоянии 1000 единиц будет отображаться того же размера, что и объект прямо перед камерой.

projections

Создание камеры

Чтобы создать камеру, кликните правой кнопкой мыши по игровому объекту и выберите Add Component ▸ Camera. Также можно создать файл компонента камеры в иерархии проекта и добавить его к игровому объекту.

create camera component

Компонент камеры имеет следующие свойства, определяющие усечённую пирамиду камеры:

camera settings

Id : Идентификатор компонента

Aspect Ratio : (Только для перспективной камеры) — соотношение ширины и высоты усечённой пирамиды. 1.0 означает квадратный вид. 1.33 подходит для соотношения 4:3, например 1024x768. 1.78 — для 16:9. Это значение игнорируется, если включён Auto Aspect Ratio.

Fov : (Только для перспективной камеры) — вертикальный угол обзора камеры в радианах. Чем шире угол обзора, тем больше камера видит.

Near Z : Z-значение ближней плоскости отсечения.

Far Z : Z-значение дальней плоскости отсечения.

Auto Aspect Ratio : (Только для перспективной камеры) — если включено, камера автоматически вычисляет соотношение сторон.

Orthographic Projection : Включите, чтобы использовать ортографическую проекцию (см. ниже).

Orthographic Zoom : (Только для ортографической камеры) — масштаб ортографической проекции (> 1 = приближение, < 1 = отдаление).

Orthographic Mode : (Только для ортографической камеры) — управляет тем, как ортографическая камера определяет зум относительно размера окна и ваших значений дизайна (game.projectdisplay.width/height).

  • Fixed (постоянный зум): используется текущее значение Orthographic Zoom.
  • Auto Fit (вписать): автоматически подбирает зум так, чтобы вся дизайн-область помещалась в окно. Возможен дополнительный контент по бокам/сверху/снизу.
  • Auto Cover (заполнить): автоматически подбирает зум так, чтобы дизайн-область заполняла окно целиком. Возможна обрезка по бокам/сверху/снизу. Доступно только при включённой Orthographic Projection.

Использование камеры

Все камеры по умолчанию включены и обновляются каждый кадр, а Lua-модуль camera доступен во всех скриптовых контекстах. Начиная с Defold 1.8.1, больше не нужно явно включать камеру через отправку сообщения acquire_camera_focus компоненту камеры. Старые сообщения acquire и release всё ещё поддерживаются, но рекомендуется использовать сообщения enable и disable, как для любого другого компонента, который нужно включать или выключать:

msg.post("#camera", "disable")
msg.post("#camera", "enable")

Чтобы получить список всех доступных камер, используйте функцию camera.get_cameras():

-- Внимание: вызовы render доступны только в рендер-скрипте.
-- camera.get_cameras() можно использовать везде,
-- но render.set_camera — только в рендер-скрипте.

for k,v in pairs(camera.get_cameras()) do
	-- таблица camera содержит URL всех камер
	render.set_camera(v)
	-- здесь выполняется рендеринг — всё, что использует материалы с
	-- матрицами вида и проекции, будет использовать матрицы этой камеры.
end
-- чтобы отключить камеру, передайте nil (или вызовите без аргументов) в render.set_camera.
-- после этого все вызовы рендеринга будут использовать матрицы вида и проекции,
-- заданные напрямую в рендер-контексте через render.set_view и render.set_projection
render.set_camera()

Модуль camera содержит множество функций для управления камерой. Вот несколько из них, полный список доступен в API документации:

camera.get_aspect_ratio(camera)        -- получить соотношение сторон
camera.get_far_z(camera)               -- получить дальнюю плоскость отсечения
camera.get_fov(camera)                 -- получить угол обзора
camera.get_orthographic_mode(camera)   -- получить ортографический режим (одно из camera.ORTHO_MODE_*)
camera.set_aspect_ratio(camera, ratio) -- установить соотношение сторон
camera.set_far_z(camera, far_z)        -- установить дальнюю плоскость отсечения
camera.set_near_z(camera, near_z)      -- установить ближнюю плоскость отсечения
camera.set_orthographic_mode(camera, camera.ORTHO_MODE_AUTO_FIT) -- установить ортографический режим
... и другие

Камера идентифицируется URL, который представляет полный путь к компоненту в сцене, включая коллекцию, игровой объект и id компонента. В примере используйте URL /go#camera для доступа к компоненту камеры из той же коллекции, и main:/go#camera для доступа к камере из другой коллекции или рендер-скрипта.

create camera component

-- Доступ к камере из скрипта в той же коллекции:
camera.get_fov("/go#camera")

-- Доступ к камере из скрипта в другой коллекции:
camera.get_fov("main:/go#camera")

-- Доступ к камере из рендер-скрипта:
render.set_camera("main:/go#camera")

Каждый кадр компонент камеры с текущим фокусом камеры отправляет сообщение set_view_projection на сокет "@render":

-- builtins/render/default.render_script
--
function on_message(self, message_id, message)
	if message_id == hash("set_view_projection") then
		self.view = message.view                    -- [1]
		self.projection = message.projection
	end
end
  1. Сообщение от компонента камеры содержит матрицу вида и матрицу проекции.

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

Матрица вида определяет позицию и ориентацию камеры. Камера с Orthographic Projection центрирует вид на позиции игрового объекта, к которому прикреплён компонент камеры, тогда как камера с Perspective Projection располагает нижний левый угол вида на позиции игрового объекта.

Рендер-скрипт

Начиная с Defold 1.9.6, при использовании дефолтного рендер-скрипта Defold автоматически устанавливает последнюю включённую камеру для рендеринга. Ранее требовалось явно отправлять сообщение use_camera_projection рендереру, чтобы уведомить его об использовании матриц камеры. Сейчас это не обязательно, но для обратной совместимости можно продолжать использовать.

Также можно явно задать конкретную камеру для рендеринга в рендер-скрипте. Это полезно, например, в многопользовательских играх для точного контроля, какая камера используется.

-- render.set_camera автоматически применит матрицы вида и проекции
-- для всего рендеринга, пока не будет вызван render.set_camera() без аргументов.
render.set_camera("main:/my_go#camera")

Чтобы проверить, включена ли камера, используйте функцию get_enabled из Camera API:

if camera.get_enabled("main:/my_go#camera") then
	-- камера включена, используем её для рендеринга
	render.set_camera("main:/my_go#camera")
end

::: sidenote Чтобы использовать set_camera вместе с отсечением по усечённой пирамиде (frustum culling), передайте опцию: render.set_camera("main:/my_go#camera", {use_frustum = true}) :::

Панорамирование камеры

Перемещайте камеру по игровому миру, перемещая игровой объект, к которому прикреплён компонент камеры. Компонент автоматически отправит обновлённую матрицу вида на основе текущих координат по осям X и Y.

Масштабирование камеры

Для перспективной камеры масштабирование происходит перемещением игрового объекта по оси Z. Компонент камеры автоматически обновит матрицу вида с учётом текущей позиции по Z.

Для ортографической камеры масштабирование выполняется изменением свойства Orthographic Zoom:

go.set("#camera", "orthographic_zoom", 2)

Также для ортографической камеры можно выбрать способ определения зума с помощью параметра Orthographic Mode или из кода:

-- текущий режим (одно из: camera.ORTHO_MODE_FIXED, _AUTO_FIT, _AUTO_COVER)
local mode = camera.get_orthographic_mode("#camera")

-- включить режим "вписать" (contain), чтобы дизайн-область всегда полностью помещалась в окно
camera.set_orthographic_mode("#camera", camera.ORTHO_MODE_AUTO_FIT)

-- включить режим "заполнить" (cover), чтобы окно всегда было заполнено
-- camera.set_orthographic_mode("#camera", camera.ORTHO_MODE_AUTO_COVER)

-- вернуть "фиксированный" режим и управлять масштабом вручную через orthographic_zoom
-- camera.set_orthographic_mode("#camera", camera.ORTHO_MODE_FIXED)

Адаптивный зум

Адаптивный зум — это изменение значения зума камеры при изменении разрешения экрана относительно первоначального разрешения, заданного в game.project.

Два распространённых подхода к адаптивному зуму:

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

Пример:

local DISPLAY_WIDTH = sys.get_config_int("display.width")
local DISPLAY_HEIGHT = sys.get_config_int("display.height")

function init(self)
	local initial_zoom = go.get("#camera", "orthographic_zoom")
	local display_scale = window.get_display_scale()
	window.set_listener(function(self, event, data)
		if event == window.WINDOW_EVENT_RESIZED then
			local window_width = data.width
			local window_height = data.height
			local design_width = DISPLAY_WIDTH / initial_zoom
			local design_height = DISPLAY_HEIGHT / initial_zoom

			-- max zoom: контент изначального разрешения заполнит и выйдет за границы экрана
			local zoom = math.max(window_width / design_width, window_height / design_height) / display_scale

			-- min zoom: контент изначального разрешения полностью поместится в экран
			--local zoom = math.min(window_width / design_width, window_height / design_height) / display_scale
			
			go.set("#camera", "orthographic_zoom", zoom)
		end
	end)
end

Полный пример адаптивного зума доступен в этом примерном проекте.

Примечание: Для ортографической камеры типичное поведение «вписать/заполнить» можно получить без пользовательского кода, установив Orthographic Mode в Auto Fit (вписать) или Auto Cover (заполнить). В этих режимах эффективный зум рассчитывается автоматически на основе размера окна и заданного разрешения дизайна.

Следование за игровым объектом

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

follow game object

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

Преобразование мыши в мировые координаты

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

--- Преобразует экранные координаты в мировые с учётом
-- вида и проекции конкретной камеры
-- @param camera URL камеры для преобразования
-- @param screen_x экранная координата X
-- @param screen_y экранная координата Y
-- @param z опциональный Z для преобразования, по умолчанию 0
-- @return world_x мировая координата X
-- @return world_y мировая координата Y
-- @return world_z мировая координата Z
function M.screen_to_world(camera, screen_x, screen_y, z)
    local projection = go.get(camera, "projection")
    local view = go.get(camera, "view")
    local w, h = window.get_size()

    -- https://defold.com/manuals/camera/#converting-mouse-to-world-coordinates
    local inv = vmath.inv(projection * view)
    local x = (2 * screen_x / w) - 1
    local y = (2 * screen_y / h) - 1
    local x1 = x * inv.m00 + y * inv.m01 + z * inv.m02 + inv.m03
    local y1 = x * inv.m10 + y * inv.m11 + z * inv.m12 + inv.m13
    return x1, y1, z or 0
end

Имейте в виду, что значения action.screen_x и action.screen_y из on_input() должны использоваться как аргументы для этой функции. На странице Примеры показано использование преобразования экранных координат в мировые. Также есть примерный проект с демонстрацией этого процесса.

::: sidenote Сторонние решения для камеры предоставляют функции для преобразования координат между экраном и миром. :::

Манипуляции во время выполнения

Камеры можно изменять из кода с помощью различных сообщений и свойств (подробности в API документации).

Компонент камеры имеет следующие свойства, которые можно читать и изменять через go.get() и go.set():

fov : Угол обзора камеры (number).

near_z : Ближняя плоскость отсечения (number).

far_z : Дальняя плоскость отсечения (number).

orthographic_zoom : Масштаб ортографической камеры (number).

aspect_ratio : Соотношение ширины и высоты усечённой пирамиды. Используется при вычислении проекции перспективной камеры. (number).

view : Вычисленная матрица вида камеры. ТОЛЬКО ДЛЯ ЧТЕНИЯ. (matrix4).

projection : Вычисленная матрица проекции камеры. ТОЛЬКО ДЛЯ ЧТЕНИЯ. (matrix4).

Сторонние решения для камеры

Существуют решения сообщества, которые реализуют распространённые функции, такие как тряска экрана, следование за объектами, преобразование экранных координат в мировые и многое другое. Их можно скачать с портала Defold Asset Portal: