User Tools

Site Tools



behaviortree

This is an old revision of the document!


Behavior Tree Framwork

Behavior Trees are a wonderful thing when it comes to writing any kind of AI logic. It is currently in its “first working version” and there are still improvements coming in the future. GW2Minion is completely written with this framework. FFXIVMinion is not making use of this, but it *could* be used as well.

BT Basics, How they work: Behavior trees for AI

If you want to write your own LUA Addon for GW2Minion, this framework is what you need to use.

Tutorials

These Tutorials will show you how to make your own Lua Addon and create some logic with our Behavior Framework.

1. Creating a new Addon

  • Create a new Folder under MinionApp/Bots/…/ with your addon name (I named mine sillyjumper)
  • Create two empty files: module.def and a lua file with your addon name.
  • Copy paste the code into your module.def:
[Module]
Name=sillyjumper
Dependencies=minionlib,GW2Minion
Version=1
Files=sillyjumper.lua
Enabled=1
  • Name - has to match your foldername
  • Dependencies - need to be set in order to be able to use functions defined by these two other “addons”
  • Version - not used yet
  • Files - a comma seperated list of your lua files that should be loaded. The order is important!.
  • Enabled - take a wild guess

Now open your addon lua file (sillyjumper.lua in my case) in a text editor and add this code (ofc. rename it to your addon name):

-- Your Addon should always be a LOCAL table, else anyone could just steal your code
local sillyjumper = {}
 
 
-- We need to defiune a (callback)-function, which enables the BehaviorTree Manager to load the files from your local addon folder.
function sillyjumper.LoadBehaviorFiles()
 
	-- Load all our local "bot/addon" BTree files
	BehaviorManager:LoadBehaviorFromFolder(GetLuaModsPath()  .. "\\\GW2Minion\\\SillyJumper")
 
end
-- Registering our function for the event that is being called by the BT-Manager to refresh the "internal" list of available Behaviors.
RegisterEventHandler("RefreshBehaviorFiles", sillyjumper.LoadBehaviorFiles)


2. Creating a Behavior Tree

We now need to create a new Behavior Tree for our new addon.


3. Behavior Tree Basics

The Root-BehaviorTree-Node is being called about 4-5 times a second. It always starts from the top and goes downwards through the tree until an Action-Node returns self:fail() / self:running() or self:success().


  • How to add your Addon GUI Elements to the Main Menu
  • How to save and load addon settings


5. Let's get Moving - Subtrees

  • How to use existing Subtrees
  • What is the global node context and how to use it
  • Accessing game data and functions




6. Making the Addon Private

At this point, you can freely share your addon with anyone else, they just need to copy the folder into their …/LuaMods/.. folder.
But in the case that you want to keep your code private and put your addon into the minionapp store, the next steps are required.

  • Read how private addons are working in general ⇒ Private Addon API
  • Open the addon lua file we created in the 1st Tutorial (sillyjumper.lua) in the text editor.
  • Instead of just loading “all BehaviorTree files” from our folder like before, we will now change this to load only a specific file, our main Behavior Tree:
-- Your Addon should always be a LOCAL table, else anyone could just steal your code
local sillyjumper = {}
-- Aquire the Private Addon Functions, so we can use them later.
sillyjumper.modulefunctions = GetPrivateModuleFunctions()
 
 
-- We need to defiune a (callback)-function, which enables the BehaviorTree Manager to load our files.
function sillyjumper.LoadBehaviorFiles()
 
	-- The BehaviorTree Framework requires specific information in order to use an "external  private BTree":
	local btreeinfo = {
			-- Our Main Behaviortree filename:
			filename = "sillyjumper.bt",
			-- Our Foldername and a mandatory subfolder where our "sillyjumper.bt" will be loaded from:
			filepath = GetLuaModsPath()  .. "\\\SillyJumper\\data",				
			-- Callback function, when the BTree was changed and saved in the BT-Editor. Reload all addon bt files here, including private subtrees.
			Reload = function() sillyjumper.LoadBehaviorFiles() end,				
			-- if set, it will be treated as a private addon, loadable from the addon store
			private = true,
 
		}
 
 
	-- At this point we now have to branch between loading the files as developer (which has the original lua and .bt files in the local folder)
	-- And the user who just has the precompiled .paf file from the Addon Store in his folder.
	if ( FileExists(GetLuaModsPath()  .. "\\\SillyJumper\\\data\\\sillyjumper.bt")) then 
		d("[SillyJumper] - Loading Developer Version")
		-- Loads the BTree data from the locally present .bt file
		sillyjumper.btree = BehaviorManager:LoadBehavior( btreeinfo )					
 
	else
		d("[SillyJumper] - Loading SillyJumper Addon")
 
		-- We need to load the BTree data by ourself from our local .paf and pass that to the LoadBehavior() to create an instance of it.
		-- Get all files in our ..LuaMods\SillyJumper\data\ folder, find our sillyjumper.bt and load that into the BehavioerManager:
		local files = sillyjumper.modulefunctions.GetModuleFiles("data")
		if(table.valid(files)) then
			for _,filedata in pairs(files) do
				if( btreeinfo.filename == filedata.f) then
					local fileString = sillyjumper.modulefunctions.ReadModuleFile(filedata)
					if(fileString) then						
						local fileFunction, errorMessage = loadstring(fileString)
						if (fileFunction) then
							btreeinfo.data = fileFunction()					
						end
					end
					break
				end
			end
		end
 
		if (table.valid(btreeinfo.data)) then
			sillyjumper.btree = BehaviorManager:LoadBehavior( btreeinfo )
		end
	end
 
	-- At this point we now have a local instance of our BehaviorTree => sillyjumper.btree
	if ( not table.valid(sillyjumper.btree)) then
		ml_error("[SillyJumper] - Failed to load SillyJumper behaviortree")
	end	
 
end
-- Registering our function for the event that is being called by the BT-Manager to refresh its intern list of available Behaviors.
RegisterEventHandler("RefreshBehaviorFiles", sillyjumper.LoadBehaviorFiles)
  • Don't forget to move your .bt file into the mandatory “data” folder (you can use any other folder name ofc, but your code needs to be changed as well then):

  • Now reload your lua modules ingame and you should see that your Addon is loading:

  • Your Addon is now secured and if distributed through the MinionApp-Store, noone is able to read or steal your code.


7. Extending Node Context

As you learned earlier, all BehaviorTree Nodes are sharing a “context” table which can be accessed at any time by all Nodes.
If you have large functions or code parts which are often reused and would clutter the Nodes, you can just move the code into a lua file.
We create a custom “context” table, define the functions and pass that to the BehaviorTree. All Nodes can then access the functions / code.

  • Add the following changes to your code:
  1. Create the sillyjumper.btreecontext = {} table
  2. Extend the local btreeinfo with the new “LoadContext = function() return sillyjumper.GetContext() end,” function
-- Your Addon should always be a LOCAL table, else anyone could just steal your code
local sillyjumper = {}
-- Aquire the Private Addon Functions, so we can use them later.
sillyjumper.modulefunctions = GetPrivateModuleFunctions()
 
-- Our custom context which will hold code and functions and make these available inside the BT-Nodes
sillyjumper.btreecontext = {}
 
 
-- We need to defiune a (callback)-function, which enables the BehaviorTree Manager to load our files.
function sillyjumper.LoadBehaviorFiles()
 
	-- The BehaviorTree Framework requires specific information in order to use an "external  private BTree":
	local btreeinfo = {
			-- Our Main Behaviortree filename:
			filename = "sillyjumper.bt",
			-- Our Foldername and a mandatory subfolder where our "sillyjumper.bt" will be loaded from:
			filepath = GetLuaModsPath()  .. "\\\SillyJumper\\data",				
			-- Callback function, when the BTree was changed and saved in the BT-Editor. Reload all addon bt files here, including private subtrees.
			Reload = function() sillyjumper.LoadBehaviorFiles() end,				
			-- if set, it will be treated as a private addon, loadable from the addon store
			private = true,
 
                        -- (optional) Callback function, is called by the BTree Framework when the BTree is started. Allows us to supply a custom "context" table to the BTree:
			LoadContext = function() return sillyjumper.GetContext() end,
		}
  • Now lets create the Callback function in your lua file where your custom “context” table is being created and all your functions defined.
  • Additionally I'll create a simple test function which will then be made available in the “context” table.
-- Create a function which will then be usable inside the BT-Nodes
sillyjumper.LetsJump = function()
	Player:Jump()
end
 
-- Is called when the BTree is started. Allows us to supply a custom context table to the BTree
function sillyjumper.GetContext()
	sillyjumper.btreecontext.Jump = sillyjumper.LetsJump
 
	return sillyjumper.btreecontext
end

-




Dont mind the stuff below…just things I dont want to forget when finising this tutorial ;)

Please be aware that this GUI

  1. - The “variables” table is always the same for all “instances” of the node.
  2. - The “settings” table holds values which can be different for each “instance” of a node.

— common errors in Node Code: if ( Player.alive ) then

  Inventory:DepositCollectables()
  self:success()

end self:fail()

must be : if ( Player.alive ) then

  Inventory:DepositCollectables()
  self:success()

else

  self:fail()

end

To start an external btree that takes control of the bot if(ml_bt_mgr.running and not ml_bt_mgr.paused) then

ml_bt_mgr.paused = true
my_btree:start()

end

if(my_btree and ml_bt_mgr.paused) then my_btree:run() end

The bot main pulse will continue to run, but the main btree will not.

When your task is completed remember to set ml_bt_mgr.paused to false

behaviortree.1480606915.txt.gz · Last modified: 2016/12/01 15:41 by fxfire