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. danielboldroff

    danielboldroff

    Joined:
    Sep 27, 2021
    Posts:
    7
    I was hoping I could figure this problem out on my own but alas, I am unable.

    My issue is with the 'random node'. For reasons unbeknownst to me when in play mode, the random node just rapidly cycles through the selection leaves and never stops. I've tried to structure the tree everyway I could think of an yet the problem persists.

    To make the matter stranger, this also happens with the WaitRandom node. it just rapidly cycles but never stops to allow the following leaf to run.

    right now my tree looks like this:

    tree("Orbit")
    fallback
    random
    tree("OrbitCW")
    tree("OrbitCCW")


    A simple tree I know. But I'm losing sleep over it now. What would be some reasons to cause the never ending rapid cycling of the random node?
     
  2. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    @danielboldroff

    Is the "Orbit" tree above complete or is it cropped? The fallback node is not that useful if there is only one child node.

    I guess you want some object to rotate either clockwise or counter clockwise randomly. Here the random should give 50% chance to run either OrbitCW" or "OrbitCCW". Is this what you want to achieve? Maybe a node inside those trees are failing causing the whole BT to repeat itself quickly.
     
    Last edited: Oct 31, 2021
  3. danielboldroff

    danielboldroff

    Joined:
    Sep 27, 2021
    Posts:
    7
    @ericbegue

    Yes, exactly. I am trying to get an object to randomly rotate either clockwise or counter clockwise then have the tree wait a random duration before recycling. I've tried the waitrandom node sequenced in parallel with random node as well. yet the random functions, including waitrandom, all seem to just rapidly cycle and never allow the subsequent node to proceed. Could this be a problem with my code instead? Does the random selector require code to have fail functions or and other like code bits?

    areciated
     
  4. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    @danielboldroff

    Could you post the "OrbitCW" and "OrbitCCW" trees as well as the task implementing the rotation so we can have look?

    Edit:
    I've run a small test with a script doing what you want to do (with placeholders instead of a task that implements the rotation):


    Code (CSharp):
    1. tree "Root"
    2.     sequence
    3.         random
    4.             tree "OrbitCW"
    5.             tree "OrbitCCW"
    6.         WaitRandom(0.5, 2.0)
    7.  
    8.  
    9. tree "OrbitCW"
    10.     DebugLog "OrbitCW"
    11.  
    12. tree "OrbitCCW"
    13.     DebugLog "OrbitCCW"
    14.  
    15.  
     
    Last edited: Oct 31, 2021
  5. danielboldroff

    danielboldroff

    Joined:
    Sep 27, 2021
    Posts:
    7
    @ericbegue

    I appreciate that. The tree is performing as intended. But for some reason my obit scripts don't run. Strangely they run perfectly if I use them in a single child tree:

    Code (CSharp):
    1. tree "Root"
    2.            OrbitCW
    for some reason the random node interferes with their run time. My C# knowledge is novice at best

    Code (CSharp):
    1.     [Task]
    2.     public void OrbitCW()
    3.     {
    4.         // minDistance refs a how close the object can be the target
    5.         if (moveTowards.magnitude < minDistance + 1)
    6.          
    7.             transform.RotateAround(target.transform.position, Vector3.up, orbitSpeed * Time.deltaTime);
    8.             Task.current.Succeed();
    9.  
    10.         //moveTowards refs a distance calc
    11.         if (moveTowards.magnitude > minDistance)
    12.             Task.current.Fail();
    13.     }
    I've rewritten this everyway I know how, so I am assuming something is broken and I just can't see it.
     
  6. danielboldroff

    danielboldroff

    Joined:
    Sep 27, 2021
    Posts:
    7
    I wanted to add I tried the Random node with a basic move forward and backward tasks and they also do not run when called. I can get them to run without problems otherwise
     
  7. ratking

    ratking

    Joined:
    Feb 24, 2010
    Posts:
    348
    I think your code has a bug and is missing a block after the first if(). Currently Task.current.Succeed() is called all the time, even though you indented it like it should be called only if moveTowards' magnitude is smaller than minDistance+1.
     
    ericbegue likes this.
  8. danielboldroff

    danielboldroff

    Joined:
    Sep 27, 2021
    Posts:
    7
    @ericbegue

    So I've now tried this we other various movement scripts and the issue still occurs with any random node children. anything I plug in there suffers the same issue. At this point, I'm just testing at random but have not had any luck at finding the source. Would you be willing to make a simple script that allows an object to move forward and backward along with a tree used to control the direction at random?

    Appreciated
     
  9. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    5,996
    BT is not recommended for such simple behaviors, as you all see, BT is VERY hard to debug and even understand.
    For single actions FSM is the way to go, it's easy to understand and easier to debug.
    The ideal system is hierarchical: GOAP for decision making and FSM for actions.
     
  10. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    @danielboldroff
    It seems you are trying to learn programming in C# and Behaviour Tree at the same time. I suggest to take it slow and learn one thing at a time at bite size. Any advanced technique is useless if you don't master the basics (you have to learn walking before running).

    It is not efficient to write and rewrite a program at random until it runs the way you want. You need to know the language first before trying to solve any problem using it. You need to understand what each line does exactly. I suggest learning to write simple programs in C# first to get acquainted with the language.

    Normally you should not expect people to write code for you, but here are a MonoBehaviour and a BT script implementing what you want. Please ask if something is not clear to you:

    Code (CSharp):
    1. using UnityEngine;
    2. using Panda;
    3. public class Movement : MonoBehaviour
    4. {
    5.     public float speed = 1.0f;
    6.  
    7.     [Task]
    8.     void Move(float distance)
    9.     {
    10.         Vector3 destination;
    11.  
    12.         if( Task.current.isStarting)
    13.         {
    14.             // When the task starts, compute and store the destination
    15.             destination = transform.position + Vector3.right * distance;
    16.             Task.current.item = destination;
    17.         }
    18.         else
    19.         {
    20.             // Retrieve the destination
    21.             destination = (Vector3) Task.current.item;
    22.         }
    23.  
    24.         // Move toward the destination at given speed
    25.         var direction = (destination - transform.position).normalized;
    26.         transform.Translate(direction * Time.deltaTime*speed);
    27.  
    28.  
    29.         // Check if we have reached/overshooted the destination.
    30.         // if that the case, the task succeeds
    31.         if ( Vector3.Dot( (destination - transform.position), direction) < 0.0)
    32.         {
    33.             transform.position = destination;
    34.             Task.current.Succeed();
    35.         }
    36.  
    37.     }
    38. }
    39.  
    Code (CSharp):
    1. tree("Root")
    2.     sequence
    3.         random
    4.             Move(-1.0) // Move Left
    5.             Move(1.0) // Move Right
    6.         Wait(1.0)
    7.  
     
    Last edited: Nov 4, 2021
  11. mike6502

    mike6502

    Joined:
    Oct 5, 2016
    Posts:
    42
    Hey Eric, just wanted to pop in and say thanks for Panda BT Pro. It's a critical path component for me, not just for AI, but also as a generic way to manage sequential async tasks. I use it everywhere. And being able to diff changes in clear text is a lifesaver.

    Hope you're able to continue supporting this gem of an asset.

    -Mike
     
    ratking likes this.
  12. danielboldroff

    danielboldroff

    Joined:
    Sep 27, 2021
    Posts:
    7
    @ericbegue
    I am indeed still new to coding and I do appreciate you humoring my lack of understanding. Thanks to your generosity, I was able to overcome the error that I was making. I really needed to see what this basic script and tree looked like and compare it to what I was doing. I was getting frustrated because I was getting my other movement scripts to work with panda but not this super simple script. Again, thanks for adding to my knowledge and helping me along the journey.
     
  13. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    @mike6502
    I'm glad to hear this package is useful to you. Don't worry, even though I was not very active the last times, I've recently dedicated some hobby time to improve Panda BT. So you can expect a new release soon ;). Also there are some great ideas previously mentioned here that I would like to implement. BTW thank you guys, particularly to @laurentlavigne, for having kept this forum alive.

    @danielboldroff
    You're welcome. We all were beginners at some point, so we know the struggle you're experiencing. Just keep up the good work and programming will become a second nature, if that's what you want.
     
    Last edited: Dec 25, 2021
  14. ratking

    ratking

    Joined:
    Feb 24, 2010
    Posts:
    348
    This might not be what a BT is for, but is it possible to suppress the Running result? Specifically, I wondered, if it's possible to have two (or more!) Wait() commands in parallel, but nothing I tried works.
    Code (CSharp):
    1. tree("Root")
    2.     parallel
    3.         sequence
    4.             Wait(0.3333)
    5.             DebugLog("A")
    6.         sequence
    7.             Wait(1.0)
    8.             DebugLog("B")
    The above, naturally, prints A B A B A B..., but I'm searching for a combination of nodes that would print A A A B A A A B...
     
  15. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    @ratking
    I see, you want to run the logs in parallel at different frequency by repeating them as soon as they are finished.

    For that, you just need to use the `repeat` node:

    Code (CSharp):
    1. tree("Root")
    2.     parallel
    3.         repeat tree("LogA")
    4.         repeat tree("LogB")
    5.  
    6. tree("LogA")
    7.     sequence
    8.         Wait(0.3333)
    9.         DebugLog("A")
    10.  
    11. tree("LogB")
    12.     sequence
    13.         Wait(1.0)
    14.         DebugLog("B")
    15.  
     
  16. ratking

    ratking

    Joined:
    Feb 24, 2010
    Posts:
    348
    Works perfectly, thanks!
     
  17. Zinev

    Zinev

    Joined:
    Feb 8, 2017
    Posts:
    14
    O, it's nice. )) Thank you.
    I know one bug, by the way:) Debugging in the Pro version doesn't work for me.
     
  18. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    @Zinev
    Thank you for reporting this. It will be fixed in the next release.
     
  19. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    @Zinev
    There is a bug occurring when setting breakpoints on BT script attached to prefabs: the breakpoints get cleared when in play mode. That's fixed (for the next release), but do you have the same bug or is that something else?
     
  20. ratking

    ratking

    Joined:
    Feb 24, 2010
    Posts:
    348
    Another bug: some settings in the inspector (like "Repeat Root") always get reset when actually starting the game.

    By the way, I currently have an idea to use PandaBT a bit differently, and I would like to be able to stop a currently running behaviour tree and replace it with another one during runtime - repeatedly. How would I go about this, or is that actually discouraged?
    [edit] Alternatively I wonder if one could have more than one "Root" tree, changing it during runtime, without having to resort to a big tree with a fallback node and several sub trees.
     
    Last edited: Nov 15, 2021
  21. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    @ratking
    Thanks for the bug report, I'm doing an overhaul of how PandaBehaviour works with prefabs. That one will also be fixed in the next release.

    About your idea. That's possible. For that, you can implement a task that will run a tree given its name. With PandaBehaviour.GetTree(), you can select a tree at run time by its name:
    Code (CSharp):
    1. using UnityEngine;
    2. using Panda;
    3.  
    4. public class TreeSelection : MonoBehaviour
    5. {
    6.  
    7.     // Name of the tree to run by RunSelectedTree
    8.     public string treeName;
    9.  
    10.     PandaBehaviour bt;
    11.  
    12.     void Start()
    13.     {
    14.         bt = GetComponent<PandaBehaviour>();
    15.     }
    16.  
    17.     [Task]
    18.     void RunSelectedTree()
    19.     {
    20.         var tree = bt.GetTree(treeName);
    21.         if (tree != null)
    22.         {
    23.             if( Task.current.isStarting)
    24.             {
    25.                 tree.Reset();
    26.             }
    27.  
    28.             tree.Tick();
    29.             if( tree.status != Status.Running)
    30.             {
    31.                 Task.current.Complete(tree.status == Status.Succeeded);
    32.             }
    33.  
    34.             Task.current.debugInfo = treeName;
    35.         }
    36.         else
    37.         {
    38.             Task.current.Fail();
    39.         }
    40.     }
    41. }
    42.  
    Then you can use RunSelectedTree from the root tree:

    Code (CSharp):
    1. tree "Root"
    2.     RunSelectedTree
    3.  
    4. tree "a"
    5.     sequence
    6.         DebugLog "A"
    7.         Wait 1.0
    8.  
    9. tree "b"
    10.     sequence
    11.         DebugLog "B"
    12.         Wait 1.0
    13.  
    14. tree "c"
    15.     sequence
    16.         DebugLog "C"
    17.         Wait 1.0
    18.  
     
    mike6502 and ratking like this.
  22. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    Hi,

    Xmas is coming soon and it seems you guys had good behaviours this year. So... Ho Ho Ho!

    I'm happy to announce that Panda BT 1.4.4 is now available on the Asset Store.

    Another gift to unpack: the website has also received an update (mainly to improve navigating the documentation and make it more mobile-friendly).

    Here is the change log:

    Core:
    • Added ThisTask as an alias for Task.current.
    Package
    • Reorganized project to fit Unity's package system.
    • Add Assembly Definitions files for faster re-compilation and better modularity.
    Examples
    • Update/Change graphics
    Fixes
    • Fixed float parsing error occuring when local culture using other character than "." as decimal separator.
    • Fixed BT scripts mark as modified (*) when another script is applied or reverted.
    • Fixed various in-Inspector code rendering issues.
    • Fixed breakpoints cleared on play for prefabs.
    • Fixed PandaTree (returned by PandaBehaviour.GetTree(string)) not reset.
    • Fixed PandaBehaviour component errors when undo count value .
    • Fixed continuous assets refreshing while inspecting a prefab from the Assets folder.
     
    Last edited: Dec 25, 2021
  23. steven1022

    steven1022

    Joined:
    Sep 8, 2012
    Posts:
    21
    I am using this example but the RunSelectedTree task only runs once. Shouldn't the a b or c trees complete, which completes the RunSelectedTree and resets the tree to the beginning to run again and select a new tree dynamically?

    It just seems to hang on the first completion.

    I am adding a forced reset after the following line which fixes it - but it seems like I shouldn't need to force the reset. Just want to make sure I am understanding how it should work before using it this way.

    Task.current.Complete(tree.status == Status.Succeeded);
    bt.Reset(); //adding this line

    Repeat root is ticked btw.

    Thanks
     
    Last edited: Apr 21, 2022
  24. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    Which Panda BT version are you using? The last published version is 1.4.4. There was a bug in the previous versions: the tree returned by PandaBehaviour.GetTree was not resetting. That's why the example have the following:

    Code (CSharp):
    1.             if( Task.current.isStarting)
    2.             {
    3.                 tree.Reset();
    4.             }
    This was required to reset the selected tree when the `RunSelectedTree` was restarted. But after the fix in the in the last version, this code is no more required.

    If you want to not interrupt the current selected tree and wait until it completes before switching to a new tree, you can store the selected tree into `Task.current.data` when the task is starting (this way the tree name is ignored if it is changed while the selected tree is running).

    Here is the updated code:
    Code (CSharp):
    1. using UnityEngine;
    2. using Panda;
    3.  
    4. public class TreeSelection : MonoBehaviour
    5. {
    6.     // Name of the tree to run by RunSelectedTree
    7.     public string treeName;
    8.  
    9.     PandaBehaviour bt;
    10.  
    11.     void Start()
    12.     {
    13.         bt = GetComponent<PandaBehaviour>();
    14.     }
    15.  
    16.     [Task]
    17.     void RunSelectedTree()
    18.     {
    19.         if( Task.current.isStarting )
    20.             Task.current.data = bt.GetTree(treeName);
    21.  
    22.         var tree = Task.current.data as PandaTree;
    23.  
    24.         if (tree != null)
    25.         {
    26.             tree.Tick();
    27.             if (tree.status != Status.Running)
    28.                 Task.current.Complete(tree.status == Status.Succeeded);
    29.  
    30.             Task.current.debugInfo = treeName;
    31.         }
    32.         else
    33.         {
    34.             Task.current.Fail();
    35.         }
    36.     }
    37. }
     
  25. steven1022

    steven1022

    Joined:
    Sep 8, 2012
    Posts:
    21
    I want the dynamic tree to complete and then the task (RunSelectedTree) to complete and move forward on the root tree because its current task has completed.

    In the current version (1.4.4 Free) when using the code below, the tree being called with GetTree completes but the code never moves past the task (RunTaskTree) and therefore the root tree never resets and continues the AI.

    In both my example and your new example the RunTaskTree/RunSelectedTree task runs forever regardless of the status of the tree called dynamically - success or fail the task running the tree never completes to reset the root. It only moves forward when I manually call a reset on root.

    I am looking to understand why the Root tree hangs after the Dynamic tree succeeds in the RunSelectedTask task. Is the hang intended logically?

    Code (CSharp):
    1.  
    2.     [Task]
    3.     public void RunTaskTree()
    4.     {
    5.         var tree = panda.GetTree(jobTree);
    6.         if (tree != null)
    7.         {
    8.  
    9.             tree.Tick();
    10.  
    11.             if (tree.status != Status.Running)
    12.             {
    13.                 Task.current.Succeed();
    14.                 ThisTask.Succeed();
    15.             }
    16.  
    17.             Task.current.debugInfo = jobTree;
    18.         }
    19.         else
    20.         {
    21.             Task.current.Fail();
    22.         }
    23.     }
     
  26. steven1022

    steven1022

    Joined:
    Sep 8, 2012
    Posts:
    21
    Any feedback?
     
  27. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    @steven1022
    This does not happen on my side. The RunTaskFree completes and the root is repeated as expected. I'm not sure why the tree execution get stuck on RunTaskTree in your project. What you are describing is not expected and should be considered a bug. Could you PM me a small Unity project reproducing the bug and I'd investigate further what's going wrong.
     
  28. viatech

    viatech

    Joined:
    Dec 6, 2017
    Posts:
    55
    Just been looking at BTs and came across Panda. Tried the free version and noticed there is a menu option "Window/PandaBehaviour" which opens an editor window which doesn't do anything - should it?

    Also there are no screenshots or videos of the drag and drop editing available in the pro-version. Anyone used that editor and would like to share a few screenshots and comment?

    I don't mind the text based scripts (quite like them actually) but what does seem to be missing is a way for a designer to find what low level "Tasks" have been written. Does the pro-editor do that?

    Has anyone tried to implement a GUI based editor for Panda using UIToolkit?

    Thanks in advance to anyone who chimes in :)
     
  29. chuclum

    chuclum

    Joined:
    May 6, 2021
    Posts:
    1
    I'm trying to add a BT component to an enemy in runtime. The behaviour works as expected, but when I try to open the component in the inspector while I'm playing I get the following error:

    NullReferenceException: Object reference not set to an instance of an object
    Panda.BehaviourTreeEditor.getInspectorParams () (at <54fc07c933d74ed1b04be59698821741>:0)
    Panda.BehaviourTreeEditor.OnInspectorGUI () (at <54fc07c933d74ed1b04be59698821741>:0)
    UnityEditor.UIElements.InspectorElement+<>c__DisplayClass59_0.<CreateIMGUIInspectorFromEditor>b__0 () (at <da82069731e64928932a97bb7b1b5945>:0)
    UnityEngine.GUIUtility:ProcessEvent(Int32, IntPtr, Boolean&)


    This is what I do to create it:

    Code (CSharp):
    1. public void createBT(GameObject character, string bt_id)
    2.         {
    3.             try
    4.             {
    5.                 PandaBehaviour component = character.AddComponent<PandaBehaviour>();
    6.                 component.Compile(BT_dic[bt_id]);
    7.             }
    8.             catch (Exception e)
    9.             {
    10.                 Debug.Log("EXCEPTION: " + e);
    11.             }
    12.         }
    The tree format is correct as I've tried adding it before running and I can see it working in the inspector.
    Does anyone have any idea? Thanks in advance
     
  30. glatteinfo

    glatteinfo

    Joined:
    Nov 17, 2019
    Posts:
    19
    hello! Sometimes i need to call [task] functions not through panda but just within my script. I'm then getting the error Exception: `Panda.Task.current` or `ThisTask` can only be accessed from a method implementing a task (having the [Task] attribute).

    Besides the error message, nothing else seems to happen but my OCD can hardly take the red error messages :-/ Is this intended/normal? and is there a way around it?
     
  31. ratking

    ratking

    Joined:
    Feb 24, 2010
    Posts:
    348
    Can you show some relevant code? Are you calling anything from ThisTask inside that method? Be aware that you can use normal methods returning a bool as [Task]s too.
     
  32. glatteinfo

    glatteinfo

    Joined:
    Nov 17, 2019
    Posts:
    19
    Its really nothing to mention almost. A simple [Task] routine that spits the error at me if i call the function in update with ConfirmBeeingInPosition(); within an if-statement:

    Code (CSharp):
    1. [Task]
    2.     public void ConfirmBeeingInPosition()
    3.     {
    4.         //Debug.Log(ThisBot.name + "Confirming that I am still in position for work");
    5.  
    6.         switch (RootScript.CurrentAssignment)
    7.         {
    8.             case "WoodChuck":
    9.                 if (Vector3.Distance(SelectedTreeToChopWorkPoint.transform.position, ThisBot.transform.position) > 0.5f && ArrivedAtDestination == true)
    10.                 {
    11.                     BotDestination = SelectedTreeToChopWorkPoint.transform.position;
    12.                     Animation = AnimationState.stop;
    13.                     ArrivedAtDestination = false;
    14.                     ExecuteArrivalCheck = false;
    15.  
    16.                     InRange = false;
    17.  
    18.                     Debug.Log(ThisBot.name + "was not in position. Walking to work point initiated.");
    19.  
    20.                 }
    21.                 //if needed also remove this script from array "ActiveWorkersControlScripts" out of TreeScript
    22.                 break;
    23.         }
    24.         ThisTask.Fail();
    25.     }
    Its like I am just not supposed to call a function with ThisTask.something outside of the BT. But that shouldn't be the case, should it?
     
  33. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    This is a testing/debugging window that accidentally made his way to the release. It does nothing, so you can ignore it. It will be removed in the next release.

    The documentation about the In-Inspector Editor is available here. It lists the essential features of the Editor available in the pro edition.

    When inserting a new node using the In-Inspector editor, all the available tasks are listed into a drop-down menu. This can be a way to explore which tasks are implemented on all the MonoBehaviours attached to the current GameObject.

    UIToolkit is a promising UI system and should be more performant than IMGUI. I like the idea to re-implement the editor using UIToolkit but that would require to develop the editor from scratch. This is not on the roadmap for now.
     
    Last edited: Jun 11, 2022
  34. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    This error is expected. `Task.current` is only meaningful when the [Task] method is called by the PandaBehaviour component. The [Task] method are not intended to be called directly from any other component. Behind the curtain, the [Task] method is bind to a task object by the PandaBehaviour component before the method is called. This binding does not occurs when you call the method directly, that's why `Task.current` is out of context in that case.

    If you want to reuse code, you can first write your methods independently from the Panda BT framework, that is without using [Task] or Task.current. Then once, you've built yourself a standalone methods collection, you can call these methods from [Task] methods. This way you code can be used both from a Panda Script or any other MonoBehaviour.
     
    laurentlavigne likes this.
  35. glatteinfo

    glatteinfo

    Joined:
    Nov 17, 2019
    Posts:
    19
    thanks a lot for the clarification and the suggested workaround! that helped a lot =) As an alternative workaround, I just wrapped the Task.something commands in a conditional bool now in the functions I intended to call directly and it works fine, too. thanks again!
     
    ericbegue likes this.
  36. MushyAvocado

    MushyAvocado

    Joined:
    Oct 16, 2021
    Posts:
    20
    Hello, I've started using your BT asset and have found it quite useful, but there is one thing I am stumped on how to do.

    I have 3 different components: Creature, CreatureHearing, and CreatureSight. They each have different tasks. I want to have 3 different behavior trees to control each.

    I want the Creature component and Creature.BT to work on its own, with the sight and hearing to be added on to that. I tried making an isolated CreatureHearing.BT and CreatureSight.BT, but it said that the tree "root" was already defined (in Creature.BT). I've tried defining a CreatureHearing and CreatureSight tree in CreatureHearing.BT and CreatureSight.BT and calling it in Creature.BT, but it didn't work if those BTs were not attached the the PandaBehaviour component.

    So my question is, how can I add a BT that works with Creature.BT and can be removed without errors, along with the corresponding component?
     
    laurentlavigne likes this.
  37. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    Hi @MushyAvocado,

    Great that you find this asset useful.

    Instead of adding different `PandaBehaviour` components for each part of the creature, you can run the logic for each part in different trees. You can use the parallel node to run all the parts at the same time.
    Code (CSharp):
    1. tree "Root"
    2.     parallel
    3.         tree "Creature"
    4.         tree "Sight"
    5.         tree "Hearing"
    6.    
     
    Last edited: Jul 12, 2022
  38. MushyAvocado

    MushyAvocado

    Joined:
    Oct 16, 2021
    Posts:
    20
    This works, but if the creature has no CreatureSight component (if it's blind), then the tree will not work because it references tasks that are defined in CreatureSight are no longer there, and same with CreatureHearing (if it's deaf) I could always make it so the Creature requires both sight and hearing components, and then change the values in the inspector so it cannot hear or see (i.e set the range it can see to zero), but this seems inefficient.

    Maybe I am going about this the wrong way, but I don't see an efficient solution.
     
    Last edited: Jul 12, 2022
  39. vx4

    vx4

    Joined:
    Dec 11, 2012
    Posts:
    179
    hello,
    Does Panda BT support GOAP?
     
  40. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    @MushyAvocado
    You could also try to implement the "CreatureSight" and "CreatureHearing" on child GameObjects, with there own PandaBehaviour script component and own components where the tasks are defined. This way they will be completely independent.
     
  41. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    Hi @vx4,
    I've developed an experimental GOAP module for Panda BT, but it's still in an early stage and not ready for release. This project is on hold for now. I am currently focusing on preparing the release of Panda BT 2.0.0, which as the version number indicates, contains major core changes. If you are interested to try out the experimental GOAP module, we can discuss this in PM.

    Since a new major version update has been just announced, I take the occasion to give you guys a is sneak peek into the upcoming features:
    - Support for variables of any type as task parameters (which makes blackboard implementations possible and easy)
    - PandaBehaviour can be bound to specified GameObjects (shared task definitions)
    - New structural nodes: if, ifelse, break, bail and retry
    - Support for async/await method as task
    - Panda scripts are no more .txt file but own .pbt files, which are parsed during assets import, which optimizes the runtime initialization.
    - ...
     
    vx4 and mike6502 like this.
  42. mike6502

    mike6502

    Joined:
    Oct 5, 2016
    Posts:
    42
    Will the .pbt files still be a clear text format? For me, one of the best features of PandaBT is that I can easily diff changes.

    While I'm here, I'll add a low-priority feature request: I'd love to have a way to encrypt or obfuscate pbt files, to reduce the chance of users tampering with critical gameplay. I know I can do this now via manually loading a deobfuscated BT at runtime, but it would be nice to have a "hook" or something to make this easier.
     
  43. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    The .pbt files are still text files. So you just edit them and see the diff changes as usual.

    Obfuscation is not supported. However In Panda BT 2 the .pbt files are parsed during assets import and the result is stored by Unity. I guess you could modify the import script to strip the source code and keep only the compilation result, which is less human readable. We can discuss this in PM if you want more hints about making those changes in your project.
     
  44. ThisIsNik

    ThisIsNik

    Joined:
    Oct 28, 2019
    Posts:
    9
    Hi @ericbegue. First off, Panda BT is a joy to use. The API is excellently designed and easy to understand, so thank you! I've been trying out PandaBT for a few days and I want to understand your design decisions and/or if I'm missing or misunderstood anything.

    Why did you choose to implement a "non-reactive BT"? As in, ticks aren't sent to children to the left of a running child (see this book co-authored by Petter Ogren, page 10 below).

    Is there a way to emulate a "reactive BT" such as the example below with Panda? Do you recommend another pattern? Or am I missing something completely obvious?

    upload_2022-8-3_11-33-44.png
     
    Last edited: Aug 3, 2022
  45. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    Hi @FortuneSyn, thank for your interest in Panda BT and I am glad that you like using it.

    Thank you for linking this book. It contains a lot of relevant information about BT. I found the comparisons with other AI techniques really valuable.

    Looking back at the time I've wrote the first lines of the engine, I was not aware of the concept of memory-less BT nor reactive BT. Since I had a background in Game Dev, a BT engine with memory was more appropriate. So it was just the natural way for me to implement the engine with control nodes having memory. It was not really a decision. A purely reactive BT seems to be more suitable in Robotics where the environment is uncertain and very dynamic. For instance, a robot might lose the grip on an object and has to react accordingly. However, a video game is a controlled environment which, most of the time, has to be somewhat predictable for the player to have fun.

    It's perfectly possible to implement reactivity in Panda BT.


    Below are two ways to implement reactivity using Panda BT:
    • Using only non-running tasks
    • Using the while-node

    Reactivity with non-running tasks

    A pure memory-less BT can be implemented by using exclusively non-running tasks or task implementations that complete (in success or failure) on the first tick and never return "running", for instance by implementing tasks with methods returning a bool. This will make the whole tree to complete on each tick, and to fully restart on the next tick. That will make all the conditions to be re-evaluated and the appropriate actions to be executed. In other words, the state of the tree is not dependent on the state of the tree from the previous tick, that is the tree is memory-less or reactive. Again, the BT will be purely reactive if all the tasks never return the "running" state.

    If all the tasks complete on the first tick, the transcription of Fig.1.6 into a BT script will be very straightforward:
    Code (csharp):
    1. tree "Root"
    2.     fallback
    3.         sequence
    4.             fallback
    5.                 IsBallFound
    6.                 FindBall
    7.             fallback
    8.                 IsBallClose
    9.                 ApproachBall
    10.             fallback
    11.                 IsBallGrasped
    12.                 GraspBall
    13.             fallback
    14.                 IsBinClose
    15.                 ApproachBin
    16.             fallback
    17.                 IsBallPlaced
    18.                 PlaceBall
    19.         AskForHelp
    20.  
    This BT looks simple. But I think a lot of logic (and memory) goes into the implementation of the tasks. You will need to keep track of the distance to the ball inside "FindBall". The "GraspBall" task would need to be aware of the claw position (how closed or open it is)... etc. So if the this BT is memory-less, more complexity (and memory) will go into the task implementations.


    Reactivity with the while-node

    Panda BT does not have only memory nodes. Reactivity can be implemented using the while-node. Using this node it is possible to run a branch of a tree under a given condition. If, at any time, the condition is no more satisfied, the whole branch will be interrupted and the while-node will report a failure to its parent which will decide of the proper action to take next.

    Now, if you want to implement the tasks and make them complete with some inherent meaning. For instance, "MoveToBin" will complete in success only when the bin is reached, but while the robot is approaching the bin, the task return running. That means, a task will last more than one tick and a task can take some time or frames to complete, which is typical for a video game. You will need to restructure the BT and that's where the while-node can be used to make the BT reactive. Basically, you would need to specify the preconditions for each action. For instance to grasp the ball, you need the ball to be found and the ball to be within reach before the robot closes its claws around the ball. You can write the "TryGraspBall" action as a tree and list the preconditions in the first child of the while-node:
    Code (csharp):
    1. tree "TryGraspBall"
    2.     while
    3.         sequence
    4.             IsBallFound
    5.             IsBallClose
    6.         fallback
    7.             IsBallGrasped
    8.             GraspBall
    9.  
    This tree is reactive. If the ball has been found and it's within the robot reach, the robot will start to close its claws to grasp the ball. However if a human quickly take the ball away such that the robot lose sight of it, the IsBallFound task will fail and the whole "TryGraspBall" tree will fail, then another action will need to be taken accordingly (searching for the ball in that case).

    The BT tree can be rewritten as follow if the tasks take more than one tick to complete:
    Code (csharp):
    1. tree "Root"
    2.     fallback
    3.         tree "TryPlaceBall"
    4.         tree "TryApproachBin"
    5.         tree "TryGraspBall"
    6.         tree "TryApproachBall"
    7.         tree "TryFoundBall"
    8.         AskForHelp
    9.  
    10. tree "TryPlaceBall"
    11.     while
    12.         sequence
    13.             IsBallGrasped
    14.             IsBinClose
    15.         fallback
    16.             IsBallPlaced
    17.             PlaceBall
    18.  
    19. tree "TryApproachBin"
    20.     while
    21.         IsBallGrasped
    22.         fallback
    23.             IsBinClose
    24.             MoveToBin
    25.  
    26. tree "TryGraspBall"
    27.     while
    28.         sequence
    29.             IsBallFound
    30.             IsBallClose
    31.         fallback
    32.             IsBallGrasped
    33.             GraspBall
    34.  
    35. tree "TryApproachBall"
    36.     while
    37.         IsBallFound
    38.         fallback
    39.             IsBallClose
    40.             MoveToBall
    41.  
    42. tree "TryFoundBall"
    43.     fallback
    44.         IsBallFound
    45.         FindBall
    46.  

    Conclusion

    In conclusion, using the nomenclature of this book, I would say that Panda BT is an hybrid between a reactive engine and an engine with memory. And even if most of the control nodes (sequence, fallback, parallel, race, ...) keep track of the status of their children and does not tick the children that are already completed, it is possible to implement reactivity using the while-node (or exclusively using tasks that complete on the first tick) and obtain the same reactivity as in a purely reactive (or memory-less) BT engine. It all depends on how you implement the tasks and write the BT scripts.
     
    Last edited: Aug 7, 2022
    ratking likes this.
  46. steven1022

    steven1022

    Joined:
    Sep 8, 2012
    Posts:
    21
    Does this mean we could use Vector3 or LayerMasks as examples in parameters?
     
  47. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    Yes, in Panda BT 2, variables of literally any type can be used as task parameters (int, float, string, Vector3, Color, GameObject, Component, Mesh, Texture, ...) . Let's implement a traffic light to illustrate how variables can be used.

    First you use the [PandaVariable] attribute to indicate that a variable can be referenced from a BT script. Then you implement a task having the same type as parameter. Finally, you can reference the variables from a BT script by prefixing their name with the '@' character.

    The MonoBehaviour implementing the traffic light could be:

    Code (CSharp):
    1. using UnityEngine;
    2. using Panda;
    3.  
    4. public class TrafficLight : MonoBehaviour
    5. {
    6.     // Colors
    7.     [PandaVariable] public Color Red = Color.red;
    8.     [PandaVariable] public Color Green = Color.green;
    9.     [PandaVariable] public Color Orange = new Color(1.0f, 0.5f, 0.0f);
    10.  
    11.     // Durations
    12.     [PandaVariable] public float LongDuration = 5.0f;
    13.     [PandaVariable] public float ShortDuration = 1.0f;
    14.  
    15.     // Task
    16.     [PandaTask]
    17.     bool SetColor(Color color)
    18.     {
    19.         this.GetComponent<Renderer>().material.color = color;
    20.         return true;
    21.     }
    22. }
    And the BT Script describing the traffic light could be:
    Code (CSharp):
    1. #Root sequence
    2.     SetColor @Green
    3.     Wait @LongDuration
    4.     SetColor @Orange
    5.     Wait @ShortDuration
    6.     SetColor @Red
    7.     Wait @LongDuration
    8.    
    Note that the variables could be defined on other GameObjects, and that different PandaBehaviours can share the same variables, which makes it possible to implement blackboards. Overall, using variables increases the re-usability of the tasks and make the code more modular.
     
    Last edited: Aug 28, 2022
    vx4 likes this.
  48. steven1022

    steven1022

    Joined:
    Sep 8, 2012
    Posts:
    21
    Will it be a release for Panda BT Pro or is it going to be a separate asset? I just purchased Pro.

    Will you need to adjust current version implementation to meet the next version standards? I notice you write PandaTask instead of just Task so thought I would ask.
     
  49. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    @steven1022
    Panda BT 2 will be release as a separate package while Panda BT free and Panda BT pro will be still available, in order to avoid breaking existing projects by an accidental upgrade. If you've already purchased Panda BT Pro version 1, you will be able to obtain version 2 for free within a grace period (maybe 1 or 2 months).

    Version 2 will not be backward compatible with version 1. Though, if your project is small or have only a few BT scripts the upgrade should be manageable. I would not advice to upgrade an existing complex project to version 2, unless you've consider the tradeoff between the upgrade workload vs the new features benefits.

    The version 2 api will be different from version 1. For instance, as you already noticed, the [Task] attribute will be rename to [PandaTask] to avoid name collision with System.Threading.Tasks.Task, since version 2 will support async method (returning async Task<bool> ) as bt task implementation. Also, the bt scripts will not be stored in *.txt files anymore but will have their own dedicated *.pbt files (recognized by Unity Editor through a Scripted Importer). However the bt script content should be compatible with version 2, so just a file rename to change the extension should be enough. I will list all the changes in a upgrade guideline in the future.
     
  50. steven1022

    steven1022

    Joined:
    Sep 8, 2012
    Posts:
    21
    What is timing of version 2? I wouldn't want to miss the grace period after just purchasing :rolleyes: