Vit_ amin
Добрая душа
- Сообщения
- 1,504
- Реакции
- 660
Всем привет.
В данной статье вы узнаете:
Рисунок 1 - Типичный шаблон древо поведения
Если рассмотреть рис. 1, то в нём есть несколько концептуальных правил для создания древа:
Сегодня я затрону именно игру CS:GO и добавленный разработичками функционал для создания кастомного AI
Внимание: В теме я затрону только базовую концепцию (идею) создание своего ИИ
В нашем случае создадим папку под название test и переёдем туда
Чтобы движок понимал наши кастомные файлы ИИ, нам необходимо создать два файла:
Открыв файл bt_config.kv3
Пропишите туда следующий код (без вникания, если необходимы какие то уточнения, пишите в комментариях)
Сохраняем и далее переходим к файлу bt_script.kv3
Для начала нам необходимо сказать скрипту, чтобы он подхватыл конфигурационный файл настройки ботов, это делается следующим образом
Как мы с вами ранее узнали, то любое древо поведения начинается с корня, так давайте и начнём с него:
В своем примере я буду показывать создание ИИ для бота, который будет бежать до рандомной точки на карте, при этом скрипт будет считать количество попыток, которое у бота прошли успешно, когда он достиг этих рандомных точек, а когда этих точек накопилось 3 штуки, тогда он будет ждать 1 секунду и говорить в рацию "Чисто (или сектор чист)", потом счетчик сбрасывается и все по новой:
Я скрину сразу готовый результат с полным комментированием каждой строки
После написания кода сохраните его и пропишите в консоль:
Всем спасибо за внимание.
В данной статье вы узнаете:
- Древо поведения (BT - Behavior Tree)
- Базовые принципы и концепты создания своего (кастомного) AI (искуственного интелекта) ботов в CS:GO
- Введение:
- Что такое древо поведения (BT):
Рисунок 1 - Типичный шаблон древо поведения
Если рассмотреть рис. 1, то в нём есть несколько концептуальных правил для создания древа:
- Древа поведения всегда начинаются с его корня (то есть с английского root), данное правило относиться ко всем древам (в нашем случае скриптовый файл будет начинаться именно с данного ключевого слова)
- Conditional - в нашем случае как вы поняли это условие, которое будет выполнено/отклонено в зависимости от того, что разработчик скрипта хочет от игрока под управлением компьютера
- Behavior A или B это как раз таки само поведение бота, который будет действовать в той или иной степени (в нашем случае поведение напрямую зависит от условия conditional, которое находиться выше)
Сегодня я затрону именно игру CS:GO и добавленный разработичками функционал для создания кастомного AI
Внимание: В теме я затрону только базовую концепцию (идею) создание своего ИИ
- Создание ИИ
<CS:GO path>/script/ai
В нашем случае создадим папку под название test и переёдем туда
Чтобы движок понимал наши кастомные файлы ИИ, нам необходимо создать два файла:
- Конфигурационный файл ботов, который содержит то, как их Aim ведет себя при наведении на врага и осмотре игрового мира (плавность поврота/частота поворота камеры/на сколько градусов может поворачиваться камера и другие вещи), также в этом файле указано время реакции бота на врага, какова вероятность того, присядет при стрельбе бот или нет и так далее
- Сам скриптовый файл, где прописан сценарий
- bt_config.kv3 (конфигурация ботов)
- bt_script.kv3 (скриптовый сценарий ботов)
Открыв файл bt_config.kv3
Пропишите туда следующий код (без вникания, если необходимы какие то уточнения, пишите в комментариях)
C-подобный:
<!-- kv3 encoding:text:version{e21c7f3c-8a33-41c5-9977-a76d3a32aa0d} format:generic:version{7412167c-06e9-4698-aff2-e63eb59037e7} -->
{
default =
{
aim_target_acquisition_lerp_time = 0.0
aim_target_acquisition_lerp_time_deviation = 0.0
aim_target_acquisition_angle_penalty = 0.0
aim_target_acquisition_angle_penalty_deviation = 0.0
aim_target_acquisition_angle_penalty_reduction_ratio = 0.0
aim_target_acquisition_angle_tolerance = 0.0
aim_target_acquisition_angle_lerp_bias = 0.0
aim_target_tracking_lerp_time = 0.0
aim_target_tracking_lerp_time_deviation = 0.0
aim_target_tracking_focus_interval = 0.0
aim_target_tracking_focus_interval_deviation = 0.0
aim_target_tracking_angle_lerp_bias = 0.0
aim_ready_angle_tolerance = 0
aim_new_target_angle_tolerance = 0
aim_max_duration = 0
aim_max_duration_deviation = 0.0
aim_punch_angle_reaction_chance = 0.0
look_around_awareness_yaw_range = 360
look_around_awareness_pitch_range = 45
look_around_focus_interval = 1
look_around_focus_interval_deviation = .1
look_around_lerp_time = .4
look_around_lerp_time_deviation = .1
look_around_lerp_bias = .25
reaction_time = 0.3
combat_crouch_chance = 0
combat_dodge_command_duration = 0
combat_dodge_command_duration_deviation = 0
attack =
[
[ -1.0, -1.0, -1.0, -1.0, -1.0 ], [ 0.95, 0.25, 0.2, 0.05, 0.0 ], [ 0.425, 0.075, 0.0, 0.125, 0.025 ],
[ 0.155, 0.035, 0.0, 0.15, 0.05], [ 0.3, 0.0, 0.0, 0.25, 0.05 ], [ 0.22, 0.07, 0.0, 2.0, 0.0 ],
[ 0.9, 0.3, 0.0, 0.15, 0.05 ], [ -1.0, -1.0, -1.0, -1.0, -1.0 ], [ -1.0, -1.0, -1.0, -1.0, -1.0 ],
[ -1.0, -1.0, -1.0, -1.0, -1.0 ], [ -1.0, -1.0, -1.0, -1.0, -1.0 ], [ -1.0, -1.0, -1.0, -1.0, -1.0 ],
[ -1.0, -1.0, -1.0, -1.0, -1.0 ], [ -1.0, -1.0, -1.0, -1.0, -1.0 ], [ -1.0, -1.0, -1.0, -1.0, -1.0 ],
[ -1.0, -1.0, -1.0, -1.0, -1.0 ], [ -1.0, -1.0, -1.0, -1.0, -1.0 ], [ -1.0, -1.0, -1.0, -1.0, -1.0 ],
[ -1.0, -1.0, -1.0, -1.0, -1.0 ], [ -1.0, -1.0, -1.0, -1.0, -1.0 ]
]
}
}
Для начала нам необходимо сказать скрипту, чтобы он подхватыл конфигурационный файл настройки ботов, это делается следующим образом
C-подобный:
<!-- kv3 encoding:text:version{e21c7f3c-8a33-41c5-9977-a76d3a32aa0d} format:generic:version{7412167c-06e9-4698-aff2-e63eb59037e7} -->
{
// путь начинается с папки scripts и до файла (расширение указывать необходимо!)
config = "scripts/ai/test/bt_config.kv3" // В kv3 файлах, вы можете оставлять свои записи и комментарии через привычный символ //
}
C-подобный:
root =
{
}
Я скрину сразу готовый результат с полным комментированием каждой строки
C-подобный:
<!-- kv3 encoding:text:version{e21c7f3c-8a33-41c5-9977-a76d3a32aa0d} format:generic:version{7412167c-06e9-4698-aff2-e63eb59037e7} -->
{
config = "scripts/ai/test/bt_config.kv3" // подгружаем конфигурационный файл
root = // "корень" скрипта
{
// Декораторы - это условные функции в системе древа поведения
// В случае с игрой CS:GO она также выполняет условные функции в нашем случае
// decorator_repeat - это условная функция которая исполняет роль цикла (for/while/foreach и так далее)
// для любого декоратора необходимо наличие ребенка - child - то есть тело цикла (если говорить с точки зрения программирования)
// child - ребенок должен открываться и закрываться кавычками (как указано ранее было, по системе key-values)
type = "decorator_repeat"
child =
{
// selector - это функция (или еще его называют комбинатор), которая выбирает в теле функции
// (в нашем случае тело функции) - это цикл, который именовался decorator_repeat
// selector - выбирает только TRUE функции и обрывает дальнейшее выполнение
// если сравнивать с программирование - это операция continue
// например если написать for (условие)
// то слово selector выполняет функцию условия
// if (true) then - прервать и начать заного
type = "selector"
// children - дети
// когда выбирается любой из комбинаторов (а их в случае с CS:GO - 3)
// selector
// sequencer
// parallel
// то он (комбинатор) должен иметь ряд функций для своего выполнения
// комбинатор использует для своего тела функции квадратные скобки
children =
[
{
// decorator_run_once - это условная функция которая исполняет функцию только один раз
type = "decorator_run_once"
child =
{
// action_set_global_counter - это функция действия, которая создает внутри скрипта глобальную переменную
// Тип переменной Integer
// Имя переменной указывается в input_name (обязательно значало двойные, а затем одинарные кавычки)
// Величина переменной указывается в iinput_value
// В нашем случае имя переменной Test = 1
// Если рассматривать программирование то это будет
// int Test = 1 (SourcePawn)
// То есть мы прописали глобальную переменную, которая создается с инициализацией один раз
// Так как мы работаем в цикле, то данная операция (благодаря декоратору decorator_run_once
// будет выполнена только один раз
type = "action_set_global_counter"
input_name = "'Test'"
input_value = 1
// Функцию закончилась, закрываем кавычки
}
// Функцию закончилась, закрываем кавычки
// Так как у нас комбинатор selector и ниже еще есть функции для выполнения
// то ставим запятую, чтобы скрипт понимал, что эти функции соединены в комбинаторе selector
},
{
// sequencer - это комбинатор, который выполняется до тех пор, пока не сработало ложное условие
// например если написать for (условие)
// то слово sequencer выполняет функцию условия
// if (false) then - прервать и начать заного
type = "sequencer"
children =
[
// decorator_random_approach_point - это условная функция
// которая выбирает рандомную точку (которая указано на навигационной местности карты)
// и записывает в переменную ApproachPoint
// в нашем случае слово output это то, что возвращает функция, когда она выполнила свое действие
// То есть если переписать на программирование
// function decorator_random_approach_point()
// {
// return ApproachPoint;
// }
// Не забываем ставить запятую, так как у нас ниже еще идет код
{
type = "decorator_random_approach_point"
output = "ApproachPoint"
},
// action_move_to - это функция действия, которая дает команду боту, который выполняет этот скрипт двигать в точку
// destination - Это то, куда бот будет следовать
// в нашем случае, мы используем переменную ApproachPoint
// Также мы можем указать константные координаты по пример (X Y Z)
// то есть (в нашем случае это получается Origin)
// destination = "154.32 212.12 0.12"
// movement_type - указывает, что до точки бот будет бежать
// route_type - указывает, что бот будет использовать безопасный по возможности маршрут следования
// то есть, где остсутствовали звуки выстрелов, взрыва гранат, шаги врагов
{
type = "action_move_to"
destination = "ApproachPoint"
movement_type = "BT_ACTION_MOVETO_RUN"
route_type = "BT_ACTION_MOVETO_FASTEST_ROUTE"
},
// Прописываем selector
// так как нам надо выбрать из определенных вариантов верный и закончить тело функции
// в нашем случае как я говорил выше (бот должен 3 раза дойти до точки
// и потом сказать в рацию "Чисто"
// Этим (перебором) мы и будем сейчас заниматься
{
type = "selector"
children =
[
// прописывание sequencer - то есть последовательно читаем код
{
type = "sequencer"
children =
[
// action_compare_global_counter - это функция действия, которая сравнивает
// то есть сравнивает переменные типа Integer
// if (a == b)
// данная функция может сравнивать только переменные
// которые уже созданы через функцию action_set_global_counter (что мы выше и сделали)
// в данном случае мы сравниваем равна ли переменная Test числу 1
{
type = "action_compare_global_counter"
input_name = "'Test'"
input_value = 1
},
// Так как у нас комбинатор sequencer, то если бы переменная была не равна 1, то мы бы вышли из тела функции
// и бот бы снова выбрал случайную точку и пошел к ней
// но так как у нас переменная равна 1
// поэтому мы присваиваем её значение 2
{
type = "action_set_global_counter"
input_name = "'Test'"
input_value = 2
}
]
},
// по аналогии сравниваем (выше расписано)
// только сравниваем уже число 2 и если оно равно,
// тогда присваиваем переменной Test значение 3
{
type = "sequencer"
children =
[
{
type = "action_compare_global_counter"
input_name = "'Test'"
input_value = 2
},
{
type = "action_set_global_counter"
input_name = "'Test'"
input_value = 3
}
]
},
// по аналогии сравниваем (выше расписано)
{
type = "sequencer"
children =
[
{
type = "action_compare_global_counter"
input_name = "'Test'"
input_value = 3
},
{
type = "action_set_global_counter"
input_name = "'Test'"
input_value = 4
}
]
}
]
},
// Если наша переменная Test равна 4, позволяем скрипту дальше выолнить код
{
type = "action_compare_global_counter"
input_name = "'Test'"
input_value = 4
},
// action_wait - это функция действия, которая заставляет бота замереть на одном месту
// wait_time_min - время минимальное, которое бот будет "заморожен" (в нашем случае 1 секунда)
// wait_time_max - время максимальное, которое бот будет "заморожен" (в гашем случае 1 секунда)
// Также данная функция замораживает не только бота, но и заставляет повиснуть скрипт на время
{
type = "action_wait"
wait_time_min = 1
wait_time_max = 1
},
// action_say - это функция действия, которая заставляет бота сказать что-то в радиопереговоры
// phrase - Фраза, которую бот скажет в рацию
// Список фраз находиться в файле botchatter.db
{
type = "action_say"
phrase = "Clear"
},
// Устанавливаем переменной action_set_global_counter значение 1 и начинаем скрипт заного по циклу
// благодаря декоратору decorator_repeat
{
type = "action_set_global_counter"
input_name = "'Test'"
input_value = 1
}
]
}
]
}
}
}
После написания кода сохраните его и пропишите в консоль:
После этого добавьте бота и наблюдаёте за его деёствиямиmp_bot_ai_bt <PATH> (в нашем случае) mp_bot_ai_bt scripts/ai/test/bt_script.kv3
Всем спасибо за внимание.
Последнее редактирование: