Привет, Хабр. История моя берёт начало в январе 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.

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

Комментарии (25)


  1. GCU
    16.05.2019 19:40
    +1

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


    1. MrKarton Автор
      17.05.2019 11:41

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


      1. GCU
        17.05.2019 11:52

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


        1. MrKarton Автор
          17.05.2019 12:28

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


  1. Igor_Sib
    16.05.2019 21:09
    +2

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

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


    1. MrKarton Автор
      17.05.2019 07:08

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


    1. GCU
      17.05.2019 13:35

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


  1. UnrealQW
    16.05.2019 23:41
    -1

    По моим наблюдениям, геймдев — самая неблагодарная работа программиста. Если только в виде хобби сойдет.


    1. Brightori
      17.05.2019 00:00
      +1

      в чем неблагодарность? )


  1. Samrat
    17.05.2019 08:47

    Спасибо за статью!


  1. UncleJey
    17.05.2019 10:04

    Поясните пожалуйста, в чём проявление термина ИИ в данном контесте? Почему не бот? В чём интеллектуальность то? Прост для хайпу?


    1. GCU
      17.05.2019 10:41

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

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


      1. UncleJey
        17.05.2019 12:02

        A* это не интеллект. Не подменяйте понятия, пожалуйста.
        мне казалось что ИИ как минимум подразумевает обучаемость, адаптивность, иттеративность. В статье же ещё один банальный алгоритм без какой бы то ни было изящности.


        1. MrKarton Автор
          17.05.2019 12:30

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


        1. GCU
          17.05.2019 13:08

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

          P.S. На нейросети тоже можно сказать «банальный алгоритм без какой-либо изящности» :)


          1. dimkss
            17.05.2019 16:21

            На правах сарказма:
            Т.е. если взять алгоритм сортировки чисел, и результат будет близок к человеческому, то это тоже будет «интеллектуальность»? :)


            1. GCU
              17.05.2019 22:40

              Да! Вы можете предложить более интеллектуальный результат сортировки чисел? :)
              Конкретно в игровом ИИ в результате сортировки ингда были бы ещё и ошибки для пущей «человечности» :)


        1. dimkss
          17.05.2019 16:20

          Эт-то вы еще не видели как светофоры, где время горения зеленого зависит от времени суток, называли интелектуальными.
          Видать мода такая, хорошо хоть SQL скрипты пока не принято называть интеллектуальными.


          1. GCU
            17.05.2019 23:00

            В контексте игр симуляция работы светофора тоже исторически относится к ИИ — это же конечный автомат всё-таки :)
            Интеллектуально это или нет — можно легко определить просто поставив человека (как эталон интеллектуальности) в те же самые условия что и ИИ — и сравнить результат.
            Такая разновидность теста Тьюринга — посадить человека в изолированную коробку-светофор и дать ему часы. Сравнить результаты. :)
            Вот если кроме часов в светофоре ещё стоит камера или какие-то дополнительные датчики — то в таких условиях ИИ соревноваться с человеком сложнее.

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


        1. Hendalph
          18.05.2019 12:08

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


  1. fivehouse
    17.05.2019 13:12

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


    1. MrKarton Автор
      17.05.2019 13:19

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


    1. GCU
      17.05.2019 13:28

      Да, чтобы называться Искусственным Интеллектом безусловно нужно больше циклов, условий и хешей :)
      Первая статья по-моему хорошо получилась, с картинками. Поздравления автору!


  1. GCU
    17.05.2019 13:19

    Возможно стоит писать ИИИ — ru.wikipedia.org/wiki/Игровой_искусственный_интеллект, так как из заголовка не понятно что он игровой


    1. MrKarton Автор
      17.05.2019 14:46

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