Разбор проектов на Defold 4. Воспроизвести анимацию

Всем привет :waving_hand:
Продолжаем серию разборов чужого кода с этой страницы: Публичный пример Defold .

Сегодня разбираем пример от britzlВоспроизведение анимации .
Попробуйте этот проект в действии: запустите пример .
Исходная папка с проектом: ссылка на github .

Обращение к новичкам:

Я предполагаю, что у вас уже установлен Defold, если нет, перейдите по этой ссылке: Добро пожаловать в Defold.
Также, будет плюсом, если вы хотя бы поверхностно знакомы со строительными блоками Defold. Если нет, ознакомиться с основными концепциями Defold можно на официальном сайте — перейдите по этой ссылке.

Пример того, как скачать и открыть готовый проект:

Скачиваем архив с примерами проектов из github
Распаковываем скачанный ZIP архив примеров в любую папку во вашему усмотрению.
Переходим в папку examples.
Ищем проект play_animation.
Открываем game.project.

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


Скриншот 3. Открываем разархивированную папку(наименование может отличаться от вашего названия скаченной папки)
Скриншот 4. Открываем папу с примерами.

Конфликт версий

Нажмите (ctrl + B) или f5.
Если у вас фоновое изображение черное:


Зайдите в play_animation.collection и поменяйте значение Z Position с -1.0 до -0.5.

Теперь фоновое изображение отображается:

Скорее всего, это связано с тем, что граница отрисовки компонентов в какой-то версии движка поменялась с -1 включительно на -1 не включительно.

Рассмотрим play_animation.atlas:


В этом атласе содержится несколько изображений. Одно будет отвечать за фоновое изображение. Другие изображения добавлены в анимации. Анимация player_walk предназначена для перемещение игрового объекта. Анимация player_idle будет проигрываться при неподвижном состоянии игрового объекта.

Нажмите на любую анимацию в панели outline и нажмите клавишу SPACE(пробел). Анимация проигрывается в левом нижнем углу рабочей панели редактора. Ctrl + T скрывает это проигрывание.

Рассмотрим play_animation.collection:


Мы видим два игровых объекта: bg и player. Каждый имеет по одному компоненту sprite, который отвечает за визуальное представление игрового объекта, изображение этот компонент берёт из play_animation.atlas:

Там же мы можем выбирать анимацию по умолчанию для компонента:
image

Игровой объект player содержит в себе скрипт — play_animation.script. В этом файле содержится код, по которому будет “действовать” игровой объект.

Рассмотрим game.input_binding:

image


Action up, down, left, right будут служить hash-строкой, с помощью которой мы будет обрабатывать ввод с клавиатуры.
В Defold, hash в input — это способ представления строки как уникального числового идентификатора (хэш-функция), который используется для оптимизации производительности и памяти.
Когда ты работаешь с вводом, например, нажатием клавиш, Defold не передаёт строку "left" напрямую. Вместо этого он использует:

hash("left")

Это значит:

  • "left" превращается в уникальный числовой ключ (например, 0x9d3fc1c3).
  • Сравнение хэшей намного быстрее, чем сравнение строк.

Defold использует hash(...):

  • для всех имён сообщений, анимаций, ввода, групп объектов, имён скриптовых свойств и т.д.
  • потому что хранить и сравнивать строки неэффективно (особенно на мобильных устройствах).

Например:

  • action_id — это уже хэшированное значение, переданное движком при нажатии клавиши.
  • В on_input() мы будем сравнивать его с hash("left"), чтобы понять, что нажата именно клавиша “влево”.
Рассмотрим файл play_animation.script:
go.property("speed", 200) -- the speed of movement

Добавляем пользовательское свойство игровому объекту, которое будет отвечать за скорость перемещения персонажа в пикселях в секунду.

msg.post(".", "acquire_input_focus")

Захватывает ввод с клавиатуры, позволит нам управлять персонажем.

self.direction = vmath.vector3(0, 0, 0) -- the current direction of movement

Вектор направления движения. Куда повернуто изображение персонажа, в ту сторону и перемещается игровой объект.

self.actions = {} -- mapping of input states (action_id mapped to pressed state)

Таблица, где будут храниться состояния клавиш (нажата/отпущена): true/false.

self.current_animation = nil -- the current animation

Текущая анимация (для проверки повторов, чтобы не начинать все заново проигрывать анимацию, когда клавиша зажата).

function on_input(self, action_id, action)
	if action_id then
		if action.pressed then
			self.actions[action_id] = true
		elseif action.released then
			self.actions[action_id] = false
		end
	end
end

Если игрок вводит соответствующую клавишу из action указанных в game.input_binding, то начинается обработка событий ввода.
При нажатии/отпускании клавиш обновляет self.actions. Если была клавиша нажата — true, отпущена — false. Напомню ещё раз, в этом коде обрабатываются только клавиши привязанные в game.input_binding.

В функции update обрабатывается движение и анимация каждый кадр:

...
	if self.actions[hash("left")] then
		play_animation(self, hash("player_walk"))
		sprite.set_hflip("#sprite", true)
		self.direction.x = -self.speed
	elseif self.actions[hash("right")] then
		play_animation(self, hash("player_walk"))
		sprite.set_hflip("#sprite", false)
		self.direction.x = self.speed
	else
		self.direction.x = 0
	end
...
if self.actions[hash("left")] then -- или (self.actions[hash("left") ] == true)

Проверяет, нажата ли клавиша “влево”. self.actions — это таблица, в которую при нажатии клавиши заносится true.

play_animation(self, hash("player_walk"))

Если “влево” нажата — запускается анимация ходьбы "player_walk" (если ещё не проигрывается).

sprite.set_hflip("#sprite", true)

Отражает спрайт по горизонтали, чтобы он “смотрел” влево (по умолчанию в атласе изображения в анимации “смотрят” вправо).

self.direction.x = -self.speed

Устанавливает направление по оси X влево (отрицательное значение скорости).

...
else
	self.direction.x = 0
...

Игрок не нажал ни “вправо”, ни “влево”, т.е горизонтального движения нет, а значит нет и направления.

Если нажата клавиша “вправо”, то всё также, за исключением того, что скорость ставим в положительное значение.
Логика с движением “вверх”, “вниз” аналогична, только мы изменяем координаты направления по оси Y.

if self.direction.x == 0 and self.direction.y == 0 then
	play_animation(self, hash("player_idle"))
end

Если ни по X, ни по Y не движемся, то запускаем анимацию “player_idle” — стоячее положение.

go.set_position(go.get_position() + self.direction * dt)

Эта строка обновляет позицию игрового объекта.

  • go.get_position() – получает текущую позицию объекта.
  • self.direction – вектор направления, куда должен двигаться объект (например, vmath.vector3(200, 0, 0)).
  • dt – это дельта времени между кадрами (в секундах), используется для того, чтобы движение было одинаково плавным при любом FPS.
  • self.direction * dt – это расстояние, на которое нужно сдвинуть объект за текущий кадр.
  • go.set_position(...) – устанавливает новую позицию, сдвигая объект.
msg.post("@render:", "draw_text", { text = "Move using arrow keys", position = vmath.vector3(10, 100, 0) } )

Эта строка отправляет сообщение рендеру, чтобы отрисовать текст на экране.

  • msg.post(...) – отправка сообщения.
  • "@render:" – адрес получателя — встроенная система рендеринга Defold.
  • "draw_text" – тип сообщения: команда нарисовать текст.
  • { text = "...", position = vmath.vector3(...) } – таблица с параметрами:
    • text – строка, которую нужно отрисовать.
    • position – координаты на экране, где рисовать (в пикселях от нижнего левого угла экрана).
-- play animation unless the same animation is already playing
local function play_animation(self, animation)
	if self.current_animation ~= animation then
		self.current_animation = animation
		sprite.play_flipbook("#sprite", animation)
	end
end

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

sprite.play_flipbook("#sprite", animation)

sprite.play_flipbook(url, animation_id):

  • url: ссылка на спрайтовый компонент, например:
  • "#sprite" — означает “спрайт-компонент текущего объекта”.
  • Можно указать и путь к другому объекту, например: "enemy#sprite".
  • animation_id: имя анимации, которую нужно проиграть:
  • Должно быть передано как hash("имя_анимации").
  • Например: hash("run"), hash("idle"), hash("jump").

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

Другие разборы:
  1. Разбор проектов на Defold 1.Tilemap Collisions [tilemap]
  2. Разбор проектов на Defold 2. Parallax [parallax]
  3. Разбор проектов на Defold 3. Game object movement [animation, movement, input]
  4. Разбор проектов на Defold 4. Воспроизвести анимацию [animation movement input]
1 Like