Behavior Trees AI Library

EDIT: Moved to the Community Resources section so more people can use this

Want a snazzy system for creating convincing AI?
Well do I have the library for you.

Introducing…
Behavior Trees!

A behavior tree is a model for AI that is known for being able to switch between a finite set of tasks in a modular fashion. This is because behavior trees break down complicated tasks into smaller, more approachable ones, without worrying about how these small tasks are processed.

Now you may be wondering (if you’ve ever been at all interested in AI design before)
"But OniiCh_n, why not just use a Finite State Machine, or a Hierarchical FSM for a large system, instead?"

Well, the key difference between a Finite State Machine (FSM) and a Behavior Tree is that while FSMs are centered around switching between states (which may contain a wide variety of actions within a single state), Behavior Trees are focused on switching between tasks that have been broken down into more understandable forms.

"But who actually uses behavior trees?"

Have you ever played Halo? Haven’t you ever noticed how the AI in Halo just feels like they actually know what they’re doing?
That’s a behavior tree at work.
While Halo was not the first implementation of behavior trees in video games, they were pretty darn successful in it and that’s one of the features that made their game stand out.

"So how do I get to work using these ‘AI plants’?"
Feel free to grab this model and explore the library.
Installation is really straightforward, just move BehaviorTree into ReplicatedStorage and then require the module with something along the lines of:

BehaviorTree = require(game.ReplicatedStorage.BehaviorTree.behavior_tree)

Example
--This example creates a new BehaviorTree "Frank", who will complete 20 random tasks

    local BT = require(game.ReplicatedStorage.BehaviorTree.behavior_tree)

    local Frank = BT:new({
      object = {name = 'test'},
      tree = BT.Sequence:new({
        nodes = {
          BT.Task:new({
            run = function(task, object)
              print(object.name .. " looking")
              task:success()
            end
          }),
          BT.Random:new({
            nodes = {
              BT.Task:new({
                run = function(task, object)
                  print(object.name .. " walk left")
                  task:success()
                end
              }),
              BT.Task:new({
                run = function(task, object)
                  print(object.name .. " walk up")
                  task:success()
                end
              }),
              BT.Task:new({
                run = function(task, object)
                  print(object.name .. " walk right")
                  task:success()
                end
              }),
              BT.Task:new({
                run = function(task, object)
                  print(object.name .. " walk down")
                  task:success()
                end
              }),
            }
          }),
        }
      })
    })

    for _ = 1, 20 do
      Frank:run()
    end

Want to learn more about how to use behavior trees in your game or you need some documentation to use this specific library? Check out this GitHub page.

Please note that I did not write this library myself. I merely ported it for use on Roblox as part of an ongoing project and decided that it would be great to share it with all you guys.

Happy coding!
<3, OniiCh_n

78 Likes

I was literally just reading the Gamasutra article you linked on your GitHub page a few days ago and tanema’s pure Lua implementation that you forked from. What a coincidence to find this after! I’ll poke around and see how it goes.

3 Likes

It works like a charm.

Here’s an implementation I’ll be using in a project releasing next week. In this, the tree runs through all the tasks I’ve made in succession.

3 Likes

Excuse me, this might seem dumb but I can’t see a use for this.

Let’s see I’m making pacman and I want the ghoasts to move.

Using behavior trees this is the code:

local BT = require(game.ReplicatedStorage.BehaviorTree.behavior_tree)

local PacMan = BT:new({
	object = {name = "Ghoast"},
     tree = BT.Sequence:new({
       nodes = {
         BT.Random:new({
           nodes = {
             BT.Task:new({
               run = function(task, object)
                 print(object.name .. " Go Left")
                 task:success()
               end
            }),
             BT.Task:new({
               run = function(task, object)
                 print(object.name .. " Go Up")
                 task:success()
              end
             }),
             BT.Task:new({
               run = function(task, object)
                print(object.name .. " Go Right")
                 task:success()
              end
             }),
             BT.Task:new({
               run = function(task, object)
                print(object.name .. " Go Down")
                 task:success()
               end
            }),
           }
         }),
       }	
	})
})

function Move()
	PacMan:run()
end

Move()

But if I use math.random this is the code:

function Move()
	local Direction = math.random(1, 4)
	
	if Direction == 1 then
		print"Go Up"
	elseif Direction == 2 then
		print"Go Down"
	elseif Direction == 3 then
		print"Go Left"
	else
		print"Go Right"
	end
end

Move()

I am probably wrong but… Idk.

:man_shrugging:

Edit:

I just watched this and now I kind of get it. But you need a WAY better explanation of how to use this if you let me I could make one. But I would also modify the script to make it easier for the user.

1 Like

I look forward to playing around with this.

You have to actually make the player move in the function, you don’t just do “print” and expect it to move itself.

He was providing an example. He wasn’t actually implementing it.

4 Likes