Search Unity

[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,294
    @laurentlavigne
    8 MB for instantiating 10 BTs is indeed abnormally high.

    What are your benchmarks exactly with the coroutine and "no BT 10 at a time"?

    I have not yet closely evaluate the memory footprint of BTs and their initialization. It is a good opportunity for me to investigate that and try optimize where it's possible.

    Thank for pointing that out.
     
    Last edited: Jul 1, 2016
  2. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    2,033
    -coroutine yield every 10 instances (10 instances/frame) for 200 instances
    -coroutine yield every instance (1 instance/frame)
    -instancing 200 at a time

    "no BT" = no panda or move.cs was attached, only cost was the animator state machine

    I use this to instantiate, pooling overhead doesn't even register :

    Code (CSharp):
    1.     IEnumerator ByInstantiate(){
    2.         var timer = Time.realtimeSinceStartup;
    3.         for (int i = 0; i < howManyToInstantiate; i++) {
    4.             Instantiate (prefabs [Random.Range (0, prefabs.Length)], 20 * Random.insideUnitSphere + transform.position, Quaternion.identity);
    5.             if (i % 10 == 0)
    6.                 yield return null;
    7.         }
    8.         yield return null;
    9.         Debug.Log ("instancing " + (Time.realtimeSinceStartup - timer));
    10.     }
     
    Last edited: Jul 2, 2016
  3. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    2,033
    Note to the ones following this thread:

    Eric has been fixing and optimizing Panda and it is now 25x more efficient at instantiation, allocating only 36KB/instance. Instantiating 200 panda objects now hiccup just a little and the second batch doesn't even spike at all. In contrast, it used to hang for a few seconds.

    So yay Panda is now the most efficient BT available on the asset store.
     
    Last edited: Jul 14, 2016
    MV10, Martin_H and ZJP like this.
  4. ZJP

    ZJP

    Joined:
    Jan 22, 2010
    Posts:
    2,646
    Good news. Thanks. :cool:
     
    laurentlavigne likes this.
  5. kumar123k

    kumar123k

    Joined:
    Jul 5, 2016
    Posts:
    25
    it will be nice to have a video walk through on how to use the tool.
     
    laurentlavigne likes this.
  6. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    2,033
    I just captured a walk through of how I use Panda but I was rambling too much so I'll let Eric do it.
     
    Last edited: Jul 15, 2016
    kumar123k likes this.
  7. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    2,033
    @ericbegue how do I do an interrupt?
    In this case I want the animal to follow the food. So I use a parallel look on HasTargetMoved but this SetDestination_Target always waits for MoveTo_Destination to reach its destination before it runs. Isn't parallel running all children concurrently?

    Code (CSharp):
    1. tree("Go Eat")
    2.     while HasTarget
    3.         parallel
    4.             sequence HasTargetMoved
    5.                 SetDestination_Target
    6.             sequence
    7.                 MoveTo_Destination
    8.                 Eat
    EDIT: the solution was to repeat the target update loop like so:
    Code (CSharp):
    1. tree("Go Eat")
    2.     while HasTarget
    3.         parallel
    4.             //update the target if needed
    5.             repeat
    6.                 sequence HasTargetMoved
    7.                     SetDestination_Target
    8.                     Wait(0.1)
    9.             //move until reach then eat
    10.             sequence
    11.                 MoveTo_Destination
    12.                 Eat
     
    Last edited: Jul 15, 2016
    kumar123k likes this.
  8. kumar123k

    kumar123k

    Joined:
    Jul 5, 2016
    Posts:
    25
    i like panda Ai it was simple and was robust.
    i would like to see see editor modification window so that we can be able to tweak values from the editor.
     
    laurentlavigne likes this.
  9. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,294
    It feels good to have that good old forum again!

    @laurentlavigne Thank you for comparing Panda BT with other assets. Your benchmarks and feedbacks was a huge contribution to that optimization, you have a lot of credit for it, so thank you.

    @All If you have any remark or suggestion, even if it seems insignificant, please speak up. I'd be glad to hear it. Any feedback about how you are using Panda BT or how you want to use it, is welcome.

    When I'm done with some polishing, I intend to do a series of tutorials. Meanwhile, you can use this forum if you want something to be clarified.

    Great that you found a solution. Here is another way, it's not better or worst, it's just different:
    Code (CSharp):
    1. tree("Go Eat")
    2.     while HasTarget
    3.         repeat // repeat while we have a target
    4.             fallback
    5.                 while not HasTargetMoved // Stop moving if the target has moved.
    6.                     sequence
    7.                         MoveTo_Destination
    8.                         Eat
    9.                 sequence  // Set the destination target if target has moved
    10.                     HasTargetMoved
    11.                     SetDestination_Target
    It's great that you like it!

    The pro edition comes with an in-Inspector editor where the parameter are editable as any inspector field. Also you can organize your tree by drag-n-drop and you can insert a new task by selecting it from a drop-down box listing all the available tasks. The pro edition offers also break point debugging directly in the Inspector.
     
    Last edited: Jul 15, 2016
    laurentlavigne likes this.
  10. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    2,033
    Is there an "if" node? I thought I saw "if" with 2 children (condition, then & else)

    The following tree gives me an error on the "if":
    PandaScriptException: Task node has 3 children. None is expected in file 'BT.Worm Furry': line 4

    Code (CSharp):
    1. tree("LiveLife")
    2.     if IsAttacked
    3.         tree("Counter Attack")
    4.         fallback
    5.             tree("Go Eat")
    6.             tree("Mutate")
    7.             tree("Search For Food")
    When I change it to something that computes, the panda inspector stays error, I have a scene object selected, not a prefab.
     
    Last edited: Jul 16, 2016
  11. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    2,033
    I wanted to share with you all how fast and easy it is to make interesting behaviors in Panda BT.

    In this new game, furry worms are an evolution of the worm that serve as guard and I wanted them to gang up against any animal that attacks them or other members of their families (snail, worm, mother worm)

    20 minutes after I decided to implement it I was done.

    Here are a few bits of the code, maybe this can help someone.

    BT:
    Code (CSharp):
    1. tree ("Counter Attack")
    2.     sequence
    3.         Acquire_Prey_Attackant
    4.         tree ("Go Kill")
    C#:
    Code (CSharp):
    1. public static Dictionary<int, HashSet<GameObject>> attackants = new Dictionary<int, HashSet<GameObject>>();
    2.     [Task]
    3.     bool IsAttacked(){
    4.         attackants [familyGroup].RemoveWhere (a => a == null); //cleanup
    5.         return attackants[familyGroup].Count>0;
    6.     }
    7.     [Task]
    8.     bool Acquire_Prey_Attackant()
    9.     {
    10.         acquiredPrey = ClosestObject(transform.position, Unit.attackants[unit.familyGroup]);
    11.         return acquiredPrey != null;
    12.     }
     
  12. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,294
    There is no "if" node, though you can implement the if-logic using a sequence:
    Code (CSharp):
    1. sequence
    2.     IsSomeConditionTrue
    3.     DoSomeAction
    And you can implement the if-then-else logic using a sequence and a fallback:
    Code (CSharp):
    1. fallback
    2.     sequence
    3.         IsSomeConditionTrue
    4.         ThenDoSomeAction
    5.     ElseDoSomeAction
    6.  
    Side note: There was an if-node in the early development of Panda BT, but I droped it when I realized it was a duplication with the sequence and fallback nodes.
     
  13. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    2,033
    You really want to keep panda minimalist. Is the error message not catching this because you still have 'if' in your dictionary?
     
  14. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    2,033
    What's a more optimize form for this?

    Code (CSharp):
    1. tree("LiveLife")
    2.     fallback
    3.         while IsAttacked
    4.             tree("Counter Attack")
    5.         while not IsAttacked
    6.             fallback
    7.                 tree("Go Eat")
    8.                 tree("Mutate")
    9.                 tree("Search For Food")
     
  15. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,294
    Yes, I try to keep stuffs as simple as possible.
    "if" is not a keyword. But it get analysed as a task. Since a task can not have children, an error is raised.

    You can embed the while conditions inside the sub-trees. That would simplify the "LiveLife" tree and make the sub-tree self contained:
    Code (CSharp):
    1. tree("LiveLife")
    2.     fallback
    3.         tree("Counter Attack")
    4.         tree("Go Eat")
    5.         tree("Mutate")
    6.         tree("Search For Food")
    7.          
    8.  
    9. tree("Counter Attack")
    10.     while IsAttacked
    11.         // do stuffs
    12.  
    13. tree("Go Eat")
    14.     while not IsAttacked
    15.         // do stuffs
    16.      
    17. tree("Mutate")
    18.     while not IsAttacked
    19.         // do stuffs
    20.  
    21. tree("Search For Food")
    22.     while not IsAttacked
    23.         // do stuffs
    24.  
     
    Last edited: Jul 17, 2016
  16. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    2,033
    "if" isn't a task in my components, so did you forget to remove it from the dll?
     
  17. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,294
    @laurentlavigne
    That's not some residual error due to the if node being removed. You could renamed "if" to something else, the error would be the same.
    Any node that's not a keyword (tree, sequence, fallback, parallel, race, random, not, mute) is assumed to be a task, even if the corresponding C# implementation does not exists. The well-form structure of the tree is checked before searching for task implementations.
     
  18. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    2,033
    It would make more sense from a user perspective to know implementation error before form.

    This is cleaner, I'll do that.
     
  19. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,294
    In any other scripting/programming language the parsing goes in this order:

    1. Token
    2. Syntax
    3. Semantic
    4. Context

    If a step fails, the process stop there, therefore eventual errors occurring in the following steps are not displayed.

    But it might be a good idea to display both errors to avoid a two-passes-correction of the BT script.
     
  20. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    2,033
    Token are what, the actual keywords right? So in this case "if" is the token, which should error before the context
     
  21. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,294
    In Panda BT, the whole process of parsing the script file to obtain an executable BT goes as follow:

    1. Tokenisation
    The input file is parsed to produce a sequence of tokens. A token is a symbol recognized by the language, it can be a keyword (tree, sequence, while, ... ), the open and closed parenthesis or a word: the task name, the node parameters.

    2. Parsed tree generation and syntax check.
    The token are analysed to produce the node hierarchy. The syntax and the tree structure are check at this stage.

    3. Runtime BT generation
    Based on the node hierarchy, an executable version of the tree is produced.

    4. Task binding to the C# sharp functions.
    Each task are bound to their respective C# implementation. It's only at this stage that task implementations are looked up via reflection.

    In the case of "if", it passes the tokenisation (step 1). Since it is not a keyword it is recognized as a word. Then, in the step 2, it is recognized as a task (since it can't be anything else, it must be a task). Then the tree structure is checked. We know that tasks are leaf node, that is, they don't have children. But when you write:
    Code (CSharp):
    1.     if IsAttacked
    2.         tree("Counter Attack")
    3.         fallback
    The parser, found a task named "if". And it figures out that this task has three children: another task named "IsAttacked", a sub-tree "Counter Attack" and a fallback node. Then it will raise an error, since tasks are supposed to be leaf node and have no children. And the process stops there because an error occurred, so, step 3 and 4 are not even reached. So it does not check if there is actually a task named "if" implemented in C#.
     
    Last edited: Jul 20, 2016
  22. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    2,033
    I understand. I thought tokens were all keywords.
    Then yes if it could go past step 2 to give task not found that would be superb
     
    Last edited: Jul 20, 2016
  23. Martin_H

    Martin_H

    Joined:
    Jul 11, 2015
    Posts:
    3,873
    Is that update online already? I'm using the free version and it's dated June 7th on the assetstore page.

    In my project I noticed that it seems the "Wait" node is very slow. This is my tree:

    Code (csharp):
    1. tree("Root")
    2.    parallel
    3.      repeat
    4.        mute
    5.          tree("TestWaiting")
    6.      repeat  
    7.        mute
    8.          tree("Think")
    9.      repeat
    10.        mute
    11.          tree("Move")
    12.      repeat
    13.        mute
    14.          tree("Fight")
    15.  
    16.  
    17.    
    18. tree("TestWaiting")
    19.    sequence
    20.      Wait 5.0
    21.    
    22.    
    23.    
    24. tree("Think")
    25.    sequence
    26.      Wait 1.0
    27.      ThinkMove
    28.      
    29.  
    30.  
    31. tree("Move")
    32.    MoveAlongPath
    33.  
    34.  
    35. tree("Fight")
    36.    while HasLOS
    37.      sequence
    38.        Aim
    39.        Wait 0.2
    40.        Shoot
    41.        Wait 2.0
    42.  
    43.  

    In-editor the profiler measures ~ 42ms and 0.7mb GC alloc for all the BT updates (~400 instances). When I remove all Wait commands, and the entire TestWaiting tree and the call to it, then it drops to ~8ms with 0b GC alloc.

    Is this something that is fixed once the next update goes online?


    I have to say for Panda BT being free, it is hard-to-believe-it's-given-away-for-free levels of amazing! I was skeptical at first because it comes as a DLL, but when I saw you can buy the pro version to get the source, I gave it a try. I find it very comforting to know that there's the option to go pro if I should ever hit a problem for which I need source access. I think it is a very clever business model and I hope it works.

    It had a bit of a learning curve for me, because I have never used behaviour trees before and the concept was new to me. If you want to further improve the documentation for noobs like me, I suggest to give some short examples of typical BT scripts with explanations on how and why they work. I didn't pick the BTs in the example project apart yet, if those are commented thoroughly, maybe it would be enough to select a few examples and just add them to the online documentation. Just a thought.

    Keep up the great work Eric!
     
    MV10 likes this.
  24. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,294
    @Martin_H
    The GC alloc optimization will be included in 1.3.1, which is coming soon.

    Some tasks (including Wait) was indeed leaking CG alloc due to string formatting for displaying debug info in the inspector. The allocations should occur only when the BT is displayed in the inspector (where the debug info is actually displayed). This has been fixed and 1.3.1 will include this fix aswell.

    I am finishing some wrapping before submitting Panda BT 1.3.1 to the Asset Stores. Meanwhile, if you want 1.3.1 before it is published, just PM me and send it to you.

    Panda BT is (or became) free mainly because of two reasons:
    • Behaviour Tree is not a very popular technique (compared to FSM for instance). (I believe BT is even an underrated tool).
    • BT in the form of scripting is very unusual. All the other BT packages on the Asset Stores consists of visual node editor and are not really oriented towards programmers.
    These two points combined would make it very unlikely for a user to spend money on a tool he barely knows and can't really see what it leverages. In short, Panda BT is free just to let people know and experience what the tool actually do. Of course, if it becomes more popular it might not be free anymore :p. So, consider you guys the first lucky ones who obtained it for free!

    The pro version is indeed delivered with the sources. In general, it seems a packages only in the form of a dll is a no-go for a lot of users. But the pro version does not only comes with the sources, it also has more features:
    • In inspector editor which allows you to drag & and drop the nodes to organize the tree.
    • Break points debugging: you can setup break points directly in the inspector which would pause the editor on a given node status (is starting, has succeeded or has failed). Which is valuable to debug the BT.
    Indeed, the documentation is so far mainly only references and that's not the most appropriate form for a beginner to find its way through. I am planning to add some tutorials, which would be more beginner friendly. Though, the examples contained in the package are fully commented. Did you had a look at them?

    Thank you!

    I would really appreciate if you could drop a review on the Asset Store. It helps a lot to increase the visibility of the tool and that comes as great support.
     
    Last edited: Aug 8, 2016
    laurentlavigne likes this.
  25. Martin_H

    Martin_H

    Joined:
    Jul 11, 2015
    Posts:
    3,873
    Awesome! Good to hear it's fixed in the next version. I think I can wait till the update goes online on the assetstore.

    On an old iOS project with Cocos2D I used Objective-C in a way that was vaguely similar to your implementation (but not interpreting scripts at runtime, just hardcoded Objective-C and way less flexible and feature rich). The scripts also had a root that got an update tick each frame and the updates would propagate down the tree and hit nodes with delays, conditions, triggers and game specific stuff.
    So your solution being script based is actually a lot more familiar to me than visual nodes. I just have a hard time memorizing things like how exactly the fallback node works and I need to go back to the docs to check such things every now and then.

    I always play the example scenes first for new assets and was quite impressed how complex the behaviours could get. But I needed to read the documentation first to be able to really make sense of scripts. I just somehow forgot going back there after reading the docs I guess. I have no compelling reason why I hadn't looked more closely at the scripts in the demos. Might be because I generally have a hard time reading any kind of code and rather trial and error myself through things till they work.
    I didn't have much time yet, but after taking a quick look yesterday I think this might actually be all I need to figure out the rest :). I was already able to improve my own ai BT, thanks a lot!

    Done!
     
    laurentlavigne likes this.
  26. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,294
    I'm happy to announce that Panda BT 1.3.1 (Free and Pro) is now available on the Asset Store.

    Here is the release notes:

    Panda BT - 1.3.1

    Core
    • Optimisation: drastically decreased GC Alloc during BTs initialisation.
    Fixes
    • Fixed BT script field disappearing in the Inspector when BT script is invalid.
    • Fixed PandaBehaviour inspector not being updated when selecting BT script through the asset selector window.
    • Fixed null exception raised when adding PandaBehaviour component on prefabs.
    • Fixed Task.IsInspected being true while BT is not displayed in the Inspector.
    • Highlight unimplemented task in Inspector.
    • Fixed parsing error occurring when no EOL on last line.
    • Fixed BT scripts not being refreshed when reverted (Pro).
    Minor Changes
    • New icon for PandaBehaviour component and gizmo.
    • Added PandaBehaviour.xml for auto-completion documentation in IDE.
     
    Martin_H and laurentlavigne like this.
  27. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    2,033
    WOW!
    is super awesome and it works ;)
    Is a pretty major addition! I've tried editing a script but no autocomplete, how is it used with monodev and SE3?
     
  28. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,294
    @laurentlavigne
    Sorry for the deception. It is really a minor addition, that is not about having BT scripts auto-completed but it is simply about the documentation that pops up during auto-completion when editing C#.
    auto-complete-documentation.png
    (spotted a typo: wether instead of whether)
     
    Last edited: Aug 22, 2016
  29. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    2,033
    @ericbegue is me, is me, I didn't read the english properly.
    Autocomplete panda in se3 would be so badass, and completely cannibalize your pro version.
     
  30. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,294
    @laurentlavigne It would be a pro feature then :). Auto-completion in an external IDE would be awesome, but it would be a huge project by itself. It would requires one plugin per supported IDE. Also, editing a BT script externally is not that simple, it requires the IDE to know the context: on which gameObject the script is attached to, what MonoBehaviours are being used, what are the available task implementations, ... so it would requires a close synchronisation between Unity and the IDE. And that's just about auto-completion, it gets even more complicated when considering to add some debugging facilities...

    But I had that already pondered, that's why I've choose to implement the in-Inspector drag-and-drop Editor in the pro version.

    What is SE3 btw?
     
    Last edited: Aug 23, 2016
    laurentlavigne likes this.
  31. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    2,033
  32. Dechichi01

    Dechichi01

    Joined:
    Jun 5, 2016
    Posts:
    2
    Hey Eric! First of all thanks for this amazing asset, been using it for AI in my mobile shooter game...

    I actually have a question about the low level side of "gluing" two languages (in your case, your BT language and C#). I've been thinking about creating a asset that integrates functional languages with C# in Unity, so one could write gameplay logic in LISP (for instance) and a C# code could call these functions and variables (pretty much the same happens in PandaBT I believe).

    Could you point me to some material where I can learn more about that? Right now I don't have the technical knowledge and don't know where to begin...
     
    laurentlavigne likes this.
  33. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,294
    @Dechichi01
    If you are looking specifically for Lua, I've just googled for "Lua C# bindings". You might be interested to have a look at these libraries:
    - Moonsharp
    - Nlua
    - DynamicLua

    Specifically for Panda BT, the "gluing" is done by using reflection. This is a feature natively available in C# (see System.Reflection) that essentially allows you to query information about the entities (classes, methods, property, ...) of the current running program. The PandaBehaviour component simply looks for methods within all the component attached to a the GameObject and try to find the methods matching the name and the parameters of the tasks used in a BT script.

    If you are interested into implementing your own language, you might be interested to learn about parsing and about how compilers work.

    Hope that give some directions towards what you are looking for.
     
    Last edited: Sep 19, 2016
    Dechichi01 and laurentlavigne like this.
  34. tomtominc

    tomtominc

    Joined:
    Nov 7, 2013
    Posts:
    13
    Hi! Is it possible to add a BT script at runtime?

    Let's say I have an EnemyBlueprint.prefab. Then a database with reference to property values to setup the EnemyBlueprint.prefab

    I'll instantiate the enemy by name like:

    Code (CSharp):
    1.      
    2. public static bool Spawn ( string entityID , BlueprintType type, Vector2 point )
    3. {
    4.     IEntity entity = SpawnBlueprint ( type );
    5.  
    6.     ((IBlueprint)entity).SetData ( entityID );
    7.  
    8.     return Spawn ( entity , point );
    9.  
    10. }
    11.  
    Then in the enemy class I set up behaviour like so:

    Code (CSharp):
    1. public class AutonomousBehviour : EntityBehaviour , IBlueprint
    2. {
    3.     //----------- other properties ----------
    4.  
    5.     [Header ("Database")]
    6.     public AutonomousBlueprintDatabase autonomousDatabase;
    7.     public AIBehaviourDatabase aiBehaviourDatabase;
    8.  
    9.     // reference to the panda behaviour script attached to the game object
    10.     public PandaBehaviour pandaBehaviour;
    11.  
    12.     public void SetData ( string key )
    13.     {
    14.         AutonomousBlueprint blueprint = autonomousDatabase.Find ( key );
    15.  
    16.         //--------- other setup code ---------
    17.  
    18.         TextAsset behaviour = aiBehaviourDatabase.Find ( blueprint.AIBehaviourID ).Behaviour;
    19.  
    20.         pandaBehaviour.scripts = new TextAsset[1];
    21.         pandaBehaviour.scripts [0] = behaviour;
    22.      
    23.     }
    24. }

    My goal is to only have one prefab, the prefab will be set up with a string id - the BT script will then call functions on the "AutonomousBehaviour.cs" class since it will hold helpers and such. Is this possible?
     
    laurentlavigne likes this.
  35. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,294
    @tomtominc
    Yes, you can change the BT scripts at runtime. Just re-compile the BT after you've assigned the text assets:
    Code (CSharp):
    1.         pandaBehaviour.scripts = new TextAsset[1];
    2.         pandaBehaviour.scripts [0] = behaviour;
    3.         pandaBehaviour.Compile(); // re-compile the BT to apply the change.
    Alternatively, you can pass a BT script as string to the Compile function. You can use that if you happen to generate scripts at runtime.
     
    laurentlavigne likes this.
  36. GroundCombo

    GroundCombo

    Joined:
    Jan 23, 2015
    Posts:
    17
    Hi Eric, I've been considering Panda BT for our game and I like the simplicity of it. One question about scripting: I've written Lua wrappers for various game objects for other scripting purposes, and I'd like to add generic Lua condition and action nodes in addition to other custom nodes to avoid duplicating too much functionality. So something like this:
    Code (csharp):
    1.  
    2. sequence
    3.     LuaCondition("
    4. <insert code here>
    5. ")
    6.     SomeAction
    7.  
    Does the parser support any multi-line quoting mechanism already, and if not, how easily do you think it could be added or can you think of some other handy way of including long strings? I don't expect to have huge code blocks, but on occasion a few lines might be needed.
     
    laurentlavigne likes this.
  37. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,294
    Hi @GroundCombo. Great that you're considering Panda BT for your game. I hope it'll help.

    The parser does not currently support multi-line strings. But maybe you'd be satisfied with the following solution:
    Code (CSharp):
    1. using UnityEngine;
    2. using Panda;
    3.  
    4. public class BTLua : MonoBehaviour
    5. {
    6.     [Multiline]
    7.     public string[] luaScripts;
    8.  
    9.     [Task]
    10.     bool LuaCondition(int scriptIndex)
    11.     {
    12.         int luaResult = 0;
    13.         string script = luaScripts[scriptIndex];
    14.         // ...
    15.         // Run the lua script using your Lua binding framework and return the result.
    16.         // ...
    17.         return luaResult != 0;
    18.     }
    19. }
    20.  
    The idea is to have an array of strings that will be editable as multi-line texts in the Inspector, where you can store your Lua scripts. Then you can reference these Lua scripts by index from the BT scripts:
    Code (CSharp):
    1. sequence
    2.     LuaCondition(0)
    3.     SomeAction
    4.  
    It might even make the BT script a bit more readable, since it is not mixing Lua and Panda in the same text file.

    Hope that helps.
     
    Last edited: Sep 21, 2016
    laurentlavigne likes this.
  38. GroundCombo

    GroundCombo

    Joined:
    Jan 23, 2015
    Posts:
    17
    You're right, that is cleaner and I should be able to arrange things so that editing stays flexible enough. Thanks for the tip, I'm going to do some more research.
     
    laurentlavigne likes this.
  39. shallwaycn

    shallwaycn

    Joined:
    Apr 3, 2012
    Posts:
    198
    Hi Eric,
    I've bought the pro version, It's really convenient for our game.
    But just now I've encountered a strange problem that the nested Parallel node doesn't work properly as I expected.

    if I added a nested parallel node, the whole process seems to be messed up, ai character become dull and some parallel node doesn't even get invoked:(

    Currently I'm not sure if this is a panda behaviour's bug or not, but If yo can help to take a investigate on this issue, I would be very grateful.

    [solved]
    My code's mistake, not the panda behaviour's bug. Sorry to bothered:)
     
    Last edited: Sep 29, 2016
  40. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,294
    Hi @shallwaycn,

    Thank you for buying the pro version. It's great that it helps you guys to develop your game. I'd be glad to hear if you have any usage feedback.

    I see that you've already solved your problem. But I would like to warn that there are two situations that are not supported by Panda BT and will raise a runtime error:
    • Recursion: a sub-tree can not make a call to itself, directly or indirectly (via a child).
    • Parallel: the same sub-tree can not run in parallel.
    These restrictions are there in order to keep the visualization of the tree simple and straightforward. If you really need to run the same sub-tree in parallel, a work around consists to duplicate it. Note that these restrictions does not apply to tasks, so you can run many instances of same task in parallel or do recursion (on the C# side) without any problem.
     
    Last edited: Sep 29, 2016
  41. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    2,033
    I'm trying to get some sims like behavior in there and Utility AI seems really expressive (also that's what they use)
    First idea is to use sequence
    Code (CSharp):
    1. sequence
    2.    utility_eat
    3.    utility_cleanup
    4.    utility_socialize
    But sequence evaluate each task one by one so I cannot compare each utility value before choosing an action.
    Is there a way around this? Maybe a new structure key? (more work for you ;)
    http://www.gdcvault.com/play/1012410/Improving-AI-Decision-Modeling-Through
     
  42. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,294
    @laurentlavigne
    Instead of 'sequence', you could give a try to 'race'. This node runs all its children in parallel and succeeds on the first child that succeeds and it fails only when all children fail. So you could use the 'race' node to start all utility tasks on the same frame. Then each task,when it's starting, would have a way to evaluate itself (eventually by considering other utility tasks) and decide whether to keep running or fail immediately in order to let the elected task running.
    Thanks to the video to this presentation, it seems very interesting.
     
  43. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    2,033
    Something like this?

    Code (CSharp):
    1.     #region utilities
    2.  
    3.     // boilerplate
    4.     public List<float> responseValues = new List<float>();
    5.     [Task]
    6.     void ResetUtilities()
    7.     {
    8.         responseValues.Clear ();
    9.         Task.current.Succeed ();
    10.     }
    11.     bool UtilityOfHighestValue(float value)
    12.     {
    13.         foreach (var u in responseValues) {
    14.             if (u > value)
    15.                 return false;
    16.         }
    17.         return true;
    18.  
    19.     }
    20.     //specitics
    21.     public AnimationCurve responseEnergy;
    22.     [Task]
    23.     void Utility_Eat()
    24.     {
    25.         var task = Task.current;
    26.         if (task.isStarting)
    27.         {
    28.             task.item = responseEnergy.Evaluate (energy / initialEnergy);
    29.             responseValues.Add ((float)task.item);
    30.         }
    31.         else {
    32.             if (UtilityOfHighestValue ((float) task.item))
    33.                 task.Succeed ();
    34.             else
    35.                 task.Fail ();
    36.         }
    37.     }
    38.  
    39.     public AnimationCurve responseBoredom;
    40.     [Task]
    41.     void Utility_Socialize()
    42.     {
    43.         var task = Task.current;
    44.         if (task.isStarting)
    45.         {
    46.             task.item = responseBoredom.Evaluate (boredom / initialBoredom);
    47.             responseValues.Add ((float)task.item);
    48.         }
    49.         else {
    50.             if (UtilityOfHighestValue ((float) task.item))
    51.                 task.Succeed ();
    52.             else
    53.                 task.Fail ();
    54.         }
    55.     }
    56.     #endregion
    Code (Boo):
    1. tree("LiveLife")
    2.     race
    3.         ResetUtilities
    4.         sequence
    5.             Utility_Hungry
    6.             tree("Search For Food")
    7.         sequence
    8.             Utility_GiveBirth
    9.             tree("Give Birth")
    10.         sequence
    11.             Utility_SearchForPrey
    12.             tree("Search For Prey")
    13.         sequence
    14.             Utility_NeedRest
    15.             tree("Go Rest")
     
  44. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,294
    I see, I would suggest the following approach by organizing the tree this way:
    Code (CSharp):
    1. tree("LiveLife")
    2.     fallback
    3.         tree("Search For Food")
    4.         tree("Give Birth")
    5.         tree("Search For Prey")
    6.         tree("Go Rest")    
    7.  
    8. tree("Search For Food")
    9.     while WantToSearchFood
    10.         // do stuffs
    11.  
    12. tree("Give Birth")
    13.     while WantToGiveBirth
    14.         // do stuffs
    15.  
    16. tree("Search For Prey")
    17.     while WantToSearchPrey
    18.         // do stuffs
    19.  
    20. tree("Go Rest")    
    21.     while WantToRest
    22.         // do stuffs
    23.  
    Basically each tree is conditioned with a WanTo* task that indicates whether the creature is willing or have decided to perform the task. You could use a 'sequence' instead of a 'while' if you want the creature to keep performing the ongoing action even if it has change his mind.

    Each WantTo* task would be a boolean on the C# sharp side:
    Code (CSharp):
    1.     [Task]
    2.     bool WantToSearchForFood;
    3.  
    4.     [Task]
    5.     bool WantToHunt;
    6.  
    7.     [Task]
    8.     bool WantToReproduce;
    Which can be set to true/false to activate or desactivate the action. This evaluation could happen at any time, for instance in the Update function, or you could do it in from a task as well (like a "Think" task).
     
    Last edited: Oct 10, 2016
  45. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    2,033
    Oh that's so much cleaner, thanks!
    It's interesting how the decision making is transferred from a bunch of conditions to massaging response curves.

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using Panda;
    5.  
    6. public class Wants : MonoBehaviour {
    7.     public AnimationCurve EnergyToHunger
    8.     , EnergyToAggressivness
    9.     , DistanceToThreat
    10.     , EnergyToBreeding
    11.     , MateEnergyToDesire
    12.     , MateLevelToDesire //differential level
    13.     , DesireToMating
    14.     , EnergyToComfort
    15.     , ComfortToBreeding;
    16.  
    17.     public Dictionary<string, float> responseValues = new Dictionary<string, float>();
    18.  
    19.     Unit unit;
    20.     public float tick = .5f;
    21.     float tickRandomness=-1;
    22.     void Start()
    23.     {
    24.         unit = GetComponent<Unit> ();
    25.         if (tickRandomness == -1)
    26.             tickRandomness = Random.value*tick;
    27.     }
    28.  
    29.     float tickTimer;
    30.     string decision;
    31.     void Update()
    32.     {
    33.         if (Time.time > tickTimer) {
    34.             tickTimer = Time.time + tick + tickRandomness;
    35.             responseValues.Clear ();
    36.             //evaluate all Wants
    37.             responseValues.Add("SearchForFood", EnergyToHunger.Evaluate(unit.energy/unit.initialEnergy));
    38.             responseValues.Add("SearchForPrey", EnergyToAggressivness.Evaluate (unit.energy/unit.initialEnergy));
    39.  
    40.             //decision
    41.             float decisionValue=-1;
    42.             decision = "";
    43.             foreach(KeyValuePair<string, float> kvp in responseValues)
    44.             {
    45.                 if (kvp.Value > decisionValue) {
    46.                     decisionValue = kvp.Value;
    47.                     decision = kvp.Key;
    48.                 }
    49.             }
    50.         }
    51.     }
    52.  
    53.     #region interface with Panda
    54.     [Task]
    55.     bool WantTo(string name)
    56.     {
    57.         return decision == name;
    58.     }
    59.     #endregion
    60. }
    61.  
    in the BT script
    Code (CSharp):
    1. while WantTo "SearchForPrey"
     
    Last edited: Oct 9, 2016
    ZJP likes this.
  46. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,294
    @laurentlavigne
    That's really neat! The WantTo task is generic and you can customize how each creature would make its decision based on these response curves.
     
    Last edited: Oct 13, 2016
  47. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    2,033
    And these curves are sensitive, the results get unpredictable real fast :D
     
  48. ZJP

    ZJP

    Joined:
    Jan 22, 2010
    Posts:
    2,646
    Well done. :cool:
     
  49. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    2,033
    @ericbegue shifting gear, I'm implementing Harvest Moon type interaction in the game. It can interact with NPC, Machines, Objects etc... and the interaction is different for each type. I'd like to use Panda for the dialogue system and various interactions. Is there a way in c# to call a tree?
     
  50. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,294
    @laurentlavigne There's currently no direct mechanism to arbitrary call a tree in C# . But what you want to do could be realized with the following workaround.

    First, you would need to define a task that tests whether a string variable (that could be set anywhere on the C# side) equals to a specified value:
    Code (CSharp):
    1. using UnityEngine;
    2. using Panda;
    3.  
    4. public class BTActivator : MonoBehaviour
    5. {
    6.     public string flag = "";
    7.  
    8.     [Task]
    9.     bool IsFlag(string strFlag)
    10.     {
    11.         return flag == strFlag;
    12.     }
    13. }
    14.  
    Then your BT would look like something like this:
    Code (CSharp):
    1. tree("Root")
    2.     fallback
    3.         tree("TreeA")
    4.         tree("TreeB")
    5.         tree("TreeC")
    6.  
    7. tree("TreeA")
    8.     while IsFlag("a")
    9.         sequence
    10.             DebugLog("A running...")
    11.             Wait(1.0)
    12.  
    13. tree("TreeB")
    14.     while IsFlag("b")
    15.         sequence
    16.             DebugLog("B running...")
    17.             Wait(1.0)
    18.  
    19. tree("TreeC")
    20.     while IsFlag("c")
    21.         sequence
    22.             DebugLog("C running...")
    23.             Wait(1.0)
    24.  
    So, you can select which tree to run by setting the value of BTActivator.flag to the appropriate value.
     
    Last edited: Nov 9, 2016
    ZJP and laurentlavigne like this.