Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    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
    Hi guys!

    Panda BT is script-based Behaviour Tree engine. It allows you to easily write complex and scalable logics for your games.

    Here are the main features:
    • Lightweight and performant Behaviour Tree engine.
    • Minimalist Built-in scripting language.
    • Easily extensible by defining custom tasks in C#.
    • Realtime visualization and debugging.
    • Version control friendly (produces no binary assets).
    A typical work flow is as follow:
    1. Define simple tasks as C# methods in MonoBehaviours.
    2. Use the defined tasks as building block for writing BT scripts.
    3. Visualize the BT as it is running to have insight about how your AI is performing.
    Here is a screenshot of the Panda Behaviour component running a BT script:



    You can read the online documentation and try some demos on the Panda Behaviour website.

    Please let me know what do you think about it, how it is useful to you or what would you expect as improvements.

    Eric
     
    Last edited: Nov 13, 2022
    Abbrew, ZJP, nuverian and 2 others like this.
  2. seldom

    seldom

    Joined:
    Dec 4, 2013
    Posts:
    118
    The demos look really nice, especially being able to follow the tree execution inside the game window! Do I understand this correctly: trees are edited in text files, which can then be dragged onto a panda component?
     
    laurentlavigne and ericbegue like this.
  3. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    Thanks for your feedback!

    The visualization of tree execution is a great way to know exactly what's going on. It's also available in the Inspector.

    Yes, that's the way to use this component. You can also assigned several script files to the same PandaBehaviour component for better organisation. For example, you can have a script file used as a behaviour library, and another script file that implement the main BT.
     
    Last edited: Mar 8, 2016
  4. Jamster

    Jamster

    Joined:
    Apr 28, 2012
    Posts:
    1,102
    This is a really good asset, not tricky to understand and master at all! Keep up the good work :)
     
    laurentlavigne likes this.
  5. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    Thank you @Jamster! I'm glad you understand it and even master it!
    I am actually worried about the learning curve of my tool. It's reassuring you have no difficulties to learn how to use it.:)
     
  6. Jamster

    Jamster

    Joined:
    Apr 28, 2012
    Posts:
    1,102
    I don't think it's too hard to learn at all, perhaps more documentation on things like the Task.current wouldn't go amiss however as it took me a while to work out what that was doing with that.

    One thing that could be thought upon (though I'm not sure how you'd do it now it's released) is a more firm syntax. I think it's just me being used to C like languages but for the while loop it seems a little odd that the condition looks almost exactly the same as the body, the only difference being that they're on different lines. Another is that functions can have parenthesis or no parenthesis though that's easily solved by just adding parenthesis everywhere :p

    Nonetheless, a very good piece of code, seems fast enough running in practice and it's pretty flexible. I'm not sure if I'll use it in my current project (not that I know where that project is going at all) just because for the most part in my particular use I could do everything I'd do in Panda by just adding a line of code to Update. Though I'll definitely keep it in mind for the future :)

    May I also ask how it works inside the dll? Are you constructing a tree/fsm internally in Awake/Start or are you parsing the text as you go? If constructing a tree can we access the tree from code? :)

    Jamie
     
  7. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    Task.current

    Task.current is consequence of using methods as task implementations rather than classes. It would have been more tedious to define a new class each time a new task is required. But this construction is indeed a bit exotic if you expect an OOP architecture. Task.current is a key object, I will put more focus on its importance in the documentation or future tutorials. Thanks for your feedback on this important point.


    About the syntax


    About the syntax, if you have an idea about what would be more appropriate please tell me! And don't worry about it's already released; it's always possible to implement a versioning.

    The language is different from existing languages indeed (except for the indentation which is similar to Python). In fact, I've started with the idea to define the BT directly in C#, but I couldn't come up with a satisfying API that would make the BT both simple to write and easy to read. That's why I went in the direction of defining a new scripting language. A positive byproduct of that solution is the realtime visualization: that would have been impossible or really hard to implement in a C#-only solution.

    The basic principles behind the syntax are:
    • Everything is a node (tree, sequence, while, fallback, task, ...)
    • A node can have parameters (which type can be: boolean, int, float or string)
    • A node can have other nodes as children.
    Following this principle, the condition and the body are indeed both children of the while node, only their order distinct them from each other. The first child is the condition, the second child is the body. However, it's possible to make a visual distinction between the condition and the body by using indentation or single line parenting.

    For example:
    Code (CSharp):
    1.  
    2. // This...
    3. while
    4.     condition
    5.     body
    6.  
    7. // ...is the same as
    8. while condition
    9.     body

    Side note: while is not a loop (in Panda)


    I know it sounds confusing, but the while node is not a loop but a conditional node. The only node that implements looping is the repeat node.

    Let's clarify that with examples.

    (The syntax highlighting is not meaningful here, it's just that C# highlighting is applied... I'm dreaming about having a dedicated Panda syntax highlighting, ;) at the forum maintainers)

    For example the following construction is not a loop, but it's rather a condition:
    Code (CSharp):
    1.  // Start the engine and drive until the tank is empty.
    2. while hasGass
    3.    sequence
    4.        StartEngine
    5.        Drive
    6.  
    The while node is used to run a node under a satisfied assumption. We start the engine only once, then we keep driving while we have gass. There is no repetition.

    The equivalent of the while loop of other programming language is implemented with the combination of the while node and the repeat node:
    Code (CSharp):
    1.  // Shoot at the target several times as long as we have bullets.
    2. while hasBullets
    3.     reapeat
    4.         sequence
    5.            AimAtTarget
    6.            Shoot
    Here we repeat at aiming and shooting at the target while we have bullets. The aiming and shooting are repeated.


    Internals

    The script is not parsed as the BT is executed, it's parsed only once. A tree is indeed constructed as the result of the parsing.

    I tried to exposed the minimum of the internal functioning to the user (encapsulation principle). It's possible to access the internal tree from code, but that's not documented. Do you think there are use cases to have it exposed through the API, or are you just curious?:)
     
    Last edited: Mar 9, 2016
    nutversion1 likes this.
  8. Jamster

    Jamster

    Joined:
    Apr 28, 2012
    Posts:
    1,102
    Interesting, I definitely think that that information should (mostly) all be in the docs (it may be in there, I'll be honest I mainly used the examples to learn!) :)

    My thoughts were that if something were to return a condition affecting the statement before then it should be in brackets (or something) like:
    Code (csharp):
    1. while (condition)
    2.     body
    but actually I'd disagree with myself now because to follow it you need nodes such as:
    Code (csharp):
    1. fallback
    2.     (a)
    3.     (b)
    4.  
    5. repeat
    6.     (sequence
    7.         a
    8.         b)
    Which isn't so nice...

    I think that the best solution is actually the inline children as you setup, it looks a lot better than my idea!

    I was mainly being curious, I suppose there could be times where it would be nice to load in behaviours from the internet (for modding prehaps) but I doubt that being able to access the tree would have huge value.

    Jamie :)
     
  9. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    Also the condition can be any node, even a complete tree!
    So you can have structure like this.
    Code (CSharp):
    1. // Drink while glass is not empty *and* still thirsty
    2. while
    3.      sequence // condition
    4.         not IsGlassEmpty
    5.         IsThirsty
    6.      repeat // action
    7.          sequence
    8.              GrabGlass
    9.              Drink
    10.              PutGlassDown
    11.  
    Here the sequence is used to implement an AND logic.

    There was someone who was looking for a solution to make a game where the players would write their own AIs and let them battle each other (I couldn't find back his post). He was looking for a solution that would allow the edition and compilation of the BT at run-time. Panda is suitable for this usage. For now, a TextAsset is required to store the BT script. But maybe I could expose an API to build BT directly from a string.
     
    Last edited: Mar 17, 2016
    nutversion1 and Jamster like this.
  10. tatoforever

    tatoforever

    Joined:
    Apr 16, 2009
    Posts:
    4,364
    Hey Eric,
    Great tool, I would like to request access to the sources of the tool you are providing (already wrote a review on the AssetStore). I don't mind paying for it.
    Keep the good work.
     
    Last edited: Mar 9, 2016
  11. ericbegue

    ericbegue

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

    Thank you for your interest in my package and for your review on the Asset Store.

    I had the intention to release a paid/pro version on the Asset Store, after receiving feedbacks from users and after implementing eventual improvements. However, after several pressing requests to have access to the sources, I decided to include the sources in the pro version, and release it earlier than planned. So, stay tuned!

    It the meantime you can use the free version. The BT scripts made with the free version will be compatible with the pro version without issue.
     
    Jamster and tatoforever like this.
  12. tatoforever

    tatoforever

    Joined:
    Apr 16, 2009
    Posts:
    4,364
    I'll update the review again.
    Regards! :)
     
  13. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Looks nice. Any possibility of wrangling VS to do some auto complete and compile time checking of .BT files?

    I haven't the foggiest clue of how to approach this.
     
  14. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    Thank you @BoredMormon! Great you had some time to have a look at it.

    Excellent point. That's where there is a big space for improvement. Editing the BT scripts as plain text without a standard IDE support is not ideal. Even if the BT language is simple, having syntax highlighting and auto-completion in Visual Studio would be a productivity helper.

    It seems possible to develop plugins for VS to support custom languages. However, it seems to be a complete project in itself. If anyone has experience with this topic any input would be welcome.

    In any case, this is something on the top of my to-do-list.

    About compile time checking of BT script. The scripts are already checked when they are modified but only for the current opened scene. I've planned to implement a project-wise checking when the scripts get imported as assets.
     
  15. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    I spoke too soon on that one, a few minutes later the console spit out an error in my BT. Nice work.

    So so far so good. Just putting together a space invaders clone to see how the thing works. Will let you know in a couple of hours how it went.
     
  16. tatoforever

    tatoforever

    Joined:
    Apr 16, 2009
    Posts:
    4,364
    A small editor that let you add/remove nodes/tasks directly on the Inspector (where the BT is visible) and furthermore drag/drop/move/cut/copy/past nodes/tasks on it would be great additions in the future. :D
    If I ever get the time, I'll work on it (would need sources for that btw). ;)
     
    Kiwasi likes this.
  17. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    Sounds great. I am really curious about the result.
    Meanwhile, let me know if you have issue in the making of this game.
     
  18. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    I've been always considering an all-in-inspector editor, it sounds as good idea but I'm worried about how practical it would be. It seems difficult to have a satisfying solution combining both a drag-and-drop editing style and code typing style. It's seems this design would lead to some sort of advanced text editor in the inspector. I'm not sure about doing everything in the inspector, you need to edit your C# code externally anyway. So I rather leaning towards adding support to a trustworthy external tool such as VS.

    But your are welcome if you have suggestions, or eventual implementations. (btw, the sources are on their way to the Asset Store).
     
    Last edited: Mar 16, 2016
    tatoforever likes this.
  19. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Games done. A couple of things I noticed
    • Multi object editing would be nice.
    • Wait 100 counts down in ticks instead of seconds. Wait 100.0 counts down in seconds. Is this desired behavior? Should be in the docs.
    • Selecting an object in the hierarchy seems to reset the behavior tree on that object
    Anyway, I had fun with this. Thanks for putting it together. I'm probably going to use it heavily in some of my future work.

    You can check out the game here.
    Let me know if seeing how I used your asset would be useful, I can send source.
     
  20. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    That was fast!

    Thank you for your feedback.
    • I take note about multi object editing.
    • Task definition can be overloaded. There are indeed two versions of the "Wait" task and they work as you've described on purpose (maybe I should drop the ticks version, I'm not sure). There are other build-in tasks (implemented int the Panda Behaviour component) and that definitely need to be documented. Sorry about that. BTW, If you have suggestions about other built-in tasks, your are welcome.
    • I'll have a look about that resetting bug.
    I'm glad you like it and are planning to use it further!

    The games look nice and play smoothly. About the game-play itself, I feel the ship should move faster. Also you should be able to shoot again as soon the bullet hits an enemy or has leaved the screen.

    Of course I'm curious about how you've made it, please send it to me.:)

    Edit:
    The resetting bug is fixed. There is already a package review pending, therefore this fix won't be in the next release, but the one after.
     
    Last edited: Mar 16, 2016
  21. MV10

    MV10

    Joined:
    Nov 6, 2015
    Posts:
    1,889
    I downloaded it late last night after skimming the docs but haven't had time to play with it. A couple quick questions.

    Are Task method names required to be unique on the GameObject with the BT script, or is there a way to specifically reference a Task method on a specific class when more than one attached script uses the same method for different purposes? To make up an example off the top of my head, maybe there is a Maneuver script and a Targeting script and they each have a PointAtEnemy method. Is there a way to differentiate?

    I noticed your Task SetColor example passes constants to the method. Can variables be used? I suppose they're scoped to whatever the target Task method can see? Given that success/fail is the return BT needs, does out/ref work?

    Looks pretty cool though. Very glad you referenced other articles, the Simpson article in particular really shows off what they can do! I'm tempted to sidetrack myself into writing a node-based editor.
     
  22. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    Thank @MV10 you, I hope you'll give it a try soon!

    A task in a BT script is bound to a C# method with the same signature (defined by the method name and the types of its parameters). Unfortunately, the same method implemented in different MonoBehaviours is not supported; the engine will raise an exception not knowing which one to choose. However, it's possible to have overloaded tasks, that is, tasks with the same names but different parameters. In this case, it does not matter whether the tasks are implemented in the same MonoBehaviour or not.

    I wanted to keep the language as simple as possible with no ambition to replace C#. Instead, the BT script are complementary to C#, so you can still do a lot on the C# side. If you want to have a variable, you can do it this way with the SetColor example:

    Code (CSharp):
    1. using UnityEngine;
    2. using Panda;
    3.  
    4. public class ColorTasks : MonoBehaviour
    5. {
    6.     public Color myColor;
    7.     public Color[] myColorArray;
    8.  
    9.     [Task]
    10.     void SetColor_myColor()
    11.     {
    12.         this.GetComponent<Renderer>().material.color = myColor;
    13.         Task.current.Succeed();
    14.     }
    15.  
    16.     [Task]
    17.     void SetColor_myColor(int colorIndex)
    18.     {
    19.         this.GetComponent<Renderer>().material.color = myColorArray[colorIndex];
    20.         Task.current.Succeed();
    21.     }
    22. }
    This way you can set your colors from the Inspector as usual.
    And here is a corresponding BT script:
    Code (CSharp):
    1. tree "Root"
    2.     sequence
    3.         SetColor_myColor
    4.         Wait 1.0
    5.         tree "CycleColors"
    6.  
    7. tree "CycleColors"
    8.     sequence
    9.         SetColor_myColor 0
    10.         Wait 1.0
    11.         SetColor_myColor 1
    12.         Wait 1.0
    13.         SetColor_myColor 2
    14.         Wait 1.0
    Thank you!

    Theses articles are very inspirational, they were in fact great resources for developing Panda. If you want to implement your own node-based editor, I would advice to have a look at the Asset Store first, there are already some solutions there.

    I wanted to avoid writing yet another node-based editor, because as a programmer, I feel more efficient by typing code instead of dragging-and-dropping nodes around, and I hope there are other people like me :). And I believe that editing Behaviour Tree is closer to programming, that was in fact my motivation to create this package.
     
    Last edited: Mar 16, 2016
  23. tatoforever

    tatoforever

    Joined:
    Apr 16, 2009
    Posts:
    4,364
    Yeah, I would love typing in a text file instead of using visual editors any day but my designers love the other way (drag and drop things visually). They kinda like the minimalistic approach of your BT but where asking me if they can add/remove stuff visually instead of going through text files. lol
     
  24. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    I see. Is that so painful for designers to reach for text editor and fire it up? :) Though, I understand it can be a bit scary for designers about typing raw text and that there is something reassuring about using an interface. I had a programmer workflow in mind while designing this tool. But I think there should be no technical difficulties to integrate this tool with a more graphical editor. Anyhow, the tasks still need a C# sharp implementation, and afak there is no node-based editor for C#.

    Overall, I believe editing a Behaviour Tree is really close to programming, so it would be better to approach the workflow with programmer mindset. However, you don't need to be a highly skilled programmer to write a Behaviour Tree. In fact, I believe it requires less programming skills to write an AI using this tool, rather than in plain C# only. All you need to know is how a BT is constructed, how the structural nodes works (which is also required with a node-based editor) and how to implement methods in C#. It requires no in depth knowledge about OOP, no inheritance, no encapsulation, etc... . So a beginner in programming could put stuff together rather easily.

    I'm not completely closed to the idea to integrate a visual editor. This tool is very young. Just give it some time.

    Edit:
    I would like to add some advantages about using text file, that would be hard or inefficient to realized with a node-based editor.
    - Copy/Paste part of tree.
    - Indent a whole block of text to manage node parenting, and redefine the hierarchy.
    - Write comments anywhere to document your AI directly in script.
    - Mass renaming (useful during refactoring).

    It also makes electronic communication easier. You can easily instant-message you BT script to a colleague to show something interesting. Or post your BT on a forum to request for help. Or share your AI on a wiki page.
     
    Last edited: Mar 17, 2016
  25. iivo_k

    iivo_k

    Joined:
    Jan 28, 2013
    Posts:
    314
    Gave it a quick test and unfortunately didn't like what I saw. Just running the first demo scene with the cube changing colors, 1kB of memory is allocated per frame. Duplicate the cube and the memory allocations are also doubled. The code is in a DLL so can't try to fix it myself either and it also happens with a build, not just in editor.
     
  26. MV10

    MV10

    Joined:
    Nov 6, 2015
    Posts:
    1,889
    Oh I definitely wouldn't want visual-only ... but it was pretty striking to me how easy it was to follow the graphs in the linked articles rather than picking through a text file. Thanks for the response.
     
  27. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    I've just run the test with 1000 copies of the cube. I don't see the 1kB increase per frame.
    With 1 cube the memory used by mono is ~8 MB.
    With 1000 cube the memory used by mono is ~14 MB.
    The program is running for about 10 mins now, with that amount of cubes, the memory varies but it stays within the range of 12 MB to 17 MB.

    Note that even with a empty scene from an empty project there are memory variation of ~0.1MB per frame.

    The sources will be available in the pro version.

    Thank you for profiling. I am really concern with the performance of this tool. Any feedback on that part is welcome.
     
  28. iivo_k

    iivo_k

    Joined:
    Jan 28, 2013
    Posts:
    314
    I don't mean it leaks memory, I mean it allocates memory each frame (creates & deletes objects) which in turn makes the garbage collector run more often. It's not that big a deal on desktop, but unity's GC is bad and on mobile that causes slow frames. You can just use the profiler and see the allocations per frame, optimally those should be 0.
     
  29. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    I see it here too. I will have a closer look at it to see what exactly is going on here and try to eliminate the allocations.
    Good catch. Thank you for reporting it.
     
  30. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    I was amazed at how quickly things could be done with behavior trees. I hit a few new types of bugs in my own project, mainly because I'd never used a tree before. But the underlying logic is pretty straight forward. And being able to watch execution in the inspector window is amazing.

    https://BoredMormon@bitbucket.org/BoredMormon/pandabehaviourspaceinvaders.git

    You will have to re import PandaBehaviour. I'm generally not comfortable with putting other people's assets in my public repos.

    Feel free to use the project however you like.

    I like the ticks version, I used it in a few places where I needed to move my logic onto different frames. A version that takes unscaled time would also be useful.

    I totally took my designer hat off for this exercise. There are a ton of things that would make the game better. But my aim was to learn Behavior Trees.
     
  31. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    I found the culprit, and somebody else already did:
    http://stackoverflow.com/questions/...-parameterless-method-using-methodinfo-invoke

    I have absolutely no idea why is that happening. It's quiet annoying. Could it be a bug in the mono version Unity is using?

    Edit:

    I've submitted a bug report to Unity. I hope they can fixed it. Otherwise I would need to find a workaround...

    For the most curious of you here is a script reproducing the bug.
    Code (CSharp):
    1. using System.Reflection;
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. public class InvokeGCAllocation : MonoBehaviour
    6. {
    7.     public bool withParameters = false;
    8.  
    9.     MethodInfo _someFunction;
    10.     MethodInfo _someFunctionWithParameters;
    11.  
    12.     public void SomeFunction()
    13.     {
    14.  
    15.     }
    16.  
    17.     object[] parameters = new object[2];
    18.     public void SomeFunctionWithParameters(int a, int b)
    19.     {
    20.  
    21.     }
    22.  
    23.  
    24.     void Start ()
    25.     {
    26.         parameters[0] = 0;
    27.         parameters[1] = 1;
    28.  
    29.         _someFunction = typeof(InvokeGCAllocation).GetMethod("SomeFunction");
    30.         _someFunctionWithParameters = typeof(InvokeGCAllocation).GetMethod("SomeFunctionWithParameters");
    31.     }
    32.  
    33.     void Update ()
    34.     {
    35.         if( withParameters )
    36.             _someFunctionWithParameters.Invoke(this, parameters); // This one is fine, no memory allocated.
    37.         else
    38.             _someFunction.Invoke(this, null); // This one allocate 40B per call for the GC to clean!
    39.     }
    40. }
     
    Last edited: Mar 16, 2016
  32. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    That's why I love Behaviour Tree so much. And I love it even more hearing this kind of experience.

    Thank you for your project. I'll have a look at it.

    It's already there: RealtimeWait. I really need to document these built-in tasks:p. BTW, they are implemented in the Panda Behaviour component itself and you can have a look at it, the source is available.

    For a game made that fast is quiet successful and your main goal of learning BT is reached.
     
    Kiwasi likes this.
  33. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    This is fixed. Now the core engine is optimized on this issue; it uses 0B of GC Allocations per frame.

    However it is of course always possible to write tasks that are not optimized. If you are concern with optimized GC. Read the following:

    What you often need to take care of is the custom debugInfo string that is displayed in the inspector to give information about how a task is progressing.

    Because string.Format(...) allocates memory on each call, you want to avoid that as much as possible. If you use debugInfo, use it in combination with the new global bool Task.isInspected, which returns whether the current BT is displayed in the Inspector. So you can make sure that strings are formatted only when they are actually displayed in the Inspector.

    For example:
    Code (CSharp):
    1. if( Task.isInspected )
    2.     Task.current.debugInfo = string.Format("t-{0:0.000}",  tta);
    There is already a package review pending, so this optimization and Task.isInspected will be available in the following release.
     
    Last edited: Mar 17, 2016
    iivo_k and Kiwasi like this.
  34. ericbegue

    ericbegue

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

    I had a look at your project. Overall, the way you've used Panda is simple and straight forward. I like this style.

    Looking at the way you build the BT controlling the player firing, it seems (I'm not sure) there is confusion about how the while node works:
    Code (CSharp):
    1. tree "Shoot"
    2.     Sequence
    3.         Wait 1.5
    4.         While PlayerPressShoot
    5.             PlayerShoot
    And this is the definition of PlayerPressShoot:

    Code (CSharp):
    1.     [Task]
    2.     void PlayerPressShoot ()
    3.     {
    4.         if (Input.GetButton("Fire1"))
    5.             Task.current.Succeed();
    6.     }
    The PlayerPressShoot task runs and succeeds when the player press the "Fire1" button, and it never fails.

    The while node is used to run a task under an assumption, if the assumption fails the task is not ticked and the while node fails. You can think of it as a guard. However, the PlayerPressShoot task never fails, so guarding the PlayerShoot task is not necessary.

    I guess you wanted to do something as follow:
    First define a task that tells whether the player is pressing the fire button or not:
    Code (CSharp):
    1.     [Task]
    2.     void IsFireButtonPressed()
    3.     {
    4.         Task.current.Complete(Input.GetButton("Fire1"));
    5.     }
    This task completes immediately. it succeeds if the "Fire1" is pressed, and fails otherwise.

    Then the behaviour tree would be:
    Code (CSharp):
    1. tree "Shoot"
    2.     Sequence
    3.         IsFireButtonPressed
    4.         PlayerShoot
    5.         Wait 1.5
    So, sequences can be used to implement conditionals. Here the sequence fails if the fire button is not pressed, leaving the remaining task non-executed. In other words, "PlayerShoot" and "Wait 1.5" are executed only if "IsFireButtonPressed" succeeds.

    I really like the way you've implemented the GameController:
    Code (CSharp):
    1. tree "Root"
    2.     Sequence
    3.         FreezeGame
    4.         Wait 120
    5.         StartGame
    6.         Race
    7.             GameWon
    8.             GameLost
    9.         FreezeGame
    10.         Wait 120
    11.         RestartLevel
    Particularly, the way you've used the race node to decide how the game ends. I would never have thought to use the race node in this situation :).

    I would like add a point about the character case in script. The language keywords (tree, sequence, fallback, ...) are case insensitive. So, for example, it's up to to you if you want to write sequence, Sequence, SEQUENCE or even SeQuenCE. However task name are case sensitive: the task name in a BT script has to be the exact same name as the C# method.

    This property can be used for defining a convention to distinct between the language keywords and the tasks. I like to use only small case for the keywords and CamelCase for the task. When the task has some variable, I separate it with an underscore (for example: SetDestination_EnemyPosition). But convention is a matter of taste, so it's up to you and your team to define one.
     
    Last edited: Mar 17, 2016
    MV10 and Kiwasi like this.
  35. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Apparently the while is just a carry over from my C# habits. You are right, a sequence would actually do the job just as well, and would be clearer.

    I'm currently messing around with the trees in one of my more complex projects. I'll let you know how it goes. So far it's much easier to understand then any other AI scheme I've used.
     
    ericbegue likes this.
  36. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    Actually, I was also a victim of my C# habits. In fact, my first implementation of the while node was as you would have expected, that is a conditional loop. Later on, I feel the need for a node that would run another node under an assumption; there are plenty of situations where you need to check that some conditions are satisfied for running a node. Then, I was looking for a pertinent name for such a node. "If" didn't work, because it as this idea of a punctual branching in time and not much about long running tasks and simultaneity. Then I thought about "unless", this word was a good candidate it links two ongoing actions with a condition, however it has that double negation any programmer is allergic to. Then, I thought, wait a minute, if you look at the literal meaning of the word while, that was exactly what it means. You have two simultaneous action where one is conditioned by the other and it makes perfect sens in everyday life: "I can nail that plank while you hold it", "While I have money, I'll spend it", "You can't drive while you drunk." .Then I decided to not use the while node as a loop, there is already node that implements looping: the repeat node. And if I need to implement a conditional loop, then I just have to combine a while node and a repeat node. Everything just fitted.

    About the sequence node and conditional, I actually had an if node implemented (Because, what kind of programming does not have an if?), then I dropped it when I realized it was a duplication of the sequence node.

    Have fun with your more complex projects, I'm curious how it will go.
     
    Last edited: Mar 17, 2016
    MV10 likes this.
  37. Michael-Li

    Michael-Li

    Joined:
    Mar 19, 2016
    Posts:
    2
    HI~ ericbegue:
    Can I change bt file content dynamic when bt is running?(that mean I need to create new bt format file or text memory dynamic to use when program is running,user can change code at run time)
     
  38. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    Hi @Michael Li,

    Thank you for you interest in this tool.

    I already had similar requests. I will expose a method on the PandaBehaviour component to compile a BT from a string. It will be:
    Code (CSharp):
    1. PandaBehaviour.Compile( string source )
    It will be available in the coming releases. So, stay tune!
     
    Kiwasi likes this.
  39. Michael-Li

    Michael-Li

    Joined:
    Mar 19, 2016
    Posts:
    2
  40. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    Sorry. It seems I've missed your post.

    A graphical representation might be easier to understand and it helps even more when you are not familiar with Behaviour Tree.

    A textual representation also has some advantages:
    • It's suitable for representing hierarchy (think about the unity scene Hierarchy tab)
    • No need to do the layout manually. (With a node-based editor, you often find yourself dragging the nodes around until you get a nice layout.)
    • It's very compact! Therefore, more information at a glance. ( A node-based editor often take a large portion of the screen.)
    I'm sure you'll get to use to reading BT scripts, I guess it's a matter of habit. It should not be more difficult than reading C# code.
     
    Last edited: Mar 21, 2016
    laurentlavigne likes this.
  41. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
  42. MV10

    MV10

    Joined:
    Nov 6, 2015
    Posts:
    1,889
    Couple minor typos there... you have "pose" instead of "pause" for DebugBreak, and for the last batch of Input entries it should be "indefinitely" rather than "undefinitely."

    Definitely some interesting Input options though. I think what I'm liking the most about all of this is the ease of chaining activities not tied to frame-based processing. (I realize that is no great insight!)
     
  43. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    @MV10, thank you for spotting the typos. That's fixed now.
    If there is only one thing to say about this tool. This is it!

    A serious problem constantly present while developing games is the lack of simply describing the game logic in a time independent way. Everything is fine until you start to write some code to handle long running activities, and it get worst when theses activities include some conditions and branching, even if theses activities are quiet easy to describe in plain english (or any human language). The usual approach to solve it, is to use some states to keep track of what the AI is currently doing, then changes the state on the go and use some coroutines, some time tracking variables or counters... Then you look back at your code, and you can't decipher the high-level logic anymore, it's diluted all over your code. I have reached the conclusion that C# or any similar programming languages does not provide an appropriate mechanism to describe this type of activities and that Behaviour Trees are just the perfect tool for that.

    Let's say you want to do something simple as:

    "I want my NPC to carry this precious object to some destination, but if there are some enemies on the way, secure the object and then eliminate the enemies. Once the threat is eliminated, go on with carrying the object to its destination."

    This is a simple mission to understand.

    Now, how would you implement that in C# only? It will take you days! I don't even dare to sketch a solution for solving that.
    But with a Behaviour Tree you can almost make a literal translation of this two sentences:
    Code (CSharp):
    1. tree "root"
    2.     fallback
    3.         tree "DeliverPreciousObject"
    4.         tree "EliminateThreat"
    5.    
    6. tree "DeliverPreciousObject"
    7.     fallback
    8.         while not IsThereThreatOnTheWay
    9.             sequence
    10.                 MoveTo_PreciousObjectLocation
    11.                 PickUp_Object
    12.                 MoveTo_PreciousObjectDeliveryLocation
    13.         tree "SecurePreciousObject"
    14.    
    15. tree "SecurePreciousObject"
    16.         sequence
    17.             MoveTo_PreciousObjectSafeLocation
    18.             Drop_Object
    19.  
    20. tree "EliminateThreat"
    21.     while IsThereThreatOnTheWay
    22.         sequence
    23.             SetTarget_NearestEnemy
    24.             Attack_Target
    25.  
    You have your high-level logic readable right there. And you know that the C# implementation of the tasks should be trivial.
     
    Last edited: Mar 22, 2016
    MV10 likes this.
  44. MV10

    MV10

    Joined:
    Nov 6, 2015
    Posts:
    1,889
    Yeah my wife and I are knocking out a simple story-driven RPG in our spare time and this kind of abstraction is precisely how we'll be using this. I actually came to it trying to decide whether it was suited to dialogue trees (it isn't really, and so I'm wrapping up a custom mini-language for that).
     
  45. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    @MV10, great that you are planning to use it for your game!

    For a dialogue system, my first though was that BT was not appropriate and that you need more a Decision Tree for that. But after giving it some thoughts, it could come as handy if you define some proper tasks. Let's try out an example, let's say you want to design a dialogue for a shop in your RPG game:
    Code (CSharp):
    1.  
    2. tree "Dialogue"
    3.     sequence
    4.         Say "Welcome! What can I do for you?"
    5.         Option 1 "I'm fine... just looking around."
    6.         Option 2 "What are you selling?"
    7.         Option 3 "This is a hold-up! All your gold and fast!!"
    8.  
    9.         WaitPlayerChoice
    10.  
    11.         fallback
    12.             sequence Choice 1
    13.                 Say "Ok, take your time. I'm here if you need me."
    14.      
    15.             sequence Choice 2
    16.                 tree "Shop"
    17.      
    18.             sequence Choice 3
    19.                 Say "Help! Guard! Heeelp!"
    20.                 AddGold(100)
    21.                 AlertNearestGuard
    22.  
    23. tree "Shop"
    24.     sequence
    25.         Say "Trinkets, odds and ends, that sort of thing."
    26.         Option 1 "Health potion: 10 G"
    27.         Option 2 "Mana potion: 5 G"
    28.         Option 3 "City map: 50 G"
    29.  
    30.         WaitPlayerChoice
    31.  
    32.         fallback
    33.             sequence Choice 1
    34.                 RemoveGold(10) AddItem("HPotion")
    35.             sequence Choice 2
    36.                 RemoveGold(5) AddItem("MPotion")
    37.             sequence Choice 3
    38.                 RemoveGold(50) AddItem("CityMap")
    39.             sequence
    40.                 Say "Sorry. You don't have enough gold!"
    41.                 Fail
    42.      
    43.         Say "Here you go. Thank you!"
    The nice thing with that system is you can easily integrate the consequences of the dialogue choice quiet easily. Like the inventory management, or some triggers, like calling a guard.
     
    Last edited: Mar 23, 2016
    MV10 likes this.
  46. MV10

    MV10

    Joined:
    Nov 6, 2015
    Posts:
    1,889
    Interesting. I'll revisit this, thanks.
     
  47. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
  48. Nigey

    Nigey

    Joined:
    Sep 29, 2013
    Posts:
    1,129
    Hi @ericbegue

    I'm really liking your BT, but I've found a bug!

    Sometimes deselecting a unit, and then reselecting that unit can cause Unity to pause for between 30secs and 2 mins, and cause this error:

    This is with only this code:

    Code (CSharp):
    1.  
    2.          public void BehaviourAction()
    3.          {
    4.              if (!isInitialised)
    5.              {
    6.                  Initialise();
    7.              }
    8.  
    9.              pandaBehavior.Tick();
    10.          }
    11.  
    12.          [Task]
    13.          private bool HasTargets()
    14.          {
    15.              return controller.enemies.Count > 0 ? true : false;
    16.          }
    17.  
    18.          [Task]
    19.          private bool CanWin()
    20.          {
    21.              return true;
    22.          }
    23.  
    24.          [Task]
    25.          private void Attack()
    26.          {
    27.              Task.current.Succeed();
    28.          }
    29.  
    30.          [Task]
    31.          private bool CanMove()
    32.          {
    33.              return true;
    34.          }
    35.  
    36.          [Task]
    37.          private bool SafeDistance()
    38.          {
    39.              return false;
    40.          }
    41.  
    42.          [Task]
    43.          private void Flee()
    44.          {
    45.              controller.transporter.Flee(controller.enemies[0].unitObject.transform.position, 500);
    46.              Task.current.Succeed();
    47.          }

    Code (CSharp):
    1. tree "Root"
    2.      fallback
    3.          tree "Idle"
    4.          tree "Attack"
    5.          tree "Flee"
    6.      
    7. tree "Idle"
    8.      sequence
    9.          not HasTargets
    10.  
    11.  
    12. tree "Attack"
    13.      while CanWin
    14.              sequence
    15.                  Attack
    16.  
    17. tree "Flee"
    18.      sequence
    19.          CanMove
    20.          not SafeDistance
    21.          Flee
    So very simplistic, but it still freezes Unity for an unnatural amount of time whenever I try to select the unit in the hierarchy. This is especially strange since I'm using a high end PC.

    EDIT: I've found the culprit. When you have several components attached to a GameObject, and Panda Behavior is one of them, by selecting 'Move Up', or 'Move Down', it begins to slow down. Once you've done this about 3 or 4 times (trying to move Panda Behaviour to the top), Unity really begins to chug. This chugging will then continue whenever you try to select that unit, sometimes throwing the StackOverFlow exception. Would you be able to take a look when you get a chance? Right now my only workaround is deleting every other component from a GameObject, adding Panda Behaviour, and then adding all the rest (as it's always best to have Panda Behaviour at the top so it's easy to click on and read what's happening).

    EDIT AGAIN: I've found that by minimising panda behavior before moving it up or down the component hierarchy, it doesn't trigger the slowdown.

    Thanks!
     
    Last edited: Mar 24, 2016
  49. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    @Nigey , thanks for your bug report. I'll take a look to see what's exactly is going on there and try to fix it.

    Side note: I also often need to reorganized components attached on a GameObject, It's annoying to use the move up/down menu. It would be great if that was posible with a simple drag&drop. It seems a good candidate for a Unity feature request:).
     
    reinfeldx likes this.
  50. Nigey

    Nigey

    Joined:
    Sep 29, 2013
    Posts:
    1,129
    Lol, I thought the exact same thing.