Всем привет
В этой теме мы попробуем немного познакомиться с API ортографической камеры для игрового движка Defold от britzl. Потому что именно этот API камеры используется во многих примерах Defold Public Examples в качестве библиотечной зависимости. Мы не будем разбирать код из примера, оставим это на другой раз. В этом посте, мы просто ознакомимся с некоторыми функциями “пользовательской” камерой от britzl, для того, чтобы в дальнейшем нам было бы легче разбираться в других проектах.
Orthographic Camera API
Orthographic Camera API от britzl — API ортографической камеры позволяет с легкостью преобразовывать экранные координаты в мировые, плавно следовать за игровым объектом и создавать эффект дрожания экрана.
Ссылка на Orthographic Camera API.
Зависимость —это всё, что тебе нужно подключить извне, установить или правильно собрать, чтобы твой проект работал.
Для того, чтобы воспользоваться предоставленными возможностями ортогональной камеры от britzl, нам необходимо добавить этот проект в свой проект (если он не добавлен). Откройте файл game.project
и в поле dependencies
под проектом добавьте:
https://github.com/britzl/defold-orthographic/archive/master.zip
Или укажите ZIP-файл определенного релиза .
Нажимаем на плюсик и вставляем вышеприведенную ссылку на архив в качестве зависимости в наш проект в поле Dependencies
:
После добавления зависимости:
- Сохраняем файл
game.project
(Ctrl+S
). - Теперь выбираем
Project ▸ Fetch Libraries
, чтобы обновления вступили в силу
В качестве основы под практику возьмём проект publicexamples/examples/top_down_spaceship at master · britzl/publicexamples · GitHub.
Как я понял, встроенную камеру в игровой движок в какой-то версии поменяли. Теперь её нужно настраивать по другому, поэтому пример не является исправным. Но, сейчас мы постараемся исправить этот пример с применением камеры от britzl.
Пример в действии: Top Down Spaceship 0.0
Добавление камеры в проект:
Во-первых, добавим в этот проект библиотечную зависимость камеры от britzl, как было сказано выше.
Во-вторых, переходим в game.project
→ Bootstrap
и меняем файл скрипт рендера на тот, что был добавлен вместе с включенной библиотекой orthographic. Он позволит работать нам с “новой” камерой.
В-третьих, переходим в top_down_spaceship.collection
и добавляем игровой объект в коллекцию из зависимости ortographic
.

В-четвертых, удаляем эту строку:
go.set_position(pos + vmath.vector3(-568, - 320, 0), "camera")
В-пятых, добавляем строчку кода в function init(self
):
camera.follow(CAMERA_ID, "/player")
Теперь запускаем проект(CTRL + B
или f5
).
Для того, чтобы можно было воспользоваться функциями API в нашем скрипте, подключим Lua-модуль camera.lua
.
С помощью переменной camera
и точечной нотации мы сможем использовать функции подключенного модуля.
local camera = require "orthographic.camera"
Создаём хеш из строк "/camera"
— это идентификатор игрового объекта камеры, которую мы добавляли ранее:
local CAMERA_ID = hash("/camera")
Слежение/прекращение слежения за игровым объектом:
С помощью этой функции мы заставили следовать камеру за игровым объектом:
camera.follow(CAMERA_ID, "/player")
В функцию init(self)
вставьте создадим переменную-флаг, который будет отвечать за текущее состояние камеры — следует/не следует за игровым объектом:
self.isFollow = true
В функцию on_input
добавьте такой блок кода:
-- устанавливаем/убираем слежение камерой за spaceship
if action_id == hash("follow") and action.released then
if self.isFollow then
camera.unfollow(CAMERA_ID)
self.isFollow = false
else
camera.follow(CAMERA_ID, "/player")
self.isFollow = true
end
end
В папку game.input_binding
добавляем новую привязку ввода:
Запустите проект и нажмите клавишу F на клавиатуре. Камера должна прекращать/начинать следовать за космическим кораблём.
Таким образом, camera.follow(...)
и camera.unfollow(...)
отвечают за слежение/прекращение слежения за игровым объектом: ссылка на детальное ознакомление с этой функцией.
Добавление зума в проект:
Зум — это уровень масштабирования камеры.
Добавим возможность изменять масштаб камеры по нажатию клавиши Z
и X
клавиатуры, также не забудьте про добавления привязок ввода:
-- увеличиваем/уменьшаем масштабирование камеры
if action_id == hash("zoom_inc") and action.released then
local zoom = camera.get_zoom(CAMERA_ID)
camera.set_zoom(CAMERA_ID, zoom + 0.1)
print("Zoom increased to:", camera.get_zoom(CAMERA_ID))
elseif action_id == hash("zoom_dec") and action.released then
local zoom = camera.get_zoom(CAMERA_ID)
camera.set_zoom(CAMERA_ID, zoom - 0.1)
print("Zoom increased to:", camera.get_zoom(CAMERA_ID))
end
your code goes here
Ссылка на информацию про зум: GitHub - britzl/defold-orthographic: Orthographic camera functionality for the Defold game engine
Причина тряски?
Добавьте в on_message
, в блок кода, отвечающий за столкновение с невидимой стеной функцию:
camera.shake(CAMERA_ID, 0.05, 1, "both", function()
print("Пилот контужен")
camera.stop_shaking(CAMERA_ID)
end)
Эта функция отвечает за тряску экрана. Последний аргумент функции — коллбэк-функция, она вызывается после завершения функции shake
. В нашем случае она останавливает тряску камеры и выводит сообщение о том, что пилот был контужен
Ссылка на функцию: тык.
Также, я не упомянул о том, что API можно использовать двумя способами:
- Вызов функций
camera.lua
модуля - Отправка сообщений в
camera.script
Мы использовали первый способ.
Второй способ заключается в том, что мы просто отправляем сообщение камере или скрипту камеры:
msg.post("camera", "shake", { intensity = 0.05, duration = 1, direction = "both"})
Но, как я понял, в таком случае можно передать три аргумента, вместо четырёх:
Если есть желание, попробуйте реализовать слежение камеры за пулей.
Итоговый код в файле spaceship.script:
local camera = require "orthographic.camera"
local CAMERA_ID = hash("/camera")
go.property("angular_velocity", 5)
go.property("linear_velocity", 900)
function init(self)
msg.post(".", "acquire_input_focus")
self.isFollow = true
camera.follow(CAMERA_ID, "/player")
self.rotate = 0
end
function final(self)
msg.post(".", "release_input_focus")
end
function update(self, dt)
-- rotate based on user input and angular velocity (radians per second)
local rotation = go.get_rotation()
rotation = rotation * vmath.quat_rotation_z(self.angular_velocity * self.rotate * dt)
go.set_rotation(rotation)
-- move in direction of rotation with linear velocity (pixels per second)
local pos = go.get_position()
local distance = self.linear_velocity * dt
local direction = vmath.rotate(rotation, vmath.vector3(0, distance, 0))
pos = pos + direction
go.set_position(pos)
end
function on_input(self, action_id, action)
-- set direction of rotation based on user input
if action_id == hash("left") then
if action.pressed then
self.rotate = 1
elseif action.released then
self.rotate = 0
end
elseif action_id == hash("right") then
if action.pressed then
self.rotate = -1
elseif action.released then
self.rotate = 0
end
end
-- увеличиваем/уменьшаем масштабирование камеры
if action_id == hash("zoom_inc") and action.released then
local zoom = camera.get_zoom(CAMERA_ID)
camera.set_zoom(CAMERA_ID, zoom + 0.1)
print("Zoom increased to:", camera.get_zoom(CAMERA_ID))
elseif action_id == hash("zoom_dec") and action.released then
local zoom = camera.get_zoom(CAMERA_ID)
camera.set_zoom(CAMERA_ID, zoom - 0.1)
print("Zoom increased to:", camera.get_zoom(CAMERA_ID))
end
-- устанавливаем/убираем слежение камерой за spaceship
if action_id == hash("follow") and action.released then
if self.isFollow then
camera.unfollow(CAMERA_ID)
self.isFollow = false
else
camera.follow(CAMERA_ID, "/player")
self.isFollow = true
end
end
-- fire bullets
-- spawn bullet in front of the spaceship (based on rotation)
if (action_id == hash("trigger") or action_id == hash("touch")) and action.released then
local rotation = go.get_rotation()
local position = go.get_position() + vmath.rotate(rotation, vmath.vector3(0, 60, 0))
local bullet = factory.create("#bulletfactory", position, rotation)
local to = position + vmath.rotate(rotation, vmath.vector3(0, 1000, 0))
go.animate(bullet, "position", go.PLAYBACK_ONCE_FORWARD, to, go.EASING_LINEAR, 0.5, 0, function()
go.delete(bullet)
end)
end
end
function on_message(self, message_id, message, sender)
-- basic contact separation
if message_id == hash("contact_point_response") and message.group == hash("wall") then
go.set_position(go.get_position() + message.normal * message.distance)
-- трясём камеру
camera.shake(CAMERA_ID, 0.05, 1, "both", function()
print("Пилот был контужен")
camera.stop_shaking(CAMERA_ID)
end)
end
end
Тоже самое, но в видеоформате.
Спасибо за внимание