{"id":"scyfren/behaviortree","name":"behaviortree","scope":"scyfren","platform":"roblox","description":"Mirrored from the Wally registry.","version":"0.1.1","latest":"0.1.1","versions":["0.1.1"],"license":"GPL-3.0","licenseRating":"unsafe","licenseCaveats":["Strong copyleft: shipping this in your game plausibly requires releasing your game's entire source under GPL-3.0. Not recommended for closed-source projects."],"licenseVerified":false,"dependencies":{},"integrity":"e789db0ce693ccabe5efe60c474db743cb5452f72f10ecba5cb804faca0b61d4","likes":0,"downloads":0,"install":"forest install scyfren/behaviortree","url":"https://forest.dev/p/roblox/scyfren/behaviortree","files":"https://api.forest.dev/ai/package/roblox/scyfren/behaviortree/files","readme":"Disclaimer: This version of BehaviorTree has not been tested to work with the BehaviorTree Editor plugin made by tyridge77.\r\n\r\n---\r\n# BehaviorTree\r\n\r\nBehaviorTree is an implementation of the \"behavior tree\" paradigm for managing behavior. This allows us to create relatively complex patterns of behavior without much getting \"lost in the sauce\", so to speak. In *behavior trees*, actions are represented as **tasks**, or \"leaves\". These tasks are then collected in a container called a **tree**, which we \"run\" through in order to determine what task should be done at a given point in time.\r\n\r\n## Nodes\r\nNodes contain information about how to handle *something*. This can either be a task, or a manipulation of tasks. In BehaviorTree, there are 3 types of nodes:\r\n* Leafs\r\n* Composites\r\n* Decorators\r\n\r\nCreating nodes creates new objects, so be aware of that when reusing them for different agents.\r\n\r\n### Leafs\r\nLeafs are the foundation of BehaviorTree. They define how to act.\r\n\r\n#### Tasks\r\nThe most commonly used leaf is a `Task`. Let's take a look at how they're written.\r\n\r\n```\r\nlocal SUCCESS,FAIL,RUNNING = 1,2,3\r\n\r\nlocal NewNode = BehaviorTree.Task({\r\n\r\n   -- 'start' and 'finish' functions are optional. only \"run\" is required!\r\n\r\n   start = function(object, ...)\r\n      object.i = 0\r\n      print(\"I've prepped the task!\")\r\n   end,\r\n\r\n   run = function(object, ...)\r\n      object.i = object.i+1\r\n      if object.i == 5 then\r\n          return SUCCESS\r\n      elseif object.i > 5 then\r\n         return FAIL\r\n   end\r\n\r\n   print(\"The task is still running...\")\r\n      return RUNNING\r\n   end,\r\n\r\n   finish = function(object, status, ...)\r\n      object.i = nil\r\n      print(\"I'm done with the task! My outcome was: \")\r\n      if status == SUCCESS then\r\n         print(\"Success!\")\r\n      elseif status == FAIL then\r\n         print(\"Fail!\")\r\n      end\r\n   end\r\n})\r\n```\r\nTasks are created by calling `BehaviorTree.Task()`, with a table defining different **task functions**. When we run a behavior tree, it will \"process\" a node in the order `start -> run -> finish`. These functions will *always be called in this order*.\r\n\r\nThe `start` and `finish` functions are usually used to prep and cleanup the work that a task does, like initializing and destroying object properties. However, it is not necessary to define them. A task will function perfectly fine with just the `run` function alone.\r\n\r\nThe `run` function is the \"base of operations\" for a task. Here, we handle anything we would want to do. When we \"run\" a behavior tree, we would do so in steps. If we wanted real-time behavior, for example, we could run our trees within `RunService.Heartbeat`. Keep in mind the rate at which you will be processing trees when defining this function. Think about where to change the *task state* of a node when writing your function as well. Consider when it should `fail` so that you don't create unintended behavior. (i.e. attacking when you should be walking instead) Remember that you can **only call one state** per step.\r\n\r\nNotice the `object, ...` parameters passed to `start`, `run`, and `finish`. This object is a table that is passed to the tree when `run` is called on it, and any additional parameters are also passed along.\r\n\r\n#### Blackboard Query\r\n\r\nWhen running a tree on an object, a `Blackboard` table will be injected into the object if one does not exist already. This can be used by the Blackboard Query node for easy state lookup. A blackboard query is commonly used to see if a value is set or not in order to dictate the flow of relevant logic in a tree.\r\n\r\nYou can achieve the same effect with tasks, but it's a bit faster if you only need to perform a simple boolean or nil check\r\n\r\n#### Trees\r\nThe `Tree` is a special `Leaf` type that will execute another tree and pass the result of that tree to its parent.\r\n\r\n```\r\nlocal AnotherTree = BehaviorTree.new(...)\r\nlocal NewNode = BehaviorTree.Task({tree = AnotherTree})\r\n````\r\n### Composites\r\nThese nodes take multiple `Leafs` and give them order. In BehaviorTree, we have `Sequence`, `Selector`, `Random` types for `Compites`.\r\n\r\n#### Sequence\r\nThe `Sequence` process the nodes it is given in sequence of the order they are defined. If any of its subnodes fail, then it will not continue to process the `subnodes` that follow it and return a `fail` state itself.\r\n\r\n```\r\nSequence = BehaviorTree.Sequence({\r\n    nodes = {\r\n        node1,\r\n        node2, -- if this failed, the next step would process node1\r\n        node3\r\n    }\r\n})\r\n```\r\n#### Selector\r\nThe `Selector` node will process every node until one of them succeeds, after which it will return `success` itself. If none of its subnodes succeed, then this `Composite` would return a `fail` state.\r\n\r\n```\r\nPriority = BehaviorTree.Selector({\r\n    nodes = {\r\n        node1,\r\n        node2,\r\n        node3 -- this is the only node that suceeded, so Priority would return success\r\n    }\r\n})\r\n```\r\n#### Random\r\nThis `Selector` will randomly select a subnode to process, and will return whatever state that node returns.\r\n```\r\nRandom = BehaviorTree.Random({\r\n    nodes = {\r\n        node1,\r\n        node2,\r\n        node3\r\n    }\r\n})\r\n```\r\nNodes can also have an optional `weight` attribute that will affect `Random`. Default is `1`.\r\n\r\n```\r\nlocal SUCCESS,FAIL,RUNNING = 1,2,3\r\n\r\nnode1 = BehaviorTree.Task({\r\n    weight = 10,\r\n    run = function(object)\r\n        print(\"Weight: 10\")\r\n        return SUCCESS\r\n    end\r\n})\r\n\r\nnode2 = BehaviorTree.Task({\r\n    weight = 10,\r\n    run = function(object)\r\n        print(\"Also weight: 10\")\r\n        return SUCCESS\r\n    end\r\n})\r\n\r\nnode3 = BehaviorTree.Task({\r\n    weight = 200,\r\n    run = function(object)\r\n        print('You probably won't see \"Weight: 10\" printed'.)\r\n        return SUCCESS\r\n    end\r\n})\r\n```\r\n#### While\r\nThe `While` Only accepts two children, a condition(1st child), and an action(2nd child) It repeats until either the condition returns fail, wherein the node itself returns fail, or the action returns success, wherein the node itself returns success.\r\n\r\n```\r\nWhile = BehaviorTree.While({\r\n    nodes = {\r\n        condition, -- If this node returns fail, return fail\r\n        action -- When this node returns success, return success\r\n    }\r\n})\r\n```\r\n### Decorators\r\nDecorators are nodes that wrap other nodes and alter their task state. Right now, there are `Succeed`, `Fail`, `Invert`, and `Repeat` decorators. `Succeed`, `Fail`, and `Invert` are pretty self-explanatory, and are helpful for when you start making more complex trees via nested `Collections`. \r\n\r\nThese can be written as such.\r\n```\r\nInvert = BehaviorTree.Invert({\r\n    nodes = {nodeHere}\r\n})\r\n````\r\n`Repeat` decorators will repeat their children node tasks until `count`, or indefinitely if `count` is nil or < 0, after which they will return a `success` state. If `breakonfail` is true and its child node fails, it will stop repeating and return a `fail` state.\r\n````\r\nRepeat = BehaviorTree.Repeat({\r\n    nodes = {nodeHere},\r\n    count = 3,\r\n    breakonfail = true\r\n})\r\n````\r\n## The Tree\r\nOnce you have your nodes set up and ready to go, we can start planting some trees. A `Tree` usually starts with any `Selector`, which should have `Task` nodes in them or other `Selector` nodes with other nodes in them. They can be instantiated by calling `BehaviorTree.new()` with a `table` containing tree information as its only argument.\r\n\r\n```\r\nTree = BehaviorTree.new({\r\n    tree = BehaviorTree.Sequence({\r\n        nodes = {\r\n            node1,\r\n            node2,\r\n\r\n            BehaviorTree.Random({\r\n                nodes = {\r\n                    node3,\r\n                    node4\r\n                }\r\n            })\r\n        }\r\n    })\r\n})\r\n```\r\nAs you can see, we can nest `Composite` nodes within each other. This is where the magic of behavior trees come in! \r\n\r\n### Running trees\r\nTo run a tree, call `:run` on the tree object, passing it a table. This table is the relevant object or actor that tree is dictating behavior for. You can also pass any additional parameters you desire, and these will be passed along to the task functions.\r\n\r\n```\r\nlocal actorObject = {...}\r\n\r\nTree = BehaviorTree.new({\r\n    tree = BehaviorTree.Sequence({\r\n        -- nodes from earlier\r\n    })\r\n})\r\n\r\nwhile true do\r\n    local treeStatus = Tree:run(actorObject)\r\n    wait(1)\r\nend\r\n```\r\n\r\n---\r\n\r\n## Contributors\r\n\r\n- Originally by iniich_n and tyridge77: https://devforum.roblox.com/t/behaviortree2-create-complex-behaviors-with-ease/451047\r\n- Forked and improved by Defaultio: https://github.com/Defaultio/BehaviorTree3\r\n- Published as BehaviorTree to Wally & NPM by Scyfren\r\n","readmeTruncated":false}