Всем привет
Продолжаем серию разборов чужого кода с этой страницы: Публичный пример Defold.
Сегодня разбираем пример от britzl — Простой искуственный интеллект.
Попробуйте этот проект в действии: запустите пример.
Исходная папка с проектом: ссылка на github.
Исходная папка с публичными примера Defold на github: ссылка на github.
Обращение к новичкам:
Я предполагаю, что у вас уже установлен Defold, если нет, перейдите по этой ссылке: Добро пожаловать в Defold.
Также, будет плюсом, если вы хотя бы поверхностно знакомы со строительными блоками Defold. Если нет, ознакомиться с основными концепциями Defold можно на официальном сайте — перейдите по этой ссылке.12.
Пример того, как скачать и открыть готовый проект:
Скачиваем архив с примерами проектов из github
Распаковываем скачанный ZIP архив примеров в любую папку во вашему усмотрению.
Переходим в папку examples
.
Ищем проект play_animation
.
Открываем game.project
.
Внимание: скриншоты представлены ниже, это пример скачивания и открытия проекта, в этом примере мы рассматриваем проект с названием play_animation, потому название папок проекта будет отличаться.
В этом примере от britzl зомби появляются в случайных местах на карте, движутся к цели (либо к случайной позиции на стене, либо к игровому объекту “хитман”), а хитманы создаются по клику игрока и умирают от зомби при столкновении.
Рассмотрим game.atlas и level.tilesours и level.tilemap:
В папке images у нас содержатся изображения, которые будут помещены в тайловый источник и атлас:
Они являются “источником”, откуда потом можно установить значения для спрайта или настроить тайловую карту:
Тайловый источник содержит и группу столкновений
wall
. С которыми игровой объект сможет взаимодействовать:level.tilemap
выступает в качестве изображения поверхности уровня.
Если вы посмотрите, изображение он берёт из тайлового источника:
Рассмотрим игровой объект hitman.go:
В качестве компонентов он содержит в себе: объект столкновения, скрипт, спрайт.
Тип для объекта столкновения стоит как
Kinematic
, что означает, что логика за столкновения будет управляться пользовательским скриптом.Хитман имеет группу
hitman
.Взаимодействовать будет с
wall
, zombie
, detection
.Спрайт нужен для визуализации игрового объекта, изображение для спрайта берём из атласа:
Рассмотрим game.collection:
- У нас имеется игровой объект
go
, который содержит в себе две фабрики:hitmanfactory
иzombiedfactory
, а такжеgame.script
, отвечающий за игровой процесс. - Игровой объект
level
, который содержит в себе объект столкновения, который будет препятствовать зомби выходить за тайлы стен:
А также, содержит тайловую карту уровня.
Рассмотрим hitman.script:
Этот скрипт управляет поведением объекта “хитман”.
local COLLISION_RESPONSE = hash("collision_response")
local ZOMBIE = hash("zombie")
function on_message(self, message_id, message, sender)
if message_id == COLLISION_RESPONSE and message.group == ZOMBIE then
go.delete()
end
end
function on_reload(self)
-- Add reload-handling code here
-- Remove this function if not needed
end
COLLISION_RESPONSE = hash("collision_response")
— хэш для обработки сообщений о столкновениях.
ZOMBIE = hash("zombie")
— хэш для идентификации группы объектов “зомби”.
on_message(self, message_id, message, sender)
:
- Обрабатывает сообщения, получаемые объектом хитмана.
- Если сообщение —
COLLISION_RESPONSE
(столкновение) и группа объекта, с которым произошло столкновение, — ZOMBIE, то хитман удаляется с помощьюgo.delete()
.
Рассмотрим zombie.script:
Этот скрипт управляет поведением зомби, которые появляются на краях экрана, движутся к хитману и реагируют на столкновения.
Что делает зомби:
- Зомби появляется на краю экрана и движется к случайной позиции на стене.
- При столкновении с хитманом зомби начинает преследовать его, если он ближе текущей цели.
- При контакте со стеной зомби корректирует свою позицию и выбирает новую случайную цель на стене.
- Зомби плавно поворачивается к цели и движется с заданной скоростью.
go.property("speed", 50)
local COLLISION_RESPONSE = hash("collision_response")
local CONTACT_POINT_RESPONSE = hash("contact_point_response")
local HITMAN = hash("hitman")
local WALL = hash("wall")
go.property("speed", 50)
— cкорость зомби, задаётся как свойство с начальным значением 50 единиц в секунду.
COLLISION_RESPONSE, CONTACT_POINT_RESPONSE, HITMAN, WALL:
— хэши для обработки столкновений и идентификации групп объектов (“hitman” и “wall”).
local function random_position_on_wall()
local wall = math.random(1, 4)
if wall == 1 then
return vmath.vector3(0, math.random(0, 640), 0)
elseif wall == 2 then
return vmath.vector3(1156, math.random(0, 640), 0)
elseif wall == 3 then
return vmath.vector3(math.random(0, 1156), 0, 0)
else
return vmath.vector3(math.random(0, 1156), 640, 0)
end
end
Функция random_position_on_wall()
:
- Генерирует случайную позицию на одной из четырёх стен игрового поля:
- Стена 1: левая граница (
x = 0
, y случайное от0
до640
). - Стена 2: правая граница (
x = 1156
, y случайное от0
до640
). - Стена 3: нижняя граница (
x
случайное от0
до1156
,y = 0
). - Стена 4: верхняя граница (
x
случайное от0
до1156
,y = 640
).
- Стена 1: левая граница (
- Возвращает вектор
vmath.vector3
с координатами.
Функция init(self)
:
- Вызывается при создании зомби.
- Инициализирует
self.target_id = nil
(идентификатор цели, например, хитмана). - Устанавливает начальную цель (
self.target_position
) как случайную позицию на стене, используяrandom_position_on_wall()
.
Функция update(self, dt)
:
- Вызывается каждый кадр для обновления состояния зомби.
- Поворот:
- Вычисляет угол (
target_angle
) между текущей позицией зомби и целевой позицией (self.target_position
). - Создаёт кватернион (
target_quat
) для поворота в сторону цели. - Использует сферическую интерполяцию (
vmath.slerp
) с коэффициентом0.08
для плавного поворота зомби к цели. - Устанавливает новый поворот с помощью
go.set_rotation(q)
.
- Вычисляет угол (
- Движение:
- Получает текущую позицию зомби (
go.get_world_position()
). - Вычисляет направление движения (
d
) на основе поворота. - Перемещает зомби в направлении цели со скоростью
self.speed * dt
(где dt — время кадра).
- Получает текущую позицию зомби (
- Обновление цели:
- Если у зомби есть цель (
self.target_id
), предполагается, что цель (хитман) будет уничтожена в этом кадре, поэтому выбирается новая случайная позиция на стене (random_position_on_wall()
). - Сбрасывается
self.target_id = nil
, чтобы зомби мог выбрать новую цель в следующем кадре при новом столкновении
- Если у зомби есть цель (
Функция on_message(self, message_id, message, sender)
:
- Обрабатывает сообщения о столкновениях:
- Столкновение с хитманом (COLLISION_RESPONSE и message.group == HITMAN):
- Получает текущую позицию зомби (
my_pos
) и позицию объекта, с которым произошло столкновение (other_pos
). - Вычисляет расстояние до текущей цели (
distance_to_target
) и до объекта столкновения (distance_to_collision
). - Если объект столкновения ближе, чем текущая цель, или у зомби нет цели (
self.target_id
), обновляет цель на позицию хитмана (self.target_position = other_pos
) и сохраняет его идентификатор (self.target_id = message.other_id
).
- Получает текущую позицию зомби (
- Контакт со стеной (CONTACT_POINT_RESPONSE и message.group == WALL):
- Сдвигает зомби назад на величину
message.distance
вдоль нормали (message.normal
), чтобы избежать застревания в стене. - Выбирает новую случайную позицию на стене как новую цель.
- Сдвигает зомби назад на величину
- Столкновение с хитманом (COLLISION_RESPONSE и message.group == HITMAN):
Рассмотрим game.script:
Этот скрипт управляет общей логикой игры, включая создание зомби и хитманов, а также обработку ввода игрока.
Что делает game.script
:
- Инициализирует игру, создавая 10 зомби в случайных позициях.
- Обрабатывает клики игрока, создавая хитманов в местах кликов.
- Хранит списки активных зомби и хитманов.
- Управляет фокусом ввода для обработки действий игрока.
local function random_position()
return vmath.vector3(math.random(64, 1156 - 128), math.random(64, 640 - 128), 0)
end
Функция random_position()
:
- Генерирует случайную позицию внутри игрового поля (с отступом от краёв:
x
от64
до1156-128
,y
от64
до640-128
). - Используется для начального размещения зомби.
local function spawn_zombie()
return factory.create("#zombiefactory", random_position())
end
Функция spawn_zombie()
:
- Создаёт зомби с помощью фабрики
#zombiefactory
в случайной позиции, возвращаемойrandom_position()
.
local function spawn_hitman(position)
return factory.create("#hitmanfactory", position)
end
Функция spawn_hitman(position)
:
- Создаёт хитмана с помощью фабрики
#hitmanfactory
в указанной позиции (например, по координатам клика игрока).
function init(self)
math.randomseed(os.time())
self.hitmen = {}
self.zombies = {}
for i = 1, 10 do
table.insert(self.zombies, spawn_zombie())
end
msg.post(".", "acquire_input_focus")
end
Функция init(self)
:
- Вызывается при инициализации игры.
- Устанавливает начальное значение генератора случайных чисел с помощью math.randomseed(
os.time()
). - Создаёт пустые таблицы
self.hitmen
иself.zombies
для хранения идентификаторов хитманов и зомби. - Создаёт 10 зомби, добавляя их идентификаторы в
self.zombies
. - Запрашивает фокус ввода с помощью
msg.post(".", "acquire_input_focus")
, чтобы игра могла обрабатывать действия игрока.
function final(self)
msg.post(".", "release_input_focus")
end
Функция final(self)
:
- Вызывается при завершении игры.
- Освобождает фокус ввода с помощью
msg.post(".", "release_input_focus")
.
Функция on_input(self, action_id, action)
:
- Обрабатывает ввод игрока.
- Если действие —
touch
(хэш “touch
”) и кнопка отпущена (action.released
), создаётся новый хитман в позиции клика (action.screen_x, action.screen_y
) и его идентификатор добавляется вself.hitmen
.
Файл настроекinput_binding
:
Как всё работает вместе:
- Инициализация:
- При запуске игры (
game.script
) создаётся 10 зомби, которые появляются в случайных позициях внутри игрового поля (с отступом от краёв). - Каждый зомби (
zombie.script
) выбирает случайную позицию на одной из стен как начальную цель и начинает двигаться к ней.
- Движение зомби:
- Зомби плавно поворачиваются и движутся к своей цели со скоростью
50
единиц в секунду. - Если зомби сталкивается со стеной, он корректирует свою позицию и выбирает новую случайную цель на стене.
- Если зомби сталкивается с хитманом, он начинает преследовать его, если хитман ближе текущей цели.
- Создание хитманов:
- Игрок кликает по экрану, и в месте клика создаётся хитман (
game.script
). - Хитман статичен (в скрипте нет логики движения).
- Столкновения:
- При столкновении зомби с хитманом:
- Зомби обновляет цель на позицию хитмана и начинает двигаться к нему.
- Хитман удаляется (
hitman.script
).
- Зомби, столкнувшийся со стеной, корректирует позицию и выбирает новую цель.
- Игровой цикл:
- Зомби продолжают двигаться к своим целям (либо к хитманам, либо к случайным позициям на стенах).
- Игрок может создавать новых хитманов, кликая по экрану, чтобы уничтожать зомби
Исправление бага:
Если во время игры тайловая карта не отображается, исправьте позицию игрового объекта level на -0.5
, как пример:
Надеюсь, кому-нибудь этот материал будет полезен.
Всем спасибо за внимание
Другие разборы:
- Разбор проектов на 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. Селектор уровня [селектор уровня].
- Разбор проектов на Defold 10. Боец комбо [ввод и анимация]
- Разбор проектов на Defold 11. Coin Magnet (Магнит монет) [фабрика]
- Разбор проектов на Defold 12. Simple Ai (простой ИИ) [ИИ][AI]