Search Unity

Behavior Designer - Behavior Trees for Everyone

Discussion in 'Assets and Asset Store' started by opsive, Feb 10, 2014.

  1. opsive

    opsive

    Joined:
    Mar 15, 2010
    Posts:
    5,127
    I've been working with cygnusprojects and Seith and I have a workaround for that Unity 4.3 bug. I have submitted an update and it is currently waiting for approval. If you receive any warnings/errors caused by LoadAssetAtPath before that update is approved please send me a message and I'll send you that update.
     
  2. Silly_Rollo

    Silly_Rollo

    Joined:
    Dec 21, 2012
    Posts:
    501
    I think Rain does something like that too and it causes problems. I'd have a configurable path for behavior designer prefabs and only look there. Also, until we get an x64 editor anytime something wants to get poking all over a big project is just asking for Unity to crash.
     
  3. Seith

    Seith

    Joined:
    Nov 3, 2012
    Posts:
    755
    Hi Opsive, is there a way to use coroutines within a task?
     
  4. cygnusprojects

    cygnusprojects

    Joined:
    Mar 13, 2011
    Posts:
    767
    Hi Opsive,

    I'm trying to implement the A* pathfinding into an Action but I'm having troubles with the initialization of some components/variables. Within the override of the Awake function I'm catching some variables but I'm always getting the error some variable isn't assigned.
    For instance: UnassignedReferenceException: The variable tr of 'MoveUsingAStar' has not been assigned. but in my Awake I'm having following code:

    mecanimAnimator = gameObject.GetComponentInChildren<Animator>();
    if (mecanimAnimator == null) Debug.Log("No Mecanim ?");
    //This is a simple optimization, cache the transform component lookup
    tr = mecanimAnimator.transform;


    The message "No Mecanim ?" isn't displayed.

    This error pops up when I edited my action class so I'm guessing the OnAwake call isn't called, but the OnUpdate is (using the tr variable).
     
  5. opsive

    opsive

    Joined:
    Mar 15, 2010
    Posts:
    5,127
    That's a good point, I'll require it to be in a configurable path in the next update. I already submitted a workaround yesterday and I don't want to lose my place in the queue since that workaround will at least prevent any errors from happening.

    Yes, but currently it does not work if you pass in a string into StartCoroutine. You must use the IEnumerator version. I will look into getting the string version to work, especially since that is the only way you can stop a specific coroutine.

    Code (csharp):
    1.  
    2.     public class Coroutine : Action
    3.     {
    4.         public override void OnStart()
    5.         {
    6.             StartCoroutine(myCoroutine());
    7.         }
    8.  
    9.         public IEnumerator myCoroutine()
    10.         {
    11.             yield return null;
    12.             Debug.Log("Got here");
    13.         }
    14.     }
    15.  
    By itself that code looks like it should work fine. Have you tried putting an debug statement or breakpoint within the OnAwake call to see if it is being called?
     
    Last edited: Feb 23, 2014
  6. Seith

    Seith

    Joined:
    Nov 3, 2012
    Posts:
    755
    Hi Opsive, thanks for your reply concerning coroutines.

    I've met a very strange (and annoying) problem: I have a behavior tree which is assigned to my AI character prefab as a component and it works great in the scene where I initially created the tree. But if I drop that character prefab in another scene then the behavior tree is ineffective. I mean the component is still there (with the correct name) but it has no effect on the character.

    And if I open the Behavior Designer window, it's empty. Even though the correct behavior tree is selected in the top menu. Is this a bug or am I doing something wrong?

    Also another question: where are the behavior trees saved exactly? It makes me a little nervous not knowing. One time I deleted the Behavior Tree component from my character and it looked like I had actually destroyed that BT for all intents and purposes. Scary.
     
    Last edited: Feb 23, 2014
  7. SteveB

    SteveB

    Joined:
    Jan 17, 2009
    Posts:
    1,451
    Hmmm, seeing as there are a variety of dialog systems using BT's (or at least of a sort), do you think it's possible to do this with your tool? I watched all your videos, including creating custom tasks which seems as if this could conceivably work...mebbie? :D

    Thank you!

    -Steven
     
  8. opsive

    opsive

    Joined:
    Mar 15, 2010
    Posts:
    5,127
    @Seith -

    Can you list the steps that you took to save out the BT? Here's what I tried to do to reproduce the bug:

    1. Create a behavior tree in scene1 attached to a game object.
    2. Drag that game object into the project to save it out as a prefab
    3. Create a new scene and drag that prefab into the scene

    What step are you doing differently?

    The behavior tree tasks are saved within the behavior tree component. When you delete the component you are also going to delete the behavior tree. If you want multiple behavior trees on a single game object then you add multiple behavior tree components. This is why when you deleted the behavior tree component the behavior tree was also gone. The behavior tree is a collection of tasks, and those tasks have a base class of ScriptableObjects. ScriptableObjects don't save out to prefabs very well so Behavior Designer does its own serialization. With the bug that you are experiencing it looks like the serialization is going wrong somewhere.

    Related to the coroutines, I am working on that now and should be able to send you a new version shortly

    @SteveB -
    While I never have tried it I think you could use the basic tasks of a behavior tree to mimic a dialog tree. You could create new condition task that is used to determine which text to output and then a new action class that will actually output the text. If you combine that with sequence and selector tasks you then have a dialog tree.
     
  9. cygnusprojects

    cygnusprojects

    Joined:
    Mar 13, 2011
    Posts:
    767
    Hi Opsive,

    I worked around my problem by introducing better error trapping :) However I'm experience a problem with the so called Shared Fields. I have a field called target within the select new waypoint action and the same field in the pathfinding action (move node). When I run the tree though the target gets assigned in the selection node but the target value in the move node stays a zero vector. Any tiny thingy I'm missing?

    EDIT: Is there a way to declare and use global variables? A way to assign variables at runtime when fi spawning an enemy and assign the waypoints collection?
     
    Last edited: Feb 23, 2014
  10. opsive

    opsive

    Joined:
    Mar 15, 2010
    Posts:
    5,127
    Is target marked as a shared field within both the waypoint action and the move node action? For example:

    Code (csharp):
    1.  
    2. public class Waypoint : Action
    3. {
    4.    [SharedField]
    5.    public Transform target;
    6. }
    7.  
    and

    Code (csharp):
    1.  
    2. public class Move : Action
    3. {
    4.    [SharedField]
    5.    public Transform target;
    6. }
    7.  
    Edit: I just saw your edit. If you want to dynamically assign the variables you are going to have to use a blackboard. Take a look at the CTF and RTS blackboard for an example. The first new feature that I add will be a way to use the editor to do this so you don't need to write your own blackboard class. Is this what you were asking?
     
    Last edited: Feb 23, 2014
  11. cygnusprojects

    cygnusprojects

    Joined:
    Mar 13, 2011
    Posts:
    767
    Yes, both actions have

    Code (csharp):
    1.     [SharedField]
    2.     public Vector3 target;
    When running I can see the value in the designer inspector to change to something else then (0,0,0) but the move actions are staying at (0,0,0).
     
  12. opsive

    opsive

    Joined:
    Mar 15, 2010
    Posts:
    5,127
    Oh I see what you are trying to do now. The SharedFields only work within the Behavior Designer editor. The runtime doesn't know anything about shared fields so when you change it during gameplay it will only be local to the one task. For what you want to do I would use a blackboard (take a look at the end of my previous post if you haven't).
     
  13. cygnusprojects

    cygnusprojects

    Joined:
    Mar 13, 2011
    Posts:
    767
    Ah ok, I was under the impression that shared fields could be used at runtime. My bad :sad:. I took a look at the examples and will try to fiddle something together to fit my needs. Thanks.
     
  14. opsive

    opsive

    Joined:
    Mar 15, 2010
    Posts:
    5,127
    Yeah, unfortunately they can't. I'll add to the documentation that the fields only work within the editor to try to not confuse anybody else.

    The blackboard would be perfect for what you want to do. It would be really nice to have it built into the editor and I plan on starting to add that feature towards the end of this week or early next week.
     
  15. cygnusprojects

    cygnusprojects

    Joined:
    Mar 13, 2011
    Posts:
    767
    I implemented some kind of properties class (health, target, etc) on the gameobject where the behavior designer is attached to and within my actions I reference those properties, seems to work perfectly. Thanks.
     
  16. Seith

    Seith

    Joined:
    Nov 3, 2012
    Posts:
    755
    Well here's what I'm doing:

    1. Create a behavior tree in scene1 attached to a game object which is a prefab.
    2. Click on the "Apply" button to record the changes made to the prefab into the project.
    3. Create a new scene and drag that prefab into the scene

    I tried your suggested method (click and drag into project) and that seems to work. I just wish it would work with just clicking the "Apply" button (which is usually the way you update a prefab). Do you think that could be feasible?
     
  17. Mcg

    Mcg

    Joined:
    Mar 28, 2012
    Posts:
    112
    Hi this looks great was wondering if it's possible to change a behavior tree at run time?
     
  18. Seith

    Seith

    Joined:
    Nov 3, 2012
    Posts:
    755
    I probably don't understand all the intricacies of the process but my feeling as a user is that I would love to be able to store behavior trees as prefabs (or something else, maybe an xml file) and then to be able to put them on an object. Like a tag I could slap on something. To me BTs should exist as "tangible" objects that could be referenced within an game object's component.

    But just have BTs as "fragile" components (no independent existence in the project) that are destroyed if you remove a component strikes me as a little bit unsafe. Again I'm just a user and I don't quite comprehend all it entails...
     
  19. opsive

    opsive

    Joined:
    Mar 15, 2010
    Posts:
    5,127
    Hi Mcg, thanks for your interest. Can you describe what you are looking to change during runtime a little bit more and I can give you a better answer?

    @Seith -

    I am testing the coroutine code now and I'll send it over after I reproduce (and fix) the prefab bug. In terms of saving the behavior tree, I took the same approach that PlayMaker does. When you delete the FSM component you destroy the state machine. In Behavior Designer's case when you destroy the Behavior Tree component you destroy the behavior tree. I purposely tried to hide the raw structure of the behavior tree to make things as easy as possible. Can you think of a situation where you would want the behavior tree but not the behavior tree component?
     
  20. Mcg

    Mcg

    Joined:
    Mar 28, 2012
    Posts:
    112
    I mean for example say I have a character that would be running a behavior tree that mostly contained tasks to make it move around a scene if the character was to say collide with another game object would it be possible to run a new behavior tree that contained tasks to say attack the collided game object.So it could have something like 3 behavior trees passive , offensive , defensive and allowing to swap between the 3 when needed or does everything need to be in a single behavior?
     
  21. opsive

    opsive

    Joined:
    Mar 15, 2010
    Posts:
    5,127
    You can definitely have multiple behavior trees. In the CTF sample project I do exactly that. All of the units have two behavior trees attached to them: one for when the flag is taken and one for when the flag is not taken. The behavior tree that is run depends on the flag.
     
  22. Seith

    Seith

    Joined:
    Nov 3, 2012
    Posts:
    755
    Sure. For me a behavior tree is like a recipe. And I would like to keep my collection of recipes in a book and use them on different ingredients as needed.

    So if I create a BT I'm happy with, I would like to store it somewhere so I can keep it safe. Then when I need to apply it to another type of NPC character I can go fetch it and drag-and-drop it on the NPC. Imagine the existing BTs in a list on the left side of the Behavior Designer window, a little akin to the list of tasks. You would then select a gameObject and click "Assign BT" (or something of the sort). The BT component would appear on the game object referencing the source BT. You could then remove the BT component if you decide you don't need it on that character without destroying the BT itself.

    Right now the situation is a little like the recipe lives within the cake. So if you eat the cake (delete the BT's prefab or just the BT component) the recipe is gone forever (well, you'll have to recreate it from scratch). I hope this culinary metaphor gets my point across... :)
     
  23. opsive

    opsive

    Joined:
    Mar 15, 2010
    Posts:
    5,127
    With the current version you can save a BT out and reference it in other BTs (using an external task), but when you save it out it saves out the entire component. The RTS same project has an example of this. When the original (non-external) BT goes to consume the external BT, it only looks at the root task, so in effect all of the extra component properties are wasted space. I see your point now and I'll try to add a feature that allows you to save out the serialized BT in version 1.1 (I want to get the blackboard feature out as soon as possible so I don't want to 100% commit to yet).

    In other news, I have the coroutines done and I also fixed that serialization bug. I'll send you a PM with new assemblies.
     
  24. Silly_Rollo

    Silly_Rollo

    Joined:
    Dec 21, 2012
    Posts:
    501
    This is how I would handle it as well. In my current code framework behaviors are generic and individual characters get assigned a behavior enum that is used to retrieve the appropriate behavior. Rather than have every possible action with that behavior they have fallback behaviors so something like a range attack character doesn't need to implement melee behavior it just falls to its next behavior if range can't run. This vastly simplifies bug fixing as instead of updating a bunch of prefabs any fixes to a generic behavior automatically populate everywhere.

    Haven't had time to look at this asset but looking forward to seeing if it'll work for my TBS project.
     
  25. opsive

    opsive

    Joined:
    Mar 15, 2010
    Posts:
    5,127
    With another suggestion to save the raw serialization I'll make sure it is in the 1.1 update. With the current version you can save out and reference individual BTs, but it carries the entire component instead of just the raw serialization.
     
  26. Seith

    Seith

    Joined:
    Nov 3, 2012
    Posts:
    755
    Just out of curiosity, what does this "blackboard feature" do?

    Also another question: is it possible to access a task's public variable from within a standard monoBehavior script? Usually to get access to a monoBehavior variable I would use a GetComponent<scriptComponent>() but I guess that wouldn't work that way...
     
  27. opsive

    opsive

    Joined:
    Mar 15, 2010
    Posts:
    5,127
    Sometimes it is necessary for two tasks to communicate with each other. For example, one task may determine which target to seek towards and another task does the actual seeking towards that target. Now, in this case you could have the task that determines which target to seek towards reference the seek task and tell it directly, but that method is cumbersome and not very reliable. This is where the blackboard comes in. The blackboard is basically a list of variables that the tasks can share. The task that determines the target to seek only saves the variable once, and the seek task will reference that same variable when it is doing the actual seeking. The CTF and RTS sample projects use a blackboard this exact way - to determine which transform to seek towards/attack.

    The feature that I plan on adding is basically a new tab next to the task inspector tab. Within that tab you'll be able to create different variables, and then within the task inspector tab you'll then be able to assign those variables to the public variables within a task. This will easily allow your tasks to share information.

    It is possible but not recommended. All of the tasks are store within the RootTask of the behavior tree component, and you use recursion to find the task that you are looking for but it would be a pain. If you want to access a tasks variable outside of the behavior tree the 'correct' method is to use a blackboard.
     
  28. MitchStan

    MitchStan

    Joined:
    Feb 26, 2007
    Posts:
    568
    Wow, where did this come from? From reading the docs and looking at the videos, this is really wonderfully laid out. Looking forward to playing with this. Nice job on a excellent asset.
     
  29. cygnusprojects

    cygnusprojects

    Joined:
    Mar 13, 2011
    Posts:
    767
    Hi Opsive,

    I implemented waypoint selection and moving to the selected point in a simple BT. It's a very simple BT:
    $ScreenHunter_101 Feb. 24 20.14.jpg

    But when I'm executing I notice when he reached the first waypoint idx 1, he selected idx 2 and even before starting to moving he selects idx 3. Some scenario when reaching waypoint idx 3: selected idx 4 and directly idx 5.
    The index selection is placed in the OnStart method, so it seems this method is triggered twice :confused:
    I already tried by explicitly return Success on the OnUpdate method but no avail.

    I was in the impression that when a task was executed (and returning Success) the next node in the sequencer was executed and while a node was in Running state that node was picked up again in the next execute cycle.

    Am I missing something basic?
     
  30. opsive

    opsive

    Joined:
    Mar 15, 2010
    Posts:
    5,127
    Can you turn on logging within the behavior tree component and post that? Do you have the behavior tree restarting?
     
  31. opsive

    opsive

    Joined:
    Mar 15, 2010
    Posts:
    5,127
    I appreciate it. I should have some more videos up within a day or two.
     
  32. cygnusprojects

    cygnusprojects

    Joined:
    Mar 13, 2011
    Posts:
    767
    I do have the behavior tree restarting (I checked the Restart When Complete because without this he only moves to the first waypoint and execution stops).
    Unfortunatly I can't attach the log file as it seems to big for Unity (45 kb in size but Unity only allows 20Kb max size). I'll mail it to support@opsive.com
     
  33. MitchStan

    MitchStan

    Joined:
    Feb 26, 2007
    Posts:
    568
    Just purchased and it looks great. First small issue - on a mac and I can't seem to delete any nodes. Delete key seems to have no affect. Tried option delete, command delete, option command delete as well. What is the proper way to delete a node on a mac?

    **** EDIT *****
    Figured it out - it's FUNCTION DELETE.
     
    Last edited: Feb 25, 2014
  34. opsive

    opsive

    Joined:
    Mar 15, 2010
    Posts:
    5,127
    I have a Windows keyboard for my Mac so the mapping must be different. It was only checking for KeyCode.Delete but now it is also checking for the "Delete" command.. hopefully that will work. If you let me know what version of Unity you are running I can send you the new assembly.
     
  35. Simie

    Simie

    Joined:
    Oct 26, 2012
    Posts:
    456
    This looks great! I have a question before I buy, though.

    Is it possible to build trees at runtime? For example, I have an enemy generator which can dynamically attach abilities/weapons to an enemy. I'd like to implement a branch of the behaviour tree for each ability/weapon, and combine them together to create the final behaviour tree. Does this sound like it would be possible? Thanks
     
    Last edited: Feb 25, 2014
  36. opsive

    opsive

    Joined:
    Mar 15, 2010
    Posts:
    5,127
    In that situation I think that it would be easier to use a combination of conditions/actions in order to accomplish what you want. For example:

    $bt.PNG

    If the enemy generator has a pistol then it will go to the pistol branch. In this case I just have it being a single task called "Pistol Action" but it can be a large as you want, or reference another behavior tree. If the enemy does not have a pistol it will try to rifle, followed by the grenade. At the top I am using a selector task but you could use a priority selector task for example and dynamically give a higher priority to the grenade if you wanted to. You could create a similar behavior tree / branch that focuses on the abilities.

    There would be a way to do it dynamically by modifying the external behavior tree task but you'd have to restart the behavior tree every time it changes because all of the tasks are initialized up front.
     
    Last edited: Feb 25, 2014
  37. Simie

    Simie

    Joined:
    Oct 26, 2012
    Posts:
    456
    Hi Opsive,

    Restarting wouldn't be a problem as the abilities/weapons don't change once they're set up. Could you give a quick example of how you'd add the behaviour tree dynamically? There are quite a number of possible combinations of abilities/weapons, so the if/then approach you suggested could get unwieldy pretty fast, I feel. (It's still better than the AI we have right now, so I'll probably end up using this regardless!)

    Thanks
     
  38. opsive

    opsive

    Joined:
    Mar 15, 2010
    Posts:
    5,127
    I was originally thinking that you could do something like this:

    $external.PNG

    Where each index in the array is a separate behavior tree. But then I started to think about it some more and you need access to that array up front which is possible but there isn't a straight forward way to get to it. This goes along with the blackboard feature I am going to start implementing later this week - let me work on that and I'll keep this use case in mind when designing it.
     
  39. Lexie

    Lexie

    Joined:
    Dec 7, 2012
    Posts:
    646
    Edit: Looks like you can have global behaviour types, Great work.

    I'm looking through the examples and documentation and I cant see anyway to share behaviour trees between different AI.
    I have lots of different enemy types in my game and a lot of them have the same behaviours.

    If I have a behaviour tree to patrol an area. I don't want to have to manage that same tree on every different enemy type. Is there any way to have Global behaviour tree type? The only way I can think of getting this to work is to have a prefab for each behaviour tree, then have some main controller that has references to all the prefabs and instantiates them at runtime and links it all up. But I tend to have a lot of trees and this seems like it would get out of hand pretty fast.

    Having each behaviour tree as a component seem really strange... I'm not sure if I'm just use to other behaviour packages or what. But I tend to break every group of actions into its on behaviour tree and I have a lot of those... other packages I've used have a library of behaviour trees, that way all the behaviour trees can be shared across all the AI and it's easy to manage.

    I hope I'm wrong, it looks like the best package so far and I'd like to use it. But if its like I think it is, it's going to be pretty unusable for big projects.

    Also a good feature I've seen in other behaviour tree packages is to convert a group of actions into a behaviour tree. Makes the work flow a lot easier as you constantly simplify groups of actions as your working, Seem like you have to do this manually.
     
    Last edited: Feb 26, 2014
  40. opsive

    opsive

    Joined:
    Mar 15, 2010
    Posts:
    5,127
    Hi Lexie,

    Thanks for taking a look at it. You may have seen it but have you taken a look at the external behavior tree task? With the current version you create your global behavior tree, save it out to a prefab, then reference that prefab using the external behavior tree task. In your patrol example you would only need to create one global behavior tree and then reference that behavior tree from each patrol unit. Is that what you had in mind? In version 1.1 I am going to be saving the behavior tree out as a .asset file instead of a prefab, but the workflow will be the same. I can see that there is a lot of demand for this feature.

    I like that group actions idea, I don't think that I'll get it done for version 1.1 but I'll get it done shortly after.

    In other news, version 1.0.1 was just approved and it just contains a workaround to get around a Unity bug related to LoadAssetAtPath. I am working on finishing up another bugfix release and then I'll be starting on 1.1 in a couple of days.
     
  41. Lexie

    Lexie

    Joined:
    Dec 7, 2012
    Posts:
    646
    Exactly what I was looking for. Thank you!
     
  42. opsive

    opsive

    Joined:
    Mar 15, 2010
    Posts:
    5,127
    Awesome, the RTS sample project has an example of using the external behavior tree task. I've also recorded four new videos and one of them shows the task in action but the videos are taking forever to render.
     
  43. Seith

    Seith

    Joined:
    Nov 3, 2012
    Posts:
    755
    @Opsive: Sorry I'm a bit confused regarding couroutines support within tasks; was it already included in the .dll files you sent me or is it something you're still working on? Thanks...
     
  44. opsive

    opsive

    Joined:
    Mar 15, 2010
    Posts:
    5,127
    It should already be in the DLL that I sent you. Is one not working?
     
  45. Seith

    Seith

    Joined:
    Nov 3, 2012
    Posts:
    755
    Nope, everything's fine. I was trying to use one but I had forgotten to uncomment "using System.Collections" at the start of my task file... :)
     
  46. Gronky

    Gronky

    Joined:
    Feb 26, 2014
    Posts:
    7
    I've been thinking about buying this tool (as it looks good) but I wonder about its runtime speed. It would be good to know the results of some test with thousands of dots trying to do something semi complex (I mean behavioural graph's complexity). Have you done any tests like that already? Thanks
     
  47. opsive

    opsive

    Joined:
    Mar 15, 2010
    Posts:
    5,127
    I have never tried thousands because normally you would not have thousands of agents running a unique behavior tree... In that case you'd normally split the agents into different groups and run a behavior tree on each group. But doesn't mean I don't mind trying. What kind of tree are you thinking?
     
  48. Gronky

    Gronky

    Joined:
    Feb 26, 2014
    Posts:
    7
    I actually meant using small number of unique trees each let's say 5 levels deep. Behaviours should run very simplistic actions which do more or less nothing (maybe increment some value). Then those trees would be re-used by thousands of agents in the same time. I'd like to see results for dozens, hundreds and thousands of actors executing behaviour trees to see how number of actors impacts the speed and to see how many such trees can be run in a second. Basically I'd just like to know if "plumbing" is fast :)
     
  49. opsive

    opsive

    Joined:
    Mar 15, 2010
    Posts:
    5,127
    I don't think you're going to have a performance problem with the plumbing :)

    I tested this on a PC built in mid 2011, 3.4 GHz with 16GB of ram on Windows 7 using Unit 4.3.3 pro.

    The behavior tree was really basic, just a group of sequence/selectors and a wait action. The wait action is shipped with Behavior Designer and it just waits a specified amount of time before returning success.

    $bt.PNG

    In the tests below I select the component "Behavior Manager" because the behavior manager runs all of the behavior trees.

    1 behavior tree:
    $1.png

    10 behavior trees:
    $10.png

    50 behavior trees:
    $50.png

    200 behavior trees:
    $200.png

    continued..
     
    Last edited: Feb 26, 2014
  50. opsive

    opsive

    Joined:
    Mar 15, 2010
    Posts:
    5,127
    1000 behavior trees:
    $1000.png

    2000 behavior trees:
    $2000.png

    4000 behavior trees:
    $4000.png

    I tried to see how many more it could take but Unity crashed before I could even hit play when I got up to 16000 game objects.