Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

[AI SCRIPTING] Panda BT: Behaviour Tree scripting

Discussion in 'Assets and Asset Store' started by ericbegue, Feb 26, 2016.

  1. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    Check the indentation.

    Your tree "Patrol" has two child nodes the 'while' and the 'repeat', which raises an exception since the tree node expects exactly one child.

    I guess you want the 'repeat' to be parented to the 'while' instead.

    edit:
    Though the error should point at line 6 instead of line 1. I'll take a closer look at that.
     
    Last edited: May 24, 2016
  2. andyblem

    andyblem

    Joined:
    Nov 11, 2014
    Posts:
    26
    I think in your "Patrol" tree you should have something like
    Code (CSharp):
    1. tree "Patrol"
    2.            sequence
    3.                    while
    4.                          sequence
    5.                                 /*something here*/
    6.                     repeat
    7.                          /*something here*/
     
    Last edited: May 24, 2016
    ericbegue likes this.
  3. devstudent14

    devstudent14

    Joined:
    Feb 18, 2014
    Posts:
    133
    After tinkering with Panda BT for a while, I have to say that even as a novice programmer, your asset seems extremely accessible and powerful. Admittedly, all I've done is make a sequence with two tasks: A cube lerps from blue to red and back to blue again ad infinitum. Next I'm going to attempt a basic behaviour for a wood chopper unit. I'm guessing I'll need at least five tasks: 1) locating the nearest tree, 2) moving towards that tree, 3) chopping the tree down, 4) collecting the wood, and 5) returning the wood to a storage area. I'm also guessing that a sequence would be the most appropriate node for this sort of simple tree?
     
  4. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    Great that you like it and find it accessible and powerful. I believed no advanced programming is necessary to use this tool, now you are confirming it.

    The wood chopper AI would be a good learning project, since it is going to involved some basic BT concepts. For exercice purpose, I'm not going to give you a complete solution, but here are some leads.

    The sequence node is used to execute a series of steps in order. The sequence succeed when all the steps succeed, if only one step fails, all the sequence fails. The sequence would work for the wood chopper AI, but only in a perfect scenario. It might fails is something unexpected happen. For instance, what if the wood chopper is already carrying some wood when the sequence starts?

    Here are the additional nodes that you would need to use:
    • fallback: execute the first successful child (use to define priorities and alternative actions).
    • while: execute a child node only when an assumption/condition is true.
    • not: inverse the return status of a node.
    Let me know if you need more hints, or more details about how a node works.
     
    Last edited: May 26, 2016
    devstudent14 likes this.
  5. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    5,996
    The woodchopper is a great example, sometimes he needs to rest or eat or chit chat with friends if he feels lonely, and if he just went to school and learned to farm then he can farm if the field is closer and there is less of a need for wood (as per the mayor's decision). And if the woodchopper doesn't have an axe then he goes to the toolmaker and gets himself a new axe, or if he went to school for toolmaking, he grabs a piece of wood and forges the axe metal bit and pieces them together.
    How would I do all that with BT panda?
     
  6. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    When you have tasks depending on the completion of other tasks in order to run, you can use a combination of the while node and the fallback to define the dependencies of the tasks. The while node is used to specify the pre-condition for a task to execute, and the fallback to define a priority list, and the end you have a task dependency graph.

    For instance, let's say you have, 3 tasks, A, B, C, with the following dependency.

    Task_A requires the completion of Task_B to run.
    Task_B requires the completion Task_C to to run

    Then your BT script will be something like:
    Code (CSharp):
    1.  
    2. fallback
    3.  
    4.     tree "Task_A"
    5.         while IsTask_B_Completed
    6.             // Actions to complete task A
    7.  
    8.     tree "Task_B"
    9.         while IsTask_C_Completed
    10.             // Actions to complete task B
    11.          
    12.     tree "Task_C"
    13.         // Actions to complete task C
    14.        
    15.    


    .
     
    Last edited: May 26, 2016
  7. Steve-Tack

    Steve-Tack

    Joined:
    Mar 12, 2013
    Posts:
    1,240
    This looks pretty cool. I like the programmer-oriented nature as opposed to those node-based ones.

    I was inspired by this post to try mission logic using behavior trees:
    http://www.gamasutra.com/blogs/SamIzzo/20160517/272814/Behaviour_trees_for_missions_and_AI.php

    I played around adapting the mission system in my current game to one of the visual node-based BT assets, but it's looking like that could get clunky pretty quickly for more complex missions. Panda BT could be more appropriate. I'll try it out when I get time, and maybe try to adapt my existing AI to it also.

    Any thoughts on how appropriate Panda BT would be for "scripting" missions?
     
  8. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    @Steve Tack Thanks, I'm glad you like it.

    With Panda BT you would need to create a library of mission related tasks (which are going to be simple functions on your MissionController: MonoBehaviour), for example: FadeCameraIn/Out, DisplayText, HideHUD, ... Then your mission controller would be driven by a BT script using those tasks to define the mission. You would need some testing conditions as well to define the win and lose conditions or to scenarize the mission.

    For instances, let's say your game is a FPS and the mission consists of defending a line for 5 minutes until the reinforcement arrives while enemies are continuously spawning. Then when the reinforcement has arrived, the goal is to clear the enemy HQ.

    The BT script describing this mission would be:

    Code (CSharp):
    1. tree "root"
    2.     fallback
    3.         while IsPlayerAlive
    4.             sequence
    5.                 tree "DefendTheLine"
    6.                 tree "ClearHQ"
    7.                 DisplayText "Mission Completed!"
    8.         DisplayText "Mission Failed!"
    9.  
    10. tree "DefendTheLine"
    11.     sequence
    12.         DisplayText "Reinforcement will arrive in 5 minutes. Stand your position!"
    13.         race
    14.             Wait 300.0 // wait 5 minutes
    15.             repeat // Keep spawning enemies every 5 seconds.
    16.                 sequence
    17.                     SpawnEnemy
    18.                     Wait 5.0
    19.  
    20. tree "ClearHQ"
    21.     sequence
    22.         DisplayText "Reinforcement has arrived. Assault and clear the HQ!"
    23.         fallback // Wait until the enememy HQ is cleared.
    24.             repeat not IsEnemyHQCleared
    25.             Succeed
    26.  
     
    Last edited: May 26, 2016
  9. Steve-Tack

    Steve-Tack

    Joined:
    Mar 12, 2013
    Posts:
    1,240
    Thanks for posting that example! That's exactly the sort of thing I was talking about. I especially like that you can just mark methods with a "Task" attribute. With the other asset, you have to create separate classes for every single "Action", which seems a bit much.

    Looking forward to giving it a spin!
     
  10. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    5,996
    Thanks, it's starting to make sense. And you said it earlier I think: while in BT is if in c#
    But I always see fallback at the start of a BT instead of sequence why is that? I'm used to have fallback as a last resort thing, like in the case of switch/case, the last thing in case no case has been met.
    What about the dynamic change of priority? I am trying to map the way we do AI in Utility: each task with a weight and the weight changes with he internal state of the person: fear, hunger, boredom etc...
    Code (CSharp):
    1. 0.5 eating                              // 0.5 is the weight = 1/energy
    2. 0.7 seeking amusement        // 0.7 = boredom
    3. 0.2 working                           // 0.2 = energy/boredom
    Let's say the worker energy is low, eating becomes priority over digging trenches, then once energy level is up, but boredom is still high, time for a drink at the bar.

    Finally is it possible to create a custom function that does "while" but with extra debug info like in this case the priority value?

     
  11. devstudent14

    devstudent14

    Joined:
    Feb 18, 2014
    Posts:
    133
    @ericbegue hello! I've made a tiny amount of progress on my wood chopper BT. He can find the closest tree and move there, however there are no provisions for failures (there are no trees, or he cannot move there).

    Before I get into those, I need help with this particular problem. When I made this behaviour with a FSM, the 'strike tree' state would repeat until the tree's health was zero. At that point, the tree would be destroyed and the 'collect wood' state would be activated:

    if (attacker.GetComponent<Animation>()["attack"].time >= .9f)
    {
    if (treeScript.treeHealth > 0)
    {
    chopFSM = ChopFSM.Strike;​
    }
    else if (treeScript.treeHealth <= 0)
    {
    chopFSM = ChopFSM.Collect;​
    }​
    }

    So far, my tree BT script looks like this:

    tree "Root"
    sequence
    GeneratePathToTree //finds closests tree and generates a path with Apex
    MoveToTree //moves to tree and transitions to chop tree when the destination cell has been reached
    ChopTree //strikes the tree until the tree's health is zero​

    How do I get the chopTree task to repeat until the condition is met?​
     
  12. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    @laurentlavigne There is a lot to say to answer you :).

    Actually the if-then logic can be implemented in BT using the sequence node.

    If-then in C#:
    Code (CSharp):
    1. if( condition )
    2. {
    3.    action
    4. }
    if-then In BT
    Code (CSharp):
    1. sequence
    2.     condition
    3.     action

    As for the while node in BT, it is closer to the literal definition of the English word "while" than it is to the C# keyword. First of all, "while" in English indicates there is some elapsing time going on. Secondly, "while" links a conditional clause to the main clause: the conditional clause indicates the delimitation in time of the main clause.

    For examples:
    - The dog eats while he is hungry.
    - You keep your umbrella opened while it is raining.
    - While I was in Marseille, the sun was shinning.
    - While you have gas, you drive.

    Whereas the "while" keyword in C# sharp is a conditional repetition, though the words "repeat" is not visible in the code, just to make things shorter. But what the "while" keyword really means is: while the condition is true, repeat the following block.

    so this in C# sharp:
    Code (CSharp):
    1. while( i < 5 )
    2. {
    3.      i++;
    4. }
    would translate in BT as:
    Code (CSharp):
    1. while IsCounterLessThan 5
    2.     repeat
    3.         IncrementCounter
    4.    
    Though, in BT the while node, alone, does not do repetition. The "while" node will execute the action node while the condition node succeeds. As soon as the the condition node fails, the action node is interrupted and while node stops and fails. And as soon as the the action succeeds, the while node stops and succeeds. There is no repetition of the action node. So the "while" node can be used to execute a node under a condition. You might argue that it is similar to an "if". It is. But the "if" word does not have this notion of time embedded. So, "while" is an "if" augmented with a notion of time, so to speak.

    The sequence node and fallback node are the opposite of one to the other; fallback is the dual of sequence and vice versa:

    • The sequence runs its children one after another while they succeed. It stops on the first failing node.
    • The fallback runs its children one after another while they fail. It stops on the first succeeding node.

    You often see the fallback node at the beginning of a tree, because you often want your AI to try out a list of actions in priority. And if one action fails, try the next one. For example, you want your AI to attack the player, if the attack fails, you want the AI to chase the player, if the chase fails, you want the AI to idle.
    Code (CSharp):
    1. tree "root"
    2.     fallback
    3.         tree "Attack"
    4.         tree "Chase"
    5.         tree "Idle"
    if you replace fallback by sequence, the AI will be broken: if the "Attack" fails, the tree stops there: "Chase" and "Idle" won't happen. So the fallback node is really the appropriate one here.

    The dynamic change in priority happens with the fallback node. With the example above, "Attack" is the priority node. But if it fails, "Chase" will take precedence over "Attack". If "Chase" fails, "Idle" will take precedence over both "Attack" and "Chase".

    Now, if you want to drive your AI with some weights, let's say your AI has some stamina level. If the stamina is low, the AI needs to rest by idling. Then you just need to put a node in the "Chase" tree and the "Attack" tree such that it will fail if the stamina level is too low. Then when "Chase" fails, the next best thing do to is to "Idle":

    Code (CSharp):
    1. tree "Attack"
    2.     while
    3.         sequence // condition
    4.             IsPlayerAttackable
    5.             IsStaminaLevelGreaterThan 8.0
    6.         sequence // action
    7.             MoveTo_Player
    8.             DoMeleeAttack
    9.  
    10. tree "Chase"
    11.     while
    12.         sequence // condition
    13.             IsPlayerChaseable
    14.             IsStaminaLevelGreaterThan 5.0
    15.         sequence // action
    16.             MoveTo_Player
    17.  
    18. tree "Idle"
    19.     // Rest and increase stamina level
    20.  
     
    Last edited: May 27, 2016
  13. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    You would need an additional task that succeeds when the tree health is a above a certain value, and fails otherwise. Then you can use this task with a while and a repeat node:
    Code (CSharp):
    1. sequence
    2.     GeneratePathToTree //finds closests tree and generates a path with Apex
    3.     MoveToTree //moves to tree and transitions to chop tree when the destination cell has been reached
    4.     while IsTreeHealthGreaterThan 0.0 //strikes the tree until the tree's health is zero
    5.         repeat ChopTree
    6.  
    Note that the while node will fail when choping the tree is done. Therefore, the whole sequence will fail (which might be a wanted behaviour). But if you want the sequence to succeed instead, you would need to wrap the while node into a fallback with a Succeed:
    Code (CSharp):
    1. sequence
    2.     GeneratePathToTree //finds closests tree and generates a path with Apex
    3.     MoveToTree //moves to tree and transitions to chop tree when the destination cell has been reached
    4.     fallback //strikes the tree until the tree's health is zero
    5.         while IsTreeHealthGreaterThan 0.0
    6.             repeat ChopTree
    7.         Succeed
     
    devstudent14 likes this.
  14. devstudent14

    devstudent14

    Joined:
    Feb 18, 2014
    Posts:
    133
    This seems to work without the repeat node which is a little confusing. Could it be improved?

    tree "Root"
    fallback
    tree "FindAndChopTree"
    tree "CollectAndStoreWood"​

    tree "FindAndChopTree"
    sequence
    GeneratePathToTree //finds closests tree and generates a path with Apex
    MoveToTree //moves to tree and transitions to chop tree when cell has been reached
    while TreeLives //while the tree's health is > 0
    ChopTree //strikes the tree and removes tree's health​

    tree "CollectAndStoreWood"
    sequence
    CollectWood
    GeneratePathToStorage
    StoreWood​

    p.s. getting more and more excited by this asset :)
     
  15. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    It works because the "Root" tree is repeated by default (there is a check box on the PandaBehaviour component to toggle this option). So when the ChopTree task succeeds, the while succeeds, the sequence succeeds, the tree "FindAndChopTree" succeeds, the fallback succeeds, and finally the tree "Root" succeeds. Then on the next frame, since the root node is repeated, the root restart and ends up at the ChopTree task again (unless TreeLives fails).

    I would keep the "repeat ChopTree" part, since it's more close to how you describe the behavior.

    What to improve? Not that much, so congratulation with your first AI made with BT!

    But I would to the following to increase readability:
    - split FindAndChopTree into two sub trees and put them in a sequence.
    - I guess StoreWood also handle moving towards the storage area, I would split that into MoveToStorage and DropWood.
    - You have GeneratePathToTree and GeneratePathToStorage, which set the destination. Maybe you just need a MoveToDestination task instead of MoveToTree or the eventual MoveToStorage.

    Great that you like it that much! Maybe you could write a review on the Asset Store, it'd be a great support for me, I'd appreciate a lot.
     
    Last edited: May 27, 2016
    devstudent14 likes this.
  16. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    5,996
    If treehealth drops to zero before the character reaches the tree then fallback moves to succeeds so this tree succeeds. Is that desirable?

    I guess Collectwood succeeds only if wood is collected.
     
  17. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    5,996
    Well thank you, that was very nice of you and now I understand more.
    I'll paraphrase just to see if I get it:
    • when you say while and sequence have the notion of time, you mean that unlike c# where things are excuted all on the same frame, BT will wait until each action is done doing its thing before moving on?
    • While is a sequence which exits on condition false (first item after the while keyword). Does it take more than one action? or is it always [While condition action] and the fourth term would be executed after while exits?
    • fallback is the opposite of a sequence and the reason why it's in root is because root always gets repeated (with the repeat root option on) fallback action1 action2, if action 1 suceeds then fallback exits and root will then repeat to fallback action1, instead of fallback pointer moving to action2
    Is there a parallel sequence that executes all the actions at the same time until one fails? And likewise a parallel fallback that executes all the actions at the same time until one succeeds?

    Thanks for the weighted example.

    How would you set up characters that can "learn" new tasks?
     
  18. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    Any node (sequence, while, fallback, parallel, ..., or custom tasks) can run over several frames. So yes, unlike C# sharp, the BT script runs over several frames (In fact, this is the very reason why BT script exists). That makes your logic frame-independent, therefore closer to your game design description in plain English. Loosely speaking, C# runs on the CPU clock whereas a BT script runs on a real clock.

    A typical lifetime of a node is as follow:
    • The node is ready, which means it is inactive and ready to be executed. (it appears in grey)
    • Then the node is ticked by its parent node, the execution of the node starts and it has the running status. (it appears in blue)
    • At some point (after a number of ticks) the node completes, it either fails (red) or succeeds (green).

    While the node is running, at each tick the corresponding C# function is called. The C# function is responsible for the completion of task through Task.current.Succeed() or Task.current.Fail().

    How and in what order the tasks are executed depend on the parent nodes. For details about how each structural node process its children, consult this documentation:
    http://www.pandabehaviour.com/?page_id=23#Structural_nodes

    I don't quiet understand what you mean here. The while has always this structure: [while condition action]. That is the while node always have two child nodes: the condition node and the action node. However the child nodes (condition or action) are not limited to tasks, they can be any node type, including other structural nodes. This allows you to define complex condition and complex action.

    Yes, in this situation:
    Code (CSharp):
    1. tree "root"
    2.     fallback
    3.         action1
    4.         action2
    5.  
    If action1 succeeds, the fallback succeeds and finally the root succeeds. Then, on the next tick the root is restarted and we have the same process again.

    Yes, you have the parallel node and the race node, which run all their children at the same time. Likewise as the sequence and fallback, the parallel is the dual of the race:
    • The parallel node runs all its children at the same time and fails if one child fails. It succeeds if all children succeed.
    • The race node runs all its children at the same time and fails if all children fail. It succeeds if one child succeeds.


    You're welcome. :)

    Making a character to "learn" is beyond the purpose of Panda BT. Though, you have the possibility to generate scripts at runtime depending on what the character has learned so far. But exactly how you generate those scripts is up to you.

    If you are interested in that aspect of AI and depending of what you mean by "learn", you might be interested by Machine Learning and as well as Pattern Recognition. Beware that those are academical fields using advanced mathematics and they are not really oriented towards video games.
     
    Last edited: May 28, 2016
  19. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    5,996
    Thanks for all these answers. I finally get it.

    I don't mean leaning in the cognitive sense but in the RPG sense: add a new, predetermined skill.
    For example the woodchopper learns at school how to forge, when his axe breaks and there is no available axe in the tool shed, the woodchopper goes to the forge and makes the tool and then resumes his woodchopping.
    In GOAP you would just add a forge action (precondition has iron, has wood sticks - result new axe) and the path builder would include the new action when it builds a path to meet a demand.
    I know that I can add the forge tree text asset to the BT array and append the tree "forge" lines to the root fallback then call .Compile() ,but I am sure there is a more elegant and less bug prone way to do this.

    Is there a way to set a variable in Panda? Like
    Code (Boo):
    1. Sequence
    2.         var target = SetTarget
    3.         MoveTo target
    Another question, this time not related to BT, Panda is 5.21+, do you make use of 5+ unique features? the 5 cycle is still pretty bad as of 5.4 so I'll be using 4.7 for new projects and was wondering if you could release one for this as well.
     
    Last edited: May 28, 2016
  20. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    Then it is a matter of handling the game state. This as well beyond of the purpose of Panda BT. However Panda BT is more data oriented since the behaviours are just texts, so a BT is just another asset in your project, you don't need to define the woodchoper behaviour at runtime. Your woodchooper character would have his own BT. So your GOAT logic would be translated as:
    Code (CSharp):
    1.  
    2. tree "root"
    3.     fallback
    4.         tree "CollectWood"
    5.         tree "BuildAxe"
    6.         tree "WanderInVillage"
    7.  
    8. tree "CollectWood"
    9.     while HasAxe
    10.         // Actions to collect wood
    11.  
    12. tree "BuildAxe"
    13.     while
    14.         sequence HasIron HasWoodStick HasLearnedAxeForging
    15.         // Action to build axe
    16.  
    17. tree "WanderInVillage"
    18.     // actions to wander
    19.      
    20.  
    Panda BT only accept only the following primitive variable types in script: bool, int, float, string.
    But keep in mind that you don't have to manage everything from the BT script, there are a lot you can do from the C# side. So if you want to set a Transform as a target, you can do the following:
    Code (CSharp):
    1. using UnityEngine;
    2. using Panda;
    3.  
    4. public class CharacterAI : MonoBehaviour
    5. {
    6.     public GameObject target; // The target variable is exposed here.
    7.     public Vector3 destination;
    8.  
    9.     [Task]
    10.     void SetDestination_Target()
    11.     {
    12.         destination = target.transform.position;
    13.         Task.current.Succeed();
    14.     }
    15.  
    16.     [Task]
    17.     void MoveToDestination()
    18.     {
    19.         bool hasReachedDestination = false;
    20.  
    21.         // Moving to destination
    22.         // ...
    23.  
    24.         if (hasReachedDestination)
    25.             Task.current.Succeed();
    26.     }
    27. }
    28.  
    The from the BT script you can do:
    Code (CSharp):
    1. sequence
    2.     SetDestination_Target
    3.     MoveToDestination
    4.    
    I know it is a pain to convert a project from 4 to 5.
    But do you mean Unity 5 is not stable enough to start a new project with?

    I have no intention to support backward compatibility with 4.7. Have a look at this thread:
    http://forum.unity3d.com/threads/unity-version-usage-might-be-useful-for-package-devs.250891/page-3

    What would be a reason to not start a new project with Unity 5?

    I hope I have answered your questions.
     
    Last edited: May 28, 2016
  21. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    5,996
    Thanks again, I ended up storing the target in c# and it's super clean. Other BT use a dictionary as a blackboard, hardcoding variables in c# modules is a better separation of task IMO.

    To answer your question I do not use 5.x because lightmapping is very broken as you can see in numerous threads here http://forum.unity3d.com/forums/global-illumination.85/ and it is also quite unstable compared to 4.7, better the devil you know, you know.
     
  22. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    I also though about implementing a blackboard system for Panda BT, but I've realized that it would be useless since you can easily manage the variables on the C# side, and that, however you want. Therefore it is more flexible this way.

    I have not really intensively use the light baking system in Unity 5, so I can't tell about it. But the number of threads does not mean that it is broken, does it? There are as well plenty of threads in the scripting section and even more frequently, it does not mean that the scripting engine is broken.
     
  23. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    5,996
    Can the definition for "tree BuildAxe" be inside a separate file? The same way tasks can be in separate c# scripts - I just tried : YES
    Is there a better way to add the tree "BuildAxe" line to the root fallback than appending the line to the string then compiling? - just tried, array trees are exclusive. It would be useful to add an append option.
    See what I'm trying to do here? Dynamically add actions without having to maintain this mega list of all 20 possible actions in the root file.

    I think I can live without full GOAP implementation now but I do miss its action cost evaluation. Some sort of action chain optimization. Is chopwood, move to wood store, dropwood, move to cantina, eat cheaper than chopwood, dropwood, walk to store, get bow, hunt, eat?
    Is there a way to do that with Panda?

    I know what you mean, scripting is very different, you get either knowitall who complain about technicalities or noobs who try their luck but in the case of GI, it is an artist tool, mostly a one button switch so in this case, yes the number of complains reflect to the real state of affair.
    And what those thread don't show is the attitude of the guy in charge of Enlighten who has been acting like a car salesman trying to sell a lemon with Ante stubbornly defending a broken system.
    As a result, confidence in UT ability to fix it is very low. (might be solved in 5.5 but I hear some people laughing already)
    It is so bad that shader/coders are making a fortune circumventing enlighten for companies who made the mistake of switching to 5.x too early and realizing the enligten mess is unworkable.
    My personal experience is that mixed lights are broken, probes show wrong light shade, textures tear and the GI solution is ugly looking, the results undependable and it is about 10x slower than beast. So I gave up and didn't upgrade my 4.x pro license to 5.x and when I attempted to use 5.4 for a new project I saw the new and de-enhanced "personal edition" logo which looks so cheap it might as well say "Unity starving student edition" or "wife beater edition", you know, with a pile of burning tires in the background.
     
    Last edited: May 29, 2016
  24. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    Yes, as you've already discovered. This way you can write a library of actions that you can share among several PandaBehaviour components.

    Instead of adding the tree at runtime, you could have the tree already defined in the BT script but with an extra task to enable/disable it, similarly to the HasLearnedAxeForging task.

    There is not currently a praticable way to do this with Panda. But I am actually wondering if that could be done by introducing a new structural node. What you want is to define a dependency graph of tasks, and for each task having an evaluation function giving the cost of each task. Then a plan can be made at run time to decide which sequence of actions is the best. Though, I'm not sure how that would practically work and how that would be integrated into the existing BT system. I would need to spend some time with pen a paper to come up with something. If you have some ideas in that direction, you're welcome.

    That was lyrical!

    There are so much changes between 4 and 5, that's why I've dropped support for 4 and focus on 5 instead. Supporting both versions is a lot of work. I would rather spend my work on improving and polish what currently exist. So, I hope they will stabilized that in 5.4 or 5.5. Maybe I could make a simpler package (without the examples for instance) for 4.7, we'll see.
     
  25. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    5,996
    That's what I thought, workable but not very modular.

    When you do the pen and paper, please share a scan :) Love to see your thinking process with this.
    I'm thinking:
    Code (CSharp):
    1. [Cost (float ThisTaskCostFunction())]
    2. [Task]
    3. void ThisTask(){}
    4. float ThisTaskCostFunction(){}
    Where ThisTaskCostFunction can be just a value [Cost (2.5f)]
    And the BT would need a new keyword, let's say
    Code (CSharp):
    1. Optimum
    2.      Option
    3.      Option
    4.      Option
    5.        
    Each 'option' can be an action or a structure keyword and when optimum is parent each one will calculate its cost in its own unique way: [Cost (function())] for tasks, and total of all costs for sequence recursively. The other structures require more thought, I think random would require the random value to be chosen at cost calculation, While may have very high cost by default or what you could have is

    Code (CSharp):
    1. float Keyword ...
    where the float sets a cost value.

    I'm sure you'll find something more elegant.

    I completely understand. If there is a way I can help, PM me.
     
    Last edited: May 31, 2016
  26. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    Why would you need to append tasks at runtime. The behaviour of a character is known and written at editing time, event if some tasks are not executable because some requirements are not fulfilled. Could you please provide a use case where append task would be necessary at runtime?
     
  27. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    5,996
    The player writes a new BT for the main AI loop. in game or offline modding. also it is more rugged to code in components, in this case a BT script would be a component. not pocking at the main AI loop = less errors
     
    Last edited: May 31, 2016
  28. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    If the player can write his own BT script, he is responsable for checking for errors as well.
    I don't quiet understand. How would you decrease the chance of errors? What do you mean by "a BT script would be a component"?

    Other than happening a new line to the script, I don't see any other way.
    How would do this otherwise (ideally)?
     
  29. ZJP

    ZJP

    Joined:
    Jan 22, 2010
    Posts:
    2,649
    tatoforever and ericbegue like this.
  30. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    @ZJP
    Don't worry. Those are just discussions. So far, it has not turned into plan for implementations. Simplicity (less is more) is important for me and complications is what I am trying to avoid at most. So any feature proposal would go through a thoughtful consideration before being integrated. In general I'd removed stuffs instead of adding new ones. It's through this process that Panda BT has become what it is today. Yet, I am opened to suggestions and I'd consider them all.
     
    ZJP likes this.
  31. tatoforever

    tatoforever

    Joined:
    Apr 16, 2009
    Posts:
    4,337
    @ericbegue
    I suggested the drag and drop editor because I believe is something that can run independently of your actual Inspector code and doesn't change anything or adds any overhead. And yes focusing on one single thing (BTs) is what you need to keep.
     
  32. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    @tatoforever The current discussion is about GOAP, which would be problematic to integrate. The drag&drop editor will become a supported feature (pro only).

    Speaking of which, I would like to announce that I've completed the drag&drop editor and it will be included into the next release (1.3.0), which is currently on his way to the Asset Store.

    Here are the main features of the drag&drop editor:
    • Now you can interact with any nodes:
      • You can drag & drop the nodes around to organize the tree hierarchy (similarly to the Unity scene Hierarchy tab).
      • The node parameters are also editable similarly to any inspector input field.
    • Copy/Paste/Cut/Delete/Duplicate work as expected and Undo/Redo is supported (the corresponding keys and shortcuts are functional).
    • When you modify a script through this editor, the modified version concerns only the current GameObject instance unless you "apply" the modifications (which will save the modifications to the original script). You can also "revert" the modifications. This mechanism is similar to how prefabs work. For instance, you can use this feature to have local variations without duplicating scripts.
    • Task creation: You can select where to insert a new task with a right click (dragging with RMB will display a visual cue about where the task will be inserted. Then, at the selected location, drop-down menu will be available listing all the available tasks, grouped by MonoBehaviours, implemented on the current GameObject (see attached screenshot).
    For those of you who already purchased the pro edition and do not want to wait for the review process (which should last less than 2 weeks if everything is ok), just PM me.

    task-selection.png
     
    Last edited: Sep 6, 2016
    tatoforever likes this.
  33. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    Panda BT 1.3.0 (Free and Pro) are now available on the Asset Store.

    Here is the release notes:

    Core
    • Added in-Inspector drag&drop editor (Pro Only).
    • Refactored tasks binding.
    Fixes:
    • Fixed null exception occurring when a task is completed outside of its method (expl: from a callback).
    • Fixed mute node without child not raising exception.
    Minor changes
    • BT script fullpath is displayed in exception messages instead of file name only.
    • Modified break point UI.
     
  34. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    I've been considering GOAP for a while, and the more I'm learning about this system the less it seems attractive.

    The main avantage of GOAP would be that the AI would decide "by itself" the best course of actions to take to reach a given goal, by evaluating the cost of each action and taking the less costly sequence of actions. Which would result into a more dynamic behaviours, somewhat emergent because the behaviours has not been designed. I think the emergent behaviour is due to a lack of good overview of the person who is designing the planner. Because even if the AI dynamically decide a course of actions, all the decisions the AIs is making are based on the prior knowledge (action dependency graph + cos evaluation functions) that has been fed into the system by the person who designed the planner.

    Also, such system makes it hard to define a specific and wanted behaviour, since it would need to tweak the cost functions in order to obtain an expected behaviour. This is what Alex J. Champandard calls "baby-sitting" the planner in his 2010 retrospective.

    To conclude, integrating GOAP with Panda BT would not result into a better tool for specifying behaviours.
     
    Last edited: Jun 9, 2016
  35. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    5,996
    Don't focus too much on GOAP, there are other better system with better control of the behaviors. HTN for example is BT with a planner on top, also much easier to design.

    Alex's entry of 2013 on planners: http://aigamedev.com/open/review/planning-in-games/
     
  36. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    Thanks for this resource @laurentlavigne. It's a great overview about what have been done with planning in the last decade.

    What I am struggle with (when I said there is a conflict between BT and GOAP) is also expressed in this video: there is a gap between planners and reactive systems. Yet, both approaches can lead to the design of the same behaviours.

    Though, planners would be more appropriate in complex and interactive environments such as in open world games.

    According to him, so far, what could be done with planners could be done with reactive systems. However, planners seems to have an exciting potential that has not been fully expressed yet and still have to come. Which sounds promising.
     
    Last edited: Jun 10, 2016
  37. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    5,996
    HTN is BT with a planner on top so there is zero gap.

    Maybe that high level decision making can be done on top of Panda, so i'm wondering:
    • monos can control Tick, can they also decide which tree to execute?
    • If I want an interrupt behavior, something that stops all behavior if certain conditions are met, like health too low or friend in danger, how do I do that in BT? My guess is replacing the fallback in the root by a parallel but there is no interrupt or stopall keyword that would allow such interuption without writing the interuption (while) in every single task
     
    Last edited: Jun 10, 2016
  38. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    I can see where HTN have some advantages: it can be useful in the case you don't want to design a well defined behaviour but rather defined some constraints and hope the AI will figure out something, this would be particularly useful if your list of actions is large.

    I think that would be the most reasonable approach: having a planner on top of BT. But as it is now, there is no built-in mechanism to do that.

    You can use "while" to interrupt an action if a condition is not fulfilled. For instance:
    Code (CSharp):
    1. tree "root"
    2.     fallback
    3.         tree "Attack"
    4.         tree "Defend"
    5.  
    6. tree "Attack"
    7.     while
    8.         sequence // condition to attack
    9.             not HasLowHealth
    10.             not IsFriendInDanger
    11.         DoAttack
    12.  
    13. tree "Defend"
    14.     DoDefend
    The AI will attack only if there is enough health and no friend is in danger (note that the sequence implements an AND logic, since the sequence succeed only if all its children succeed, likewise the fallback node implements a OR logic).
    Is that what you want to do?
     
    Last edited: Jun 11, 2016
  39. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    5,996
    Is this something you're interested in developing?

    Yes but I'd like a high level interrupt if that's possible, I have a lot of actions and would like to avoid redundant code.
     
  40. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    The while node is appropriate to interupt an action. It will bail out of the current tree branch then another part of the tree will be evaluated.
    What do you mean by "high level interupt"? Do you have an example that can not be solved by the while node?
     
  41. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    5,996
    bugs:
    1. Multi object edit isn't supported so it's not possible to give multiple prefabs the same bt script
    2. when you select a prefab with a BT on it from the projects view, Panda Behavior, the BT component shows up as closed in the inspector and you get errors in the console:

    Code (CSharp):
    1. NullReferenceException: Object reference not set to an instance of an object
    2. Panda.BehaviourTree.get_btSources ()
    3. Panda.BehaviourTree.Compile ()
    4. Panda.BehaviourTreeEditor.CheckComponentChanges ()
    5. Panda.BehaviourTreeEditor.OnInspectorGUI ()
    6. UnityEditor.InspectorWindow.DrawEditor (UnityEditor.Editor editor, Int32 editorIndex, Boolean rebuildOptimizedGUIBlock, System.Boolean& showImportedObjectBarNext, UnityEngine.Rect& importedObjectBarRect) (at /Users/builduser/buildslave/unity/build/Editor/Mono/Inspector/InspectorWindow.cs:1231)
    7. UnityEditor.DockArea:OnGUI()
    8.  
     
  42. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    5,996
    While in the root will do.
     
  43. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    5,996
    Panda BT is showing slow performances with 1000 objects: 16000 fixedUpdate calls, why so many?
    Note: I'm using the Move example as template for the benchmark, so it's a very simple BT script.

    Screen Shot 2016-06-20 at 6.18.23 PM.png
     
    Last edited: Jun 21, 2016
    ZJP likes this.
  44. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    @laurentlavigne
    Thank you for the bug report.
    1. Multi edit is not currently supported.
    2. There was a similar bug in the previous versions. Are you using 1.3.0?
     
  45. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    1000 objects driven with a BT is a lot. I would suggest an upper bound of 400-500 active objects (with complex BT scripts). Unless your are making a massive RTS game, that bound should be enough. In a general usage, the CPU cost of the custom tasks would be much higher than the Panda BT overhead. Also, even if the move example is simple, there are quiet some computations with Vector3s and Quaternions going on, and this example is not optimized.

    FixedUpdate is called but not much is happening in there, there is only one if statement to figure out the "tick on" option (that's part of the mechanism to select when the BT should be ticked).
    But 16000 is indeed quiet a lot. That would be 16 times per frame per object. A frame duration should be ~0.16 s (for 60 fps), so I guess you've set the fixed delta time to 0.001 s, which causes FixedUpdate to be called 20 times more than default. The default fixed delta time is 0.02.

    About the general performance of Panda BT, I think it is quiet decent so far. There might be space for optimization, but I think it's not worth spending time on this side. Since, for a normal game, the bottle neck won't be the BT overhead. For instance in your benchmark just the camera rendering is already of the same magnitude of BehaviourTree.Update.
     
    Last edited: Jun 22, 2016
  46. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    5,996

    Nope

    It's not an RTS and needs loads of units. This benchmarks is to determine what system can handle that many and yet remain easy to design.

    Nope
    Deep profiling reveals that string formatting eating up a lot, Vector operations barely register.
    The string formatting is odd because I added the appropriate "if" to prevent debug string operation when the entity isn't being inspected. Do you parse script during execution or do you hash all that at loadup?

    On Awake the spike is gigantic about 8000ms with 2000 ms of GC, it's manageable from the game side by staggering spawning during reload but I'm wondering what happens in there.
    I pool object, where destroy is replaced by deactivate, and when re activated, the entities' BT seem to resume instead of reset. How do I reset the BT?

    Nope
    it's set at 0.02, what happens is the framerate tanks, fixedupdate being at fixed interval gets called more between frames.

    Nope
    Benchmark is designed to look at raw bt usage, and there are many ways to cull the number of displayed units, the world simulation needs to keep up though.

    You make too many assumptions to be objective. So I'll be a dick and tell you to go walk in the forest to clear your head. When you're done have a look at your code, you'll probably find a lot to improve :p
    (that's what I do)
     
  47. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    @laurentlavigne Short answer:
    Panda BT is not appropriate to handle more than 400-500 BTs.

    Supporting more than that is not on my todo list. Even if Panda BT would have a 0 ms overhead, you still need to make the task do something (A BT without tasks is useless): I am claiming that the task implementations would become a bottleneck before the tree traversal does. So, there is no need to optimize the tree traversal any further.
     
    Last edited: Jun 24, 2016
  48. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    Good idea, pooling won't make the BTs recompile. You can reset a BT by using Reset() on the PandaBehaviour component.
     
  49. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    5,996
    That's very clean - no spike just a bit of GC allocation!

    Benched that number of entities as well and the overhead isn't bad at all, I wonder why there is such an increase in the panda overhead when it reaches 1000. Anyway I'll just use Panda for more advanced behavior and keep going at the Sim with that hybrid utility layered flocking. Cheers.
     
  50. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    5,996
    Instantiating is far too spikey, definitely needs to be optimized.
    Have a look at these:
    10 BT instances at a time (move script) coroutined
    Screen Shot 2016-06-30 at 7.51.58 PM.png
    1 BT instance at a time coroutined
    Screen Shot 2016-06-30 at 7.56.00 PM.png
    no BT 10 at a time
    Screen Shot 2016-06-30 at 7.52.30 PM.png
     
    ILVI_ likes this.