之前运行的行为树,都是一颗总树,那么实际上会有很多的总树,因此需要对行为树进行管理。
BTBehaviorManager.lua
1 BTBehaviorManager = {}; 2 3 local this = BTBehaviorManager; 4 this.isEachFrameCall = false;--是否是每帧调用 5 this.printTreeStr = ""; 6 this.trees = {}; 7 8 function this.RunTree() 9 if (isEachFrameCall) then 10 else 11 while (true) do 12 local isAllFinish = this.OnUpdate(); 13 if (isAllFinish) then 14 break; 15 end 16 end 17 end 18 end 19 20 function this.OnUpdate() 21 local count = 0; 22 23 for i=1,#this.trees do 24 local tree = this.trees[i]; 25 if (tree.executionStatus == BTTaskStatus.Inactive) then --第一次执行 26 print("第一次执行:" .. tree.name); 27 tree:OnUpdate(); 28 elseif (tree.executionStatus == BTTaskStatus.Running) then --第二次以及以后执行 29 print("第二次以及以后执行:" .. tree.name); 30 tree:OnUpdate(); 31 else 32 count = count + 1; 33 end 34 end 35 36 if (count == #this.trees) then 37 return true; 38 else 39 return false; 40 end 41 end 42 43 --添加行为树 44 function this.AddTree(task) 45 table.insert(this.trees, task); 46 end 47 48 --深度优先,打印树结构 49 function this.PrintTree(task) 50 this.printTreeStr = ""; 51 this.AddToPrintTreeStr(task); 52 print(this.printTreeStr); 53 end 54 55 function this.AddToPrintTreeStr(task) 56 local taskType = task.taskType; 57 58 this.printTreeStr = this.printTreeStr .. task:ToString() .. "\n"; 59 60 if (taskType == BTTaskType.Root) then 61 this.AddToPrintTreeStr(task.startTask); 62 elseif (taskType == BTTaskType.Composite or taskType == BTTaskType.Decorator) then 63 for i=1,#task.childTasks do 64 this.AddToPrintTreeStr(task.childTasks[i]); 65 end 66 else 67 68 end 69 end
说明:
1.因为是运行在Sublime环境下的,所以这里使用while循环模拟每帧调用
2.关于AddTree和OnUpdate是否会冲突的问题。AddTree会增加trees的长度,但是在lua中for的三个表达式在循环开始前一次性求值,以后不再进行求值。比如#this.trees这个表达式只会在循环开始前执行一次,其结果用在后面的循环中。也就是说,如果在for循环过程中执行了AddTree方法,新增的元素会添加在后面,不会影响for循环的。
测试:
1.多行为树执行
TestBehaviorTree.lua
1 TestBehaviorTree = BTBehaviorTree:New(); 2 3 local this = TestBehaviorTree; 4 this.name = "TestBehaviorTree"; 5 6 function this:New() 7 local o = {}; 8 setmetatable(o, self); 9 self.__index = self; 10 o:Init(); 11 return o; 12 end 13 14 function this:Init() 15 local parallel = BTParallel:New(); 16 local action = self:GetBTActionUniversal(); 17 local action2 = self:GetBTActionUniversal2(); 18 19 self:SetStartTask(parallel); 20 21 parallel:AddChild(action); 22 parallel:AddChild(action2); 23 end 24 25 function this:GetBTActionUniversal() 26 local count = 1; 27 local a = function () 28 if (count <= 3) then 29 count = count + 1; 30 print("22"); 31 return BTTaskStatus.Running; 32 else 33 return BTTaskStatus.Success; 34 end 35 end 36 local universal = BTActionUniversal:New(nil, a); 37 return universal; 38 end 39 40 function this:GetBTActionUniversal2() 41 local universal = BTActionUniversal:New(nil, function () 42 return BTTaskStatus.Success; 43 end); 44 return universal; 45 end
TestBehaviorTree2.lua
1 TestBehaviorTree2 = BTBehaviorTree:New(); 2 3 local this = TestBehaviorTree2; 4 this.name = "TestBehaviorTree2"; 5 6 function this:New() 7 local o = {}; 8 setmetatable(o, self); 9 self.__index = self; 10 o:Init(); 11 return o; 12 end 13 14 function this:Init() 15 local repeater = BTRepeater:New(2); 16 local sequence = BTSequence:New(); 17 local log = BTLog:New("This is a other tree!!!"); 18 local log2 = BTLog:New("This is a other tree 2!!!"); 19 20 self:SetStartTask(repeater); 21 22 repeater:AddChild(sequence); 23 24 sequence:AddChild(log); 25 sequence:AddChild(log2); 26 end
TestMain.lua
1 require "BehaviorTree/Core/Init" 2 require "BehaviorTree/Test/TestBehaviorTree" 3 require "BehaviorTree/Test/TestBehaviorTree2" 4 5 local tree = TestBehaviorTree:New(); 6 local tree2 = TestBehaviorTree2:New(); 7 tree.name = "tree"; 8 tree2.name = "tree2"; 9 BTBehaviorManager.AddTree(tree); 10 BTBehaviorManager.AddTree(tree2); 11 BTBehaviorManager.RunTree();
输出如下:
2.for循环中添加行为树
TestBehaviorTree3.lua
1 TestBehaviorTree3 = BTBehaviorTree:New(); 2 3 local this = TestBehaviorTree3; 4 this.name = "TestBehaviorTree3"; 5 6 function this:New() 7 local o = {}; 8 setmetatable(o, self); 9 self.__index = self; 10 o:Init(); 11 return o; 12 end 13 14 function this:Init() 15 local repeater = BTRepeater:New(2); 16 local log = BTLog:New("This is TestBehaviorTree3 tree!!!"); 17 18 self:SetStartTask(repeater); 19 20 repeater:AddChild(log); 21 end
BTBehaviorManager.lua
1 BTBehaviorManager = {}; 2 3 local this = BTBehaviorManager; 4 this.isEachFrameCall = false;--是否是每帧调用 5 this.printTreeStr = ""; 6 this.trees = {}; 7 8 function this.RunTree() 9 if (isEachFrameCall) then 10 else 11 while (true) do 12 local isAllFinish = this.OnUpdate(); 13 if (isAllFinish) then 14 break; 15 end 16 end 17 end 18 end 19 20 local temp = false; 21 22 function this.OnUpdate() 23 local count = 0; 24 25 for i=1,#this.trees do 26 if (not temp) then 27 temp = true; 28 require "BehaviorTree/Test/TestBehaviorTree3" 29 local tree3 = TestBehaviorTree3:New(); 30 tree3.name = "tree3"; 31 this.AddTree(tree3); 32 end 33 34 local tree = this.trees[i]; 35 if (tree.executionStatus == BTTaskStatus.Inactive) then --第一次执行 36 print("第一次执行:" .. tree.name); 37 tree:OnUpdate(); 38 elseif (tree.executionStatus == BTTaskStatus.Running) then --第二次以及以后执行 39 print("第二次以及以后执行:" .. tree.name); 40 tree:OnUpdate(); 41 else 42 count = count + 1; 43 end 44 end 45 46 print("------------------------------"); 47 48 if (count == #this.trees) then 49 return true; 50 else 51 return false; 52 end 53 end 54 55 --添加行为树 56 function this.AddTree(task) 57 table.insert(this.trees, task); 58 end 59 60 --深度优先,打印树结构 61 function this.PrintTree(task) 62 this.printTreeStr = ""; 63 this.AddToPrintTreeStr(task); 64 print(this.printTreeStr); 65 end 66 67 function this.AddToPrintTreeStr(task) 68 local taskType = task.taskType; 69 70 this.printTreeStr = this.printTreeStr .. task:ToString() .. "\n"; 71 72 if (taskType == BTTaskType.Root) then 73 this.AddToPrintTreeStr(task.startTask); 74 elseif (taskType == BTTaskType.Composite or taskType == BTTaskType.Decorator) then 75 for i=1,#task.childTasks do 76 this.AddToPrintTreeStr(task.childTasks[i]); 77 end 78 else 79 80 end 81 end
输出如下。可以看到,在第一帧时,添加了行为树tree3,但是并不影响tree和tree2的执行,而在第二帧以及以后的帧时,tree3也开始执行了。