Как я свой первый ИИ писал

Привет, Хабр. История моя берёт начало в январе 2019 года.

Мы с моей тимой геймдевелоперов решили взяться за самый большой проект в нашей истории- 2Д платформер. Нет, мы не делали до этого какие-нибудь FlappyBird'ы или змейки, но объём работы в этом проекте просто сносил нам мозг. Для начала мы отказались от обычных, вертикальных лестниц, а взяли ступенчатые лестницы. Мы написали логику для дверей, которые можно было закрывать «на ключ» и прикрутили разрушаемые блоки. Наступил момент, когда надо было писать Искусственный Интеллект. Как самому опытному из нашей малоопытной команды скриптеру, честь писать ИИ выпала мне. я плакал в подушку, не понимая, что мне делать я был очень горд тем, что именно я напишу одну из самых сложных механик в нашем проекте.

Этап 1: поиск пути и движение по пути


Этап 1.1: Поиск пути

Так как основные локации у нас будут не на открытом воздухе, а в зданиях, то нужно было сделать поиск маршрута среди десятков дверей, лестниц и комнат. Подумав, мы с тимлидом решили, что стоит сделать некую пародию на алгоритм A*, где у нас будут узлы, между которыми будет бегать бот. сделали тестовую сцену, поставили узлы, для наглядности повесили на них SprateRenderer'ы. А что делать дальше?

С этим вопросом в голове я ходил 3 дня. Пока один мой товарищ не предложил интересный алгоритм, когда узлы будут возбуждаться, подобно нейронам головного мозга.

Итак. Есть узел A, около которого стоит ИИ и узел Б, к которому ИИ должен прийти. выдали всем Узлам свой ID и пометили соединенные узлы, к которым они будут отправлять сигнал. У каждого узла была своя булевая переменная «isChecked» и переменная «triggeredBy», в котором хранился ID узла, который его «возбудил». Так, когда затронут узел Б, он пройдёт по цепочке к узлу А, узнавая все ID узлов, которые прошёл сигнал. Так я получал путь из ID узлов, которые должен пройти бот. Если вы вдруг не поняли, как это работает, то я расскажу вам сказку.
Однажды Ивану нечего было делать, и поэтому он решил составить своё фамильное древо. К несчастью у него не хватало информации для воплощения этой идеи в жизнь. Иван был так увлечён этой идеей, что решил, что добравшись до главного прародителя он сможет обнаружить своих неожиданных родственников. Иван знал, где он может встретиться со своим отцом, чтобы поговорить и направился туда. Отец рассказал ему, что деда Ивана звали Iван и рассказал, где его можно найти. Иван нашёл Iвана, и тот рассказал, что прадед Ивана мог знать этого прародителя лично, но он давно помер. Иван посветил половину жизни изучению тёмных искусств, но в итоге смог воскресить своего прадеда. Прадед сказал, что его прародитель является оборотнем и что зарыто его тело на опушке у трёх сосен. Иван пошёл туда и обнаружил, роющего могилу человек. Оказалось, что этот человек – двеннадцатиюродный брат Иван. Иван сильно удивился тому, что они пришли к одному месту, но брат оказался программистом и всё объяснил.

– Здесь работает принцип навигации из моей любимой игры *название*!

– И в чём же он заключается?

– От каждого наследника можно придти к общему родителю системы, если в этой системе каждый наследник знает лично своего родителя.
Получился вот такой результат:



Это массив int-переменных, которые означают ID узлов, которые должен пройти юнит.

Этап 1.2.1: движение по пути

У меня есть список ID узлов, у меня есть бот. Что дальше? А дальше то, что надо двигать бота по этому пути.

Ну я прикинул такой вариант: дошёл бот до узла, поставил галочку, посмотрел, что там дальше, пошёл к следующему узлу. Сделал. Заработало. Я был рад… Но…

Этап 1.2.2: лестницы и их взаимодействие с ИИ

Как говорил один чёрно-белый герой: «Лестницы… мой главный враг..»

Нужно было определить, следующий узел находится над ИИ, под или на уровне. В зависимости от этой информации он будет проходить мимо лестницы(игнорировать коллизию), или забираться на неё(взаимодействовать с коллизией). Ох и много нервных клеток полегло на этой битве с движком… На форумах вычитал, что можно расставить всё по слоям и во вкладке Edit->Physics2D можно настроить игнорирование коллизий одного слоя и другого. Всё заработало!
Осталось только научить его открывать двери. Тут проблем не возникло.

Итог:





Этап 2: Эмоции и реплики


Этап 2.1: Эмоции

Да, мы решили приделать эмоции… И реплики.

Эмоции будут выделяться выражением лица и анимациями действий.

Реплики будут отображаться текстом над головой.

Эмоции я прикрутил на одном дыхании… Для этого я уже сделал переменную «emotionID», которая хранила в себе ID эмоции. А вот реплики…

Этап 2.2: Реплики

Для красоты сделал отдельный класс Phrases

[System.Serializable] //сериализуемый для красивого отображения в инспекторе
class Phrases
{
    public string Name;     //название эмоции, которой соответствуют реплики
    public int byEmotionID; //для определения, с какой эмоцией это соединять
    public string[] Phrases;//массив самих реплик
}

Сделал массив этого класса. Дальше просто в зависимости от emotionID ставил любую фразу из списка. Обновлял раз в N секунд.

Но я решил пойти дальше! Для каждого персонажа сделал файл с .phrs расширением, закодировал это с помощью того, что к байтовому числу каждого символа в файле прибавлял X байтов. Получался нечитаемый, не изменяемый текст. сделал что-то типа своей разметки, сделал алгоритм, который берёт и по этой разметке всё переводит в массив класса Phrases.
Отлично! Всё работает!

Хотел на чистом шарпе написать программку для заполнения такого файла, но тут мы переходим к концу истории.

Конец..?


От большой, неоплачиваемой работы мы быстро устали… Присоединение нового кодера не помогло… Команда развалилась… Код всё-ещё лежит на облаке Unity.

Конечно, не так давно начала зарождаться идея продолжить проект, но уже с дальнейшей монетизацией… Если что-то получится, то я, пожалуй, напишу всю историю разработки. Но на этом мой рассказ про начинающего скриптера и ИИ заканчивается.

Комментарии 29

    +1
    А если граф не связный? :)
      0

      Не совсем понял

        0
        Как работает алгоритм, если между узлами А и Б пути не существует?
        Ещё из описания непонятно — это поиск в ширину, или в глубину.
          0

          В условиях нашего проекта узлы будут существовать всегда

            0
            Телепортов нет? Односторонних дверей нет? Точки входа местами не меняются?
            (могу еще придумать)
              0

              Нет. Да и под телепорты можно настроить. Просто связывает узлы около одного портала и у другого. Односторонних дверей не планировалось, но и это можно исправить щепоткой кода. Но вопрос "зачем нам это?"

      +2
      Прикольно, у меня есть подобная наработка AI, только более продвинутая (NPC лазит, спрыгивает, запрыгивает, земля любой геометрии из SpriteShapes, на движущихся платформах даже почти ездит (малость доделать надо :)), лестницы вот только вертикальные обычные). Тоже есть эммоции (обычный, агрессивный, паника, ...) и есть список потребностей (жажда, голод, нужда в деньгах и тп) и точек интереса (есть, пить, в туалет, работать, ...), в зависимости от этого перс принимает решение, идет в нужное место и делает что надо. Сейчас правда тоже подзабросил. Может гифку запосчу позже.

      Чем не устроил A*? Отлично подходит для такого решения, простой и эффективный, не понял почему начали свой велосипед изобретать.
        0

        Хотелось сделать что-то своё, прикольное… да и мы не нашли способа интегрировать А* в 2Д платформер. Это же больше для рогаликов..(новички, что с нас взять?)

          0
          Велосипед оказался частным случаем A* :)
            0

            А и есть. Я свою реализацию писал, чтобы генерировать дороги на поле, с учётом уже проложенных (чтобы объединялась новая со старыми). Так-то А — всего лишь алгоритм, который каждый реализует со своими особенностями.

          • НЛО прилетело и опубликовало эту надпись здесь
              +3
              в чем неблагодарность? )
              0
              Спасибо за статью!
                0
                Поясните пожалуйста, в чём проявление термина ИИ в данном контесте? Почему не бот? В чём интеллектуальность то? Прост для хайпу?
                  +1
                  A* — это уже достаточно интеллектуально :)
                  В контексте игр бот и есть ИИ.
                  Интеллектуальность ИИ в играх заключается в том, чтобы с ботами было интересно играть — и это достаточно сложная задача. Например для ботов не сложно идеально перемещаться и стрелять без промаха. Бот может всегда видеть и слышать игрока, но при этом должен вести себя так, чтобы игроку было интересно его побеждать.

                  ИИ, который всегда обыгрывает человека в шахматы — достижение чисто научное.
                  В контексте же развлекательных игр — бот, который попадает в голову игроку в движении из пистолета с 500 метров — это провал. Задача победить человека любой ценой там не стоит — она давно решена классическими методами. В играх задачи ИИ идут гораздо дальше чем просто победить :)
                    0
                    A* это не интеллект. Не подменяйте понятия, пожалуйста.
                    мне казалось что ИИ как минимум подразумевает обучаемость, адаптивность, иттеративность. В статье же ещё один банальный алгоритм без какой бы то ни было изящности.
                      0

                      Это уже продвинутый ИИ, нейросети, машинное обучение и много других страшных слов. ИИ — понятие широкое

                        0
                        Увы, «интеллектуальность» зависит лишь от полученных результатов по сравнению с человеческими. В случае поставленной задачи нахождения пути — результат близок к человеческому, естественному. Да, можно сказать что сама задача не особо интеллектуальная, однако:
                        — алгоритм отмечает обработанные узлы (обучается)
                        — алгоритм работает на разных связных графах (адаптивность)
                        — итерации поиска по смежным узлам продолжаются до успеха (итеративность)

                        P.S. На нейросети тоже можно сказать «банальный алгоритм без какой-либо изящности» :)
                          +1
                          На правах сарказма:
                          Т.е. если взять алгоритм сортировки чисел, и результат будет близок к человеческому, то это тоже будет «интеллектуальность»? :)
                            0
                            Да! Вы можете предложить более интеллектуальный результат сортировки чисел? :)
                            Конкретно в игровом ИИ в результате сортировки ингда были бы ещё и ошибки для пущей «человечности» :)
                          0
                          Эт-то вы еще не видели как светофоры, где время горения зеленого зависит от времени суток, называли интелектуальными.
                          Видать мода такая, хорошо хоть SQL скрипты пока не принято называть интеллектуальными.
                            0
                            В контексте игр симуляция работы светофора тоже исторически относится к ИИ — это же конечный автомат всё-таки :)
                            Интеллектуально это или нет — можно легко определить просто поставив человека (как эталон интеллектуальности) в те же самые условия что и ИИ — и сравнить результат.
                            Такая разновидность теста Тьюринга — посадить человека в изолированную коробку-светофор и дать ему часы. Сравнить результаты. :)
                            Вот если кроме часов в светофоре ещё стоит камера или какие-то дополнительные датчики — то в таких условиях ИИ соревноваться с человеком сложнее.

                            P.S. Если SQL скрипт играет в шахматы/шашки/крестики-нолики не хуже среднестатистического обывателя, почему бы не назвать его интеллектуальным? :)
                            0
                            Просто подмена понятий, произошедшая со времен, когда под «ИИ» как раз и подразумевалось поведение компьютерного соперника/напарника. Это сейчас в этот термин вложили «обучаемость, адаптивность, итеративность» :)
                          0

                          Поиск пути — это часть ИИ, причём, зачастую, бОльшая часть этого ИИ. Другой вопрос, что конкретно здесь — это, как уже выразились — частный случай обычного А* ))

                          0
                          Как хорошо, что в геймдеве кусочек кода из 3 вложенных цикла, 10 if-then-ов и пару хешей можно назвать Искусственным Интеллектом. А если об этом не знают лохи, которые читают заголовки статей, а потом тупят над статьей в поиске этого Искусственного Интеллекта, то на это они и лохи? Так ведь?
                            0

                            Для таких придумали Тэги

                              0
                              Да, чтобы называться Искусственным Интеллектом безусловно нужно больше циклов, условий и хешей :)
                              Первая статья по-моему хорошо получилась, с картинками. Поздравления автору!
                              0
                              Возможно стоит писать ИИИ — ru.wikipedia.org/wiki/Игровой_искусственный_интеллект, так как из заголовка не понятно что он игровой

                                0

                                Ок, спасибо, учту на будущее!

                              Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                              Самое читаемое