Поведение деревья для проектов Unity3D. Написано с помощью подхода, управляемого кодом для максимизации обслуживания в крупных проектах с помощью шаблона застройщика. Вдохновлен свободным деревом поведения.
Функции
Поддерживать
Присоединяйтесь к сообществу Discord, если у вас есть вопросы или нужна помощь.
Посмотрите на предстоящие функции и прогресс развития на доске Trello.
При создании деревьев вам понадобится хранить их в переменной, чтобы правильно кэшировать все необходимые данные.
using UnityEngine ;
using CleverCrow . Fluid . BTs . Tasks ;
using CleverCrow . Fluid . BTs . Trees ;
public class MyCustomAi : MonoBehaviour {
[ SerializeField ]
private BehaviorTree _tree ;
private void Awake ( ) {
_tree = new BehaviorTreeBuilder ( gameObject )
. Sequence ( )
. Condition ( " Custom Condition " , ( ) => {
return true ;
} )
. Do ( " Custom Action " , ( ) => {
return TaskStatus . Success ;
} )
. End ( )
. Build ( ) ;
}
private void Update ( ) {
// Update our tree every frame
_tree . Tick ( ) ;
}
}
В зависимости от того, что вы возвращаете для статуса задачи, произойдет разные вещи.
tree.Tick()
перезагрузит дерево, если нет других узлов, чтобы запуститьtree.Tick()
. Ссылка на указатель отслеживается деревом и может быть очищена только в том случае, если называется tree.Reset()
. Пока переменная хранилища дерева установлена на public
или имеет атрибут SerializeField
. Вы сможете распечатать визуализацию вашего дерева, пока игра работает в редакторе. Обратите внимание, что вы не можете просматривать деревья, пока игра не работает. Поскольку дерево должно быть встроено, чтобы быть визуализированным.
Вы можете безопасно добавить новый код в свои поведенческие деревья с помощью нескольких строк. Позволяя вам настраивать BTS при поддержке будущих обновлений версий.
using UnityEngine ;
using CleverCrow . Fluid . BTs . Tasks ;
using CleverCrow . Fluid . BTs . Tasks . Actions ;
using CleverCrow . Fluid . BTs . Trees ;
public class CustomAction : ActionBase {
protected override TaskStatus OnUpdate ( ) {
Debug . Log ( Owner . name ) ;
return TaskStatus . Success ;
}
}
public static class BehaviorTreeBuilderExtensions {
public static BehaviorTreeBuilder CustomAction ( this BehaviorTreeBuilder builder , string name = " My Action " ) {
return builder . AddNode ( new CustomAction { Name = name } ) ;
}
}
public class ExampleUsage : MonoBehaviour {
public void Awake ( ) {
var bt = new BehaviorTreeBuilder ( gameObject )
. Sequence ( )
. CustomAction ( )
. End ( ) ;
}
}
Дерево поведения жидкости используется через менеджер пакетов Unity. Чтобы использовать его, вам нужно добавить следующие строки в свой файл Packages/manifest.json
. После этого вы сможете визуально контролировать, какую конкретную версию дерева поведения жидкости вы используете из окна диспетчера пакетов в Unity. Это должно быть сделано, чтобы ваш редактор Unity мог подключиться к реестру пакетов NPM.
{
"scopedRegistries" : [
{
"name" : " NPM " ,
"url" : " https://registry.npmjs.org " ,
"scopes" : [
" com.fluid "
]
}
],
"dependencies" : {
"com.fluid.behavior-tree" : " 2.2.0 "
}
}
Архивы конкретных версий и заметок о выпуске доступны на странице релизов.
Возможно, вы захотите взглянуть на проект Capture The Flag пример для рабочего примера того, как можно использовать дерево поведения жидкости в вашем проекте. Это демонстрирует использование в реальном времени с подразделениями, которые пытаются запечатлеть флаг, захватывая силу, чтобы попытаться одержать верх.
Возможно, вы захотите взглянуть на проект Capture The Flag пример для рабочего примера того, как можно использовать дерево поведения жидкости в вашем проекте. Это демонстрирует использование в реальном времени с подразделениями, которые пытаются запечатлеть флаг, захватывая силу, чтобы попытаться одержать верх.
Дерево поведения жидкости поставляется с надежной библиотекой предварительных действий, условий, композитов и других узлов, чтобы помочь ускорить процесс разработки.
Вы можете создать общее действие на лету. Если вы обнаружите, что повторно используете те же действия, вы можете изучить раздел о написании собственных индивидуальных действий.
. Sequence ( )
. Do ( " Custom Action " , ( ) => {
return TaskStatus . Success ;
} )
. End ( )
Пропустите несколько клещей на дереве поведения.
. Sequence ( )
// Wait for 1 tick on the tree before continuing
. Wait ( 1 )
. Do ( MyAction )
. End ( )
Ожидает, пока в deltaTime
истек срок числа секунд.
. Sequence ( )
. WaitTime ( 2.5f )
. Do ( MyAction )
. End ( )
Вы можете создать общее условие на лету. Если вы обнаружите, что повторно используете те же действия, вы можете изучить раздел о написании собственных пользовательских условий.
. Sequence ( )
. Condition ( " Custom Condtion " , ( ) => {
return true ;
} )
. Do ( MyAction )
. End ( )
Случайно оценить узел как истинный или ложь на основе прошедшего шанса.
. Sequence ( )
// 50% chance this will return success
. RandomChance ( 1 , 2 )
. Do ( MyAction )
. End ( )
Запускает каждый узел ребенка по порядку и ожидает статуса успеха отметить следующий узел. Если отказ будет возвращен, последовательность перестанет выполнять дочерние узлы и возвращать неудачу родителю.
Обратите внимание, что важно, чтобы за каждой композицией сопровождается оператором .End()
. Это гарантирует, что ваши узлы должным образом вложены, когда дерево построено.
. Sequence ( )
. Do ( ( ) => { return TaskStatus . Success ; } )
. Do ( ( ) => { return TaskStatus . Success ; } )
// All tasks after this will not run and the sequence will exit
. Do ( ( ) => { return TaskStatus . Failure ; } )
. Do ( ( ) => { return TaskStatus . Success ; } )
. End ( )
Запускает каждый узел ребенка, пока успех не будет возвращен.
. Selector ( )
// Runs but fails
. Do ( ( ) => { return TaskStatus . Failure ; } )
// Will stop here since the node returns success
. Do ( ( ) => { return TaskStatus . Success ; } )
// Does not run
. Do ( ( ) => { return TaskStatus . Success ; } )
. End ( )
Случайно выбирает дочерний узел с алгоритмом перетасовки. Взгляды, пока Success
не будет возвращен или каждый узел не стерт. Перетасовывает каждый раз, когда дерево изначально начинает запускать его.
. SelectorRandom ( )
. Do ( ( ) => { return TaskStatus . Failure ; } )
. Do ( ( ) => { return TaskStatus . Success ; } )
. Do ( ( ) => { return TaskStatus . Failure ; } )
. End ( )
Запускает все детские узлы одновременно, пока все они не вернутся к успеху . Выходит и останавливает все рабочие узлы, если кто -либо из них вернет неудачу .
. Parallel ( )
// Both of these tasks will run every frame
. Do ( ( ) => { return TaskStatus . Continue ; } )
. Do ( ( ) => { return TaskStatus . Continue ; } )
. End ( )
Декораторы - это родительские элементы, которые обертывают любой узел, чтобы изменить возвращаемое значение (или выполнить специальную логику). Они чрезвычайно мощные и отличный комплимент действий, условиям и композитам.
Вы можете обернуть любой узел своим собственным кодом декоратора. Это позволяет вам настраивать функциональность повторного использования.
Примечание . Вы должны вручную вызовать Update()
на детском узле, иначе он не будет стрелять. Также каждый декоратор должен следовать заявлению .End()
. В противном случае дерево не будет строить правильно.
. Sequence ( )
. Decorator ( " Return Success " , child => {
child . Update ( ) ;
return TaskStatus . Success ;
} )
. Do ( ( ) => { return TaskStatus . Failure ; } )
. End ( )
. Do ( ( ) => { return TaskStatus . Success ; } )
. End ( )
Отмените возвращенный статус дочернего узла, если это TaskStatus.Success
или TaskStatus.Failure
. Не меняет TaskStatus.Continue
.
. Sequence ( )
. Inverter ( )
. Do ( ( ) => { return TaskStatus . Success ; } )
. End ( )
. End ( )
Вернуть TaskStatus.Success
, если ребенок возвращает TaskStatus.Failure
. Не меняет TaskStatus.Continue
.
. Sequence ( )
. ReturnSuccess ( )
. Do ( ( ) => { return TaskStatus . Failure ; } )
. End ( )
. End ( )
Return TaskStatus.Failure
Если ребенок возвращает TaskStatus.Success
. Не меняет TaskStatus.Continue
.
. Sequence ( )
. ReturnFailure ( )
. Do ( ( ) => { return TaskStatus . Success ; } )
. End ( )
. End ( )
Вернуть TaskStatus.Continue
независимо от того, какой статус возвращает ребенок. Этот декоратор (и все потомки) может быть прерван, вызывая BehaviorTree.Reset()
.
. Sequence ( )
. RepeatForever ( )
. Do ( ( ) => { return TaskStatus . Success ; } )
. End ( )
. End ( )
Return TaskStatus.Failure
Если ребенок возвращает TaskStatus.Failure
, в противном случае он возвращает TaskStatus.Continue
.
. Sequence ( )
. RepeatUntilFailure ( )
. Do ( ( ) => { return TaskStatus . Success ; } )
. End ( )
. End ( )
Return TaskStatus.Success
Если ребенок возвращает TaskStatus.Success
, в противном случае он возвращает TaskStatus.Continue
.
. Sequence ( )
. RepeatUntilSuccess ( )
. Do ( ( ) => { return TaskStatus . Success ; } )
. End ( )
. End ( )
Деревья могут быть объединены с несколькими линиями кода. Это позволяет создавать инъекционные деревья поведения, которые связывают разные узлы для сложных функциональности, таких как поиск или атака.
Будьте предупреждены, что сплайсированные деревья требуют недавно построенного дерева для инъекции, так как узлы только скопированы на .Build()
.
using CleverCrow . Fluid . BTs . Trees ;
using CleverCrow . Fluid . BTs . Tasks ;
using UnityEngine ;
public class MyCustomAi : MonoBehaviour {
private BehaviorTree _tree ;
private void Awake ( ) {
var injectTree = new BehaviorTreeBuilder ( gameObject )
. Sequence ( )
. Do ( " Custom Action " , ( ) => {
return TaskStatus . Success ;
} )
. End ( ) ;
_tree = new BehaviorTreeBuilder ( gameObject )
. Sequence ( )
. Splice ( injectTree . Build ( ) )
. Do ( " Custom Action " , ( ) => {
return TaskStatus . Success ;
} )
. End ( )
. Build ( ) ;
}
private void Update ( ) {
// Update our tree every frame
_tree . Tick ( ) ;
}
}
Что делает жидкое поведение настолько мощным, так это способность писать свои собственные узлы и добавлять их в строитель без редактирования любого источника. Вы даже можете создать пакеты Unity, которые добавляют новые функциональность строителя. Например, мы можем написать новый метод создания деревьев, который устанавливает цель вашей системы ИИ с несколькими строками кода.
var tree = new BehaviorTreeBuilder ( gameObject )
. Sequence ( )
. AgentDestination ( " Find Enemy " , target )
. Do ( ( ) => {
// Activate chase enemy code
return TaskStatus . Success ;
} )
. End ( )
. Build ( ) ;
Для создания вашего первого пользовательского действия займет около 3 минут. Сначала создайте новое действие.
using CleverCrow . Fluid . BTs . Tasks ;
using CleverCrow . Fluid . BTs . Tasks . Actions ;
using UnityEngine ;
using UnityEngine . AI ;
public class AgentDestination : ActionBase {
private NavMeshAgent _agent ;
public Transform target ;
protected override void OnInit ( ) {
_agent = Owner . GetComponent < NavMeshAgent > ( ) ;
}
protected override TaskStatus OnUpdate ( ) {
_agent . SetDestination ( target . position ) ;
return TaskStatus . Success ;
}
}
Затем нам нужно расширить сценарий BehaviorTreeBuilder
который наше новое действие AgentDestination. Для получения дополнительной информации о расширениях класса C# см. Официальные документы.
using CleverCrow . Fluid . BTs . Trees ;
public static class BehaviorTreeBuilderExtensions {
public static BehaviorTreeBuilder AgentDestination ( this BehaviorTreeBuilder builder , string name , Transform target ) {
return builder . AddNode ( new AgentDestination {
Name = name ,
target = target ,
} ) ;
}
}
И ты закончил! Сейчас вы создали пользовательское действие и расширяемое строитель поведения, который является будущим для новых версий. Следующие примеры будут более такими же. Но каждый охватывает другой тип узла.
Вы можете создать свои собственные действия со следующим шаблоном. Это полезно для объединения кода, которое вы используете постоянно.
using UnityEngine ;
using CleverCrow . Fluid . BTs . Tasks ;
using CleverCrow . Fluid . BTs . Tasks . Actions ;
public class CustomAction : ActionBase {
// Triggers only the first time this node is run (great for caching data)
protected override void OnInit ( ) {
}
// Triggers every time this node starts running. Does not trigger if TaskStatus.Continue was last returned by this node
protected override void OnStart ( ) {
}
// Triggers every time `Tick()` is called on the tree and this node is run
protected override TaskStatus OnUpdate ( ) {
// Points to the GameObject of whoever owns the behavior tree
Debug . Log ( Owner . name ) ;
return TaskStatus . Success ;
}
// Triggers whenever this node exits after running
protected override void OnExit ( ) {
}
}
Добавьте свой новый узел в расширение.
using CleverCrow . Fluid . BTs . Trees ;
public static class BehaviorTreeBuilderExtensions {
public static BehaviorTreeBuilder CustomAction ( this BehaviorTreeBuilder builder , string name = " My Action " ) {
return builder . AddNode ( new CustomAction {
Name = name ,
} ) ;
}
}
Пользовательские условия могут быть добавлены со следующим примером шаблона. Вы захотите использовать их для чеков, таких как зрение, если ИИ может перейти в местоположение, и другие задачи, которые требуют сложной проверки.
using UnityEngine ;
using CleverCrow . Fluid . BTs . Tasks ;
public class CustomCondition : ConditionBase {
// Triggers only the first time this node is run (great for caching data)
protected override void OnInit ( ) {
}
// Triggers every time this node starts running. Does not trigger if TaskStatus.Continue was last returned by this node
protected override void OnStart ( ) {
}
// Triggers every time `Tick()` is called on the tree and this node is run
protected override bool OnUpdate ( ) {
// Points to the GameObject of whoever owns the behavior tree
Debug . Log ( Owner . name ) ;
return true ;
}
// Triggers whenever this node exits after running
protected override void OnExit ( ) {
}
}
Добавьте новое условие в свой поведенческий строитель дерева поведения со следующим фрагментом.
using CleverCrow . Fluid . BTs . Trees ;
public static class BehaviorTreeBuilderExtensions {
public static BehaviorTreeBuilder CustomCondition ( this BehaviorTreeBuilder builder , string name = " My Condition " ) {
return builder . AddNode ( new CustomCondition {
Name = name ,
} ) ;
}
}
Дерево поведения жидкости не ограничено просто пользовательскими действиями и условиями. Вы можете создать новые композитные типы с довольно простым API. Вот пример базовой последовательности.
using CleverCrow . Fluid . BTs . TaskParents . Composites ;
using CleverCrow . Fluid . BTs . Tasks ;
public class CustomSequence : CompositeBase {
protected override TaskStatus OnUpdate ( ) {
for ( var i = ChildIndex ; i < Children . Count ; i ++ ) {
var child = Children [ ChildIndex ] ;
var status = child . Update ( ) ;
if ( status != TaskStatus . Success ) {
return status ;
}
ChildIndex ++ ;
}
return TaskStatus . Success ;
}
}
Добавление пользовательских композитов в ваше дерево поведения так же просто, как и добавление действий. Просто берет одну строку кода.
using CleverCrow . Fluid . BTs . Trees ;
public static class BehaviorTreeBuilderExtensions {
public static BehaviorTreeBuilder CustomSequence ( this BehaviorTreeBuilder builder , string name = " My Sequence " ) {
return builder . ParentTask < CustomSequence > ( name ) ;
}
}
Декораторы также могут быть написаны на заказ, чтобы сократить повторяющийся код.
using CleverCrow . Fluid . BTs . Decorators ;
using CleverCrow . Fluid . BTs . Tasks ;
public class CustomInverter : DecoratorBase {
protected override TaskStatus OnUpdate ( ) {
if ( Child == null ) {
return TaskStatus . Success ;
}
var childStatus = Child . Update ( ) ;
var status = childStatus ;
switch ( childStatus ) {
case TaskStatus . Success :
status = TaskStatus . Failure ;
break ;
case TaskStatus . Failure :
status = TaskStatus . Success ;
break ;
}
return status ;
}
}
Реализация декораторов аналогична композитам. Если вам нужно установить аргументы на композит, вы захотите взять добычу на методе BehaviorTreeBuilder.AddNodeWithPointer()
using CleverCrow . Fluid . BTs . Trees ;
public static class BehaviorTreeBuilderExtensions {
public static BehaviorTreeBuilder CustomInverter ( this BehaviorTreeBuilder builder , string name = " My Inverter " ) {
// See BehaviorTreeBuilder.AddNodeWithPointer() if you need to set custom composite data from arguments
return builder . ParentTask < CustomInverter > ( name ) ;
}
}
Если вы используете автоматическое форматер, он, вероятно, будет управлять форматированием вашего кода с помощью синтаксиса Builder. Чтобы избежать этого, вы можете отключить форматирование, как и в Ridebrains Rider. Если вам нужна конкретная IDE, не должно быть слишком сложно Google, конкретный форматирование отключения комментариев, необходимого вам.
// @formatter:off
_tree = new BehaviorTreeBuilder ( gameObject )
. Sequence ( )
. Condition ( " Custom Condition " , ( ) => {
return true ;
} )
. Do ( " Custom Action " , ( ) => {
return TaskStatus . Success ;
} )
. End ( )
. Build ( ) ;
// @formatter:on
Чтобы получить доступ к ночным сборкам develop
, которые подходят для менеджера пакетов, вам нужно вручную редактировать свои Packages/manifest.json
так же.
{
"dependencies" : {
"com.fluid.behavior-tree" : " https://github.com/ashblue/fluid-behavior-tree.git#nightly "
}
}
Обратите внимание, что для получения новой ночной сборки вы должны удалить эту линию и любые связанные данные о блокировке в манифесте, пусть Unity восстановит, а затем добавьте ее обратно. Поскольку Unity блокирует хэш коммита для URL -адресов GIT в качестве пакетов.
Если вы хотите запустить, чтобы запустить среду разработки, вам нужно будет установить Node.js. Затем запустите следующее из корня один раз.
npm install
Если вы хотите создать сборку npm run build
, и он заполнит папку dist
.
Все коммиты должны быть сделаны с использованием Commerizen (который автоматически устанавливается при запуске npm install
). Коммуты автоматически собираются в номера версий при выпуске, так что это очень важно. PRS, которые не имеют коммита на основе коммита, будут отклонены.
Чтобы сделать тип коммита следующим в терминале из корня
npm run commit
Пожалуйста, смотрите документ «Соответствующие руководящие принципы» для получения дополнительной информации.
Спасибо этим замечательным людям (ключ эмодзи):
Пепельный синий | Джесси Талавера-Гринберг | PuresaltProductions ? | Мартин Дуверджи ? | вызовов ? | ПИОТР ДЖАСТБЕБСКИ | Sounghoo |
Tnthomas ? | СОБСТВЕННЫЙ | Angstr0m ? | Иззи ? | Джеремивансник |
Этот проект следует за спецификацией всех контролей. Взносы любого вида приветствуются!
Спасибо этим замечательным людям (ключ эмодзи):
Этот проект следует за спецификацией всех контролей. Взносы любого вида приветствуются!