Всем привет
Продолжаем серию разборов чужого кода с этой страницы: Публичный пример Defold .
Сегодня разбираем пример от britzl — Боец комбо.
Попробуйте этот проект в действии: запустите пример .
Исходная папка с проектом: ссылка на github .
Исходная папка с публичными примера Defold на github: ссылка на github.
Обращение к новичкам:
Я предполагаю, что у вас уже установлен Defold, если нет, перейдите по этой ссылке: Добро пожаловать в Defold.
Также, будет плюсом, если вы хотя бы поверхностно знакомы со строительными блоками Defold. Если нет, ознакомиться с основными концепциями Defold можно на официальном сайте — перейдите по этой ссылке.6.
Пример того, как скачать и открыть готовый проект:
Скачиваем архив с примерами проектов из github
Распаковываем скачанный ZIP архив примеров в любую папку во вашему усмотрению.
Переходим в папку examples
.
Ищем проект play_animation
.
Открываем game.project
.
Внимание: скриншоты представлены ниже, это пример скачивания и открытия проекта, в этом примере мы рассматриваем п
Preformatted text
роект с названием play_animation, потому название папок проекта будет отличаться.


Этот пример представляет собой простую реализацию распознавания комбо-атак по последовательности нажатий клавиш.
Рассмотрим файлы проекта:
attack.font
— шрифт, который используется метками.
bg.atlas
— содержит фоновое изображение.
ryu.atlas
— содержит изображения игрового персонажа и анимации.
fighter_combo.collection
— содержит в себе два игровых объекта с компонентами метка (attack
, instructions
), bg
— содержит спрайт фонового изображения, ryu
— содержит скрипт персонажа и спрайт.
Рассмотрим ryu.script:
local LEFT = hash("left")
local RIGHT = hash("right")
local UP = hash("up")
local DOWN = hash("down")
local PUNCH = hash("punch")
local KICK = hash("kick")
Создаются хэши для возможных кнопок управления(понадобятся для сравнения строк).
local ATTACKS = {
{ name = "HIGH KICK", anim = hash("hkick"), actions = { UP, KICK } },
{ name = "PUNCH", anim = hash("punch"), actions = { PUNCH } },
{ name = "LOW PUNCH", anim = hash("lpunch"), actions = { DOWN, PUNCH } },
{ name = "HADOUKEN", anim = hash("hadouken"), actions = { DOWN, RIGHT, PUNCH } },
}
Это список возможных атак. У каждой атаки есть:
name
: отображаемое имяanim
: анимация (используется вsprite.play_flipbook
)actions
: список нужных последовательных нажатий
local function find_attack(actions)
Функция find_attack(actions)
проверяет, соответствует ли переданная последовательность actions
какой-то из комбо-атак в ATTACKS
.
- Сначала проверяется длина последовательности
- Потом — поэлементное сравнение
- Если всё совпадает — возвращается объект атаки
Функция ищет точное совпадение с одной из заранее заданных атак.
for _, attack in ipairs(ATTACKS) do
- Цикл по всем атакам из таблицы
ATTACKS
. - Каждая
attack
— это таблица вида{ name, anim, actions }
.
if #attack.actions == #actions then
Сначала сравнивается длина, например, если у текущей атаки actions
длиной 3, а у игрока — 2, то это не совпадение.
local match = true
for i = 1, #attack.actions do
if attack.actions[i] ~= actions[i] then
match = false
break
end
end
Создаём флаг match
, предполагая, что будет совпадение.
Проходим по каждой кнопке (i
-тый элемент) из комбо.
Если хоть одна кнопка не совпадает по порядку, выставляем match = false
и прерываем цикл.
if match then
return attack
end
Если всё совпало — возвращаем найденную атаку.
Это завершает выполнение функции.
return nil
Если не нашли совпадений ни для одной атаки — возвращаем nil
.
local function play_animation(self, anim)
if self.anim ~= anim then
self.anim = anim
sprite.play_flipbook("#sprite", anim)
end
end
Если текущая анимация отличается от текущей, проигрывается новая анимация.
function init(self)
msg.post(".", "acquire_input_focus")
play_animation(self, hash("idle"))
self.actions = {}
self.last_input_time = 0
end
Вызывается при старте объекта:
msg.post(".", "acquire_input_focus")
— захватывает ввод.
play_animation(self, hash("idle"))
— устанавливаем анимацию в idle
.
self.actions
— инициализирует список текущих нажатий.
self.last_input_time
— инициализирует время последнего нажатия.
function on_message(self, message_id, message, sender)
if message_id == hash("animation_done") then
self.anim = nil
label.set_text("/attack#label", "IDLE")
play_animation(self, hash("idle"))
end
end
Обрабатывается событие завершения анимации:
self.anim = nil
— сбрасывает self.anim
.
label.set_text("/attack#label", "IDLE")
— устанавливает на метке слово “IDLE
”.
play_animation(self, hash("idle"))
— устанавливает анимацию idle
.
function on_input(self, action_id, action)
if action_id and action.pressed then
print(action_id)
local now = socket.gettime()
if now - self.last_input_time > 0.35 then
self.actions = {}
end
self.last_input_time = now
table.insert(self.actions, action_id)
local attack = find_attack(self.actions)
if attack then
label.set_text("/attack#label", attack.name)
play_animation(self, attack.anim)
end
end
end
Обрабатывает нажатия клавиш:
- Проверка на нажатие (
action.pressed
) - Сравнение с последним вводом — если прошло больше 0.35 секунд, сбрасывается список действий
- Добавляется новое действие в
self.actions
- Проверяется, соответствует ли текущая последовательность одной из атак
- Если да, то проигрывается анимация и пишется название на экран
if action_id and action.pressed then
Проверяем, есть ли действие (action_id
) и было ли это нажатие кнопки.
print(action_id)
Печатаем в консоль, какое действие было нажато — нужно для отладки.
local now = socket.gettime()
Получаем текущее время в секундах (с точностью до миллисекунд).
Функция socket.gettime()
возвращает время в формате UNIX timestamp
(например, 1716732768.34).
if now - self.last_input_time > 0.35 then
self.actions = {}
end
Если прошло больше 0.35 секунд с момента последнего ввода, значит это новая комбо, и мы обнуляем список действий.
Это предотвращает “смешивание” предыдущего ввода с новым.
self.last_input_time = now
— обновляем момент последнего нажатия, чтобы отслеживать интервалы между нажатиями.
table.insert(self.actions, action_id)
— добавляем текущее нажатие в список действий self.actions.
local attack = find_attack(self.actions)
— проверяем, соответствует ли текущая последовательность кнопок какой-то атаке.
if attack then
label.set_text("/attack#label", attack.name)
play_animation(self, attack.anim)
end
Если нашли атаку:
- Пишем название на экран через метку
attack#label
. - Запускаем анимацию этой атаки.
Надеюсь, кому-нибудь этот материал будет полезен.
Всем спасибо за внимание
Другие разборы:
- Разбор проектов на Defold 1.Tilemap Collisions [tilemap]
- Разбор проектов на Defold 2. Параллакс [parallax]
- Разбор проектов на Defold 3. Движение игровых объектов [animation, movement, input]
- Разбор проектов на Defold 4. Воспроизвести анимацию [animation, movement, input]
- Разбор проектов на Defold 5. Меню и игра. Прокси-коллекции [proxy-collection, gameloop, collection, gui]
- Разбор проектов на Defold 6. Пауза [пауза]
- Разбор проектов на Defold 7. Простая кнопка [простая кнопка]
- Разбор проектов на Defold 8. Фабрики и свойства [фабрики и свойства]
- Разбор проектов на Defold 9. Селектор уровня [селектор уровня].