еперь допиливаем искусственный интеллект.
Оказалось, в самом разгаре боя враги могут запросто обо всем забыть, и резко перейти в режим патрулирования, повернувшись к игроку спиной.
Как выяснилось, параметр TargetActor в BlackBoard может обнулиться, не взирая на то, что персонаж находится совсем рядом. Дичь полная, но доказанный факт. Как ни крути, если персонаж близко, за параметром TargetActor надо следить, и если слетел - обновлять. Причем делать это по событию, а не вешать на Event Tick.
Оказалось, в самом разгаре боя враги могут запросто обо всем забыть, и резко перейти в режим патрулирования, повернувшись к игроку спиной.
Как выяснилось, параметр TargetActor в BlackBoard может обнулиться, не взирая на то, что персонаж находится совсем рядом. Дичь полная, но доказанный факт. Как ни крути, если персонаж близко, за параметром TargetActor надо следить, и если слетел - обновлять. Причем делать это по событию, а не вешать на Event Tick.
Решение получилось такое: в момент, когда враг впервые обнаружил персонажа, засекаем время, а если началась задача NewPlace, проверяем сумму условий: расстояние до персонажа и время. Если близко, и обнаружение было совсем недавно - обновляем TargetActor. При этом срабатывает декоратор, перебрасывающий врага обратно в режим атаки.
Теперь командир, преследуя персонажа, забирается на вертолетную площадку достаточно лихо, а если персонаж спрыгивает вниз, командиру хватает ума развернуться и слезть обратно.
Блюпринты в экран не лезут, но главные кусочки показать могу.
Блюпринты в экран не лезут, но главные кусочки показать могу.
В искусственном интеллекте есть еще один косяк, который отнял у меня неделю, причем победа получилась не очень убедительной.
В процессе тривиального патрулирования территории, пресловутая нода GetRandomReachablePointInRadius может выдать точку прямо за спиной, от чего непись МГНОВЕННО поворачивается на 180 градусов.
Да собственно, если угол превышает хотя бы 20 градусов, проблема уже заметна.
Даже прямоходящий зомбарь, практически круглый в проекции сверху, дергается слишком резко. Для длинной крысы, у которой есть хвост, такой поворот выглядит вовсе дико.
В процессе тривиального патрулирования территории, пресловутая нода GetRandomReachablePointInRadius может выдать точку прямо за спиной, от чего непись МГНОВЕННО поворачивается на 180 градусов.
Да собственно, если угол превышает хотя бы 20 градусов, проблема уже заметна.
Даже прямоходящий зомбарь, практически круглый в проекции сверху, дергается слишком резко. Для длинной крысы, у которой есть хвост, такой поворот выглядит вовсе дико.
Если запрещать крутой поворот программно, непись навсегда застрянет в углу, поскольку зона по факту прямоугольная.
Короче, чтоб траектория патрулирования была красивой и интересной, мешать рандому нельзя. Значит, неписи должны уметь поворачиваться плавно на большой угол. Причем это должно происходить в отдельной ветке дерева поведения, чтобы задача MoveTo оставалась за пределами цикла.
Короче, чтоб траектория патрулирования была красивой и интересной, мешать рандому нельзя. Значит, неписи должны уметь поворачиваться плавно на большой угол. Причем это должно происходить в отдельной ветке дерева поведения, чтобы задача MoveTo оставалась за пределами цикла.
В итоге пришло в голову пользоваться задачей RotateToFaceBBentry, а эту самую BBentry двигать в направлении поворота.
Кстати о повороте: надо иметь фунуцию, которая скажет, правеее или левее от курса новая точка, и как далеко надо поворачивать. По научному, найти угол между Forward Vector и линией, на которой лежат Actor Location и Random Reachable Point.
Кстати о повороте: надо иметь фунуцию, которая скажет, правеее или левее от курса новая точка, и как далеко надо поворачивать. По научному, найти угол между Forward Vector и линией, на которой лежат Actor Location и Random Reachable Point.
Некоторые люди, кроме меня, уже пытались задавать такие вопросы знатокам, но ответы получались не вполне вразумительные.
В принципе, есть стандартная нода VectorCosineAngle2D, однако, непосредственно ей комфортно пользоваться можно только если угол не больше 90 градусов. Дальше приходится шаманить. В итоге возникло нечто, возвращающее ноль, если точка прямо по курсу, и 1,9999, если 180 чуть правее, и -1,9999, если 180 чуть левее.
В принципе, есть стандартная нода VectorCosineAngle2D, однако, непосредственно ей комфортно пользоваться можно только если угол не больше 90 градусов. Дальше приходится шаманить. В итоге возникло нечто, возвращающее ноль, если точка прямо по курсу, и 1,9999, если 180 чуть правее, и -1,9999, если 180 чуть левее.
На ее основе была создана задача для Behavior Tree, которая определяет, куда и насколько следует повернуть непись. В паре с задачей RotateToFaceBBentry она работет в отдельной последовательности, закрытой декоратором Conditional Loop. Когда новая точка оказывается прямо по курсу, декоратор сбрасывается, и непись оказывается во власти задачи MoveTo.
Поворот и анимация не вполне соответствуют друг другу, а потому ноги/лапы неписей откровенно проскальзывают. Но где-нибудь в высокой траве это будет не так заметно. Обратите внимание, что на представленном видео все трое имеют одинаковый для всех интеллект.
Хорошая новость - игра прошла квалификацию на GDWC 2022 (The Game Development World Championship).
На данном этапе не бог весть что, однако дали персональую витрину, которую можно видеть в общем списке, а если фильтровать по тэгам, тонет не очень глубоко, вполне можно долистаться.
https://thegdwc.com/games/274c91d4-a5d0-4302-88b0-98e..
Стало быть, кто-то на игру посмотрит, как минимум, по долгу службы.
На данном этапе не бог весть что, однако дали персональую витрину, которую можно видеть в общем списке, а если фильтровать по тэгам, тонет не очень глубоко, вполне можно долистаться.
https://thegdwc.com/games/274c91d4-a5d0-4302-88b0-98e..
Стало быть, кто-то на игру посмотрит, как минимум, по долгу службы.
Thegdwc
Game Development World Championship 2022
The Game Development World Championship is an annual competition for indie game developers, game development students, and anyone interested in game development to join in.
А теперь я сделал торговлю. В рамках текущего проекта пристегнуть некуда, но зато готовый виджет есть, и полностью реализована механика, связаная с навыком торговли.
Пока итем лежит в инвентаре, интерфейс показывает базовую цену, отражающую объективную ценность предмета в игре. Она зависит от сложности, полезности или уникальности вещи. А когда итем выкладывается для обмена, начинается перерасчет, зависящий от соотношения навыков персонажа и торговца.
Пока итем лежит в инвентаре, интерфейс показывает базовую цену, отражающую объективную ценность предмета в игре. Она зависит от сложности, полезности или уникальности вещи. А когда итем выкладывается для обмена, начинается перерасчет, зависящий от соотношения навыков персонажа и торговца.
Как правило, навык торговца выше, потому его товар оказывается с наценкой, а товар пресонажа - со скидкой. Чем больше разница навыков - тем шире вилка между куплей и продажей.
В принципе, если навык персонажа окажется выше, появится возможность выгребать у торговца все свободные деньги за счет многократной перепродажи одного и того же.
Однако, с ростом навыка персонажа могут встречаться все более грамотные торговцы, а у тогровцев с низким навыком количество свободных денег будет не велико, так что безпредельной наживы не будет.
В принципе, если навык персонажа окажется выше, появится возможность выгребать у торговца все свободные деньги за счет многократной перепродажи одного и того же.
Однако, с ростом навыка персонажа могут встречаться все более грамотные торговцы, а у тогровцев с низким навыком количество свободных денег будет не велико, так что безпредельной наживы не будет.
Наконец, добрался до карты. Долго откладывал, поскольку текущий мир слишком мал, заблудиться невозможно. Тем не менее, в любой приличной игре карта есть обязательно, причем даже две: локальная и глобальная. А что касается движка Unreal Engine, то средства работы с 2D графикой энтузиазма не внушают.
Показать картинку - дело плевое, но выполнить позиционирование объектов - не так просто, как кажется. Нода Set Render Translation обеспечивает комфорт лишь до тех пор, пока карта в неизменном масштабе и неподвижна. Желающий делать масштабирование вынужден сделать еще и скроллинг, а значит, обречен писать функцию вычисления отступа от верхнего левого угла.
Показать картинку - дело плевое, но выполнить позиционирование объектов - не так просто, как кажется. Нода Set Render Translation обеспечивает комфорт лишь до тех пор, пока карта в неизменном масштабе и неподвижна. Желающий делать масштабирование вынужден сделать еще и скроллинг, а значит, обречен писать функцию вычисления отступа от верхнего левого угла.
Виджет предоставляет элементы Vertical Scroll Box и Horizontal Scroll Box, которые можно положить один в другой и получить готовый скроллинг "из коробки". Однако, оно весьма криво работает, а самое страшное - нет никакой возможности узнать, на сколько пикселей уехало изображение.
Короче, реализовал я это дело так, что в Image вставил не 2D текстуру, а динамический материал, и управляю им через параметры ZoomFactor, CoordX, CoordY. Так я хотя бы известно, где верхний левый угол. На самом деле, процедура рассчета координат объектов получилась весьма мозгодробительная. Самое паскудное, выяснилось, что оно зависит от соотношения сторон экрана. Пришлось исходить из предположения, что будет либо 16:9, либо 9:6, либо 4:3. Для каждого из тех вариатнов подобрал свой набор параметров.
Короче, реализовал я это дело так, что в Image вставил не 2D текстуру, а динамический материал, и управляю им через параметры ZoomFactor, CoordX, CoordY. Так я хотя бы известно, где верхний левый угол. На самом деле, процедура рассчета координат объектов получилась весьма мозгодробительная. Самое паскудное, выяснилось, что оно зависит от соотношения сторон экрана. Пришлось исходить из предположения, что будет либо 16:9, либо 9:6, либо 4:3. Для каждого из тех вариатнов подобрал свой набор параметров.