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: Behaviour trees in Unity: Behave 1.1 released

Discussion in 'Assets and Asset Store' started by AngryAnt, Sep 29, 2010.

  1. AngryAnt

    AngryAnt

    Keyboard Operator

    Joined:
    Oct 25, 2005
    Posts:
    3,045
  2. Aceblues

    Aceblues

    Joined:
    Apr 7, 2010
    Posts:
    36
    Totally awesome. Thanks a bunch for sharing this.
     
  3. south_nj_unity

    south_nj_unity

    Joined:
    Oct 23, 2010
    Posts:
    1
    Can anyone help me, I'm trying to call a sub-tree from a main tree and it never goes into the sub-tree. If I remove the sub-tree and put an action there, the action is called. Thanks, Scott.
     
  4. teatime

    teatime

    Joined:
    Jun 16, 2008
    Posts:
    129
    when i try to download the unitypackage from either the angryant site or github it downloads as "behave.unitypackage.zip." if i rename it as a proper unitypackage and try to import it, i get the following error: "Error while importing package: Package has unknown format." if i expand it as a zip file, i get a bunch of metadata folders i can't use in any way. does anyone else have this problem?

    edit: nevermind, seems like a problem with google chrome. worked fine in safari.
     
    Last edited: Oct 30, 2010
  5. Pixelstudio_nl

    Pixelstudio_nl

    Joined:
    Jun 22, 2009
    Posts:
    179
    Hey Emil,

    I saw your video in wich u use a coroutine as start and then implement the actions using reflection wich i like very much.
    I was wondering how you implement functions that are related to the fixedupdate function of unity (cause thats a fixed timestep) so for example if i have a action that applies a force to a rigidbody, would you do this in the same way you are doing it now, or would you call the actual rigibody.addforce from the fixedupdate... ?

    Cheers
     
  6. tommybear

    tommybear

    Joined:
    May 3, 2009
    Posts:
    33
    So... here is a burning million dollar question; How do I delete a connection in the tree editor in the root to a child, once it has been made? The only way I can see at the moment is to delete the child and this is overkill for me :)
     
    Last edited: Nov 13, 2010
  7. NTDC-DEV

    NTDC-DEV

    Joined:
    Jul 22, 2010
    Posts:
    593
    @AngyAnt

    I assisted to your Unite 2010 presentation (very insightful by the way, congrats !) and have since been wondering how easily Behave could be used by instructional designers to create interactive activities within Unity under an e-learning perspective? Something in the order of simple procedural activities requiring steps done by a player to achieve a specific goal.

    Example;
    - Activate object X -> Play Anim X
    - Activate object Y if X is activated, Play Anim Y
    - Play Anim Z if X Y are activated

    I haven't played with it yet but my impression so far from the comments is that it is quite complicated... please correct me if I'm wrong.

    Thank you.
     
  8. pj17903a

    pj17903a

    Joined:
    Nov 20, 2010
    Posts:
    6
    update: turns out you do need to download and install the latest Mono independent of the one with Unity- didn't see a reference to that anywhere, but oh well.

    Hi andy0305 - I'm having the same problem, could you elaborate on what you meant by installing the Mono framework fixed it? I'm using the Mono that came with UNity 3.1 and still have the problem - did you install a second instance?
    thx,
     
    Last edited: Dec 2, 2010
  9. Mingo

    Mingo

    Joined:
    Nov 14, 2010
    Posts:
    39
    Hi, loving Behave so far. I've figured out the basics and got it working with a simple AI system, but I have a question that is probably more about my lack of C# knowledge than Behave:

    Should I be attaching a script that instantiates a tree to every one of my game entities that requires it, or just instantiating a single one somewhere and controlling them all through that? If so, how would I do that? At the moment I'm including game-specific logic inside the script that's implementing and ticking the tree, and attaching this to my NPC prefab.

    Looking at the Behave Debugger I see a list of tree instances and then a sub-list of Actors that are using it. This suggests that I'm doing this horribly wrong, as I'm going to have 300+ trees and only one Actor using each if I carry on like this. Is that likely to cause issues? An advantage is that the ticking of each will be staggered as I only need to tick a few times per second, but is the overhead of 300+ instances of a Behave tree something I should fix before I get carried away?

    Thanks for any help!
     
  10. Mingo

    Mingo

    Joined:
    Nov 14, 2010
    Posts:
    39
    And another question: how am I supposed to use Priority Selectors? I understand that I'm returning a choice inside SelectTopPriority(), but how am I supposed to know which selector is being executed if I have more than one? If I try to get the context with sender.ActiveContext it doesn't seem to work how I'd expect (or at all).
     
  11. Redz0ne

    Redz0ne

    Joined:
    Oct 19, 2010
    Posts:
    332
    this looks like it would fit the bill quite nicely for the AI systems i'm trying to build for my own game.

    though, final answer, does it matter if it's C# or Java or can either be used with this? asking because i don't know C#.
     
  12. bigdaddio

    bigdaddio

    Joined:
    May 18, 2009
    Posts:
    220
    I wish I could learn more about this business, it all seems pretty cool but at the moment the only place that seems to have info is going to charge me $75.

    I am wondering if I am using it correctly. I set up a tree, it has one selector and 4 actions, idle, detected, chase, retreat. so my enemy dude just hangs out in idle. If the player gets close enough it drops to the detected action. If the player gets closer, the enemy dude gives chase, if however you get to close they retreat maintaining a distance between the player and the enemy. I Think it's pretty cool and really was not hard to set up.

    Here is my issue.
    Every cycle it ticks idle, fine, but then if the player is close enough, idle fails and it ticks the detected action. however it also ticks idle. basically it ticks each action in sequence until it succeeds. yes I guess that is what it is supposed to do. Is that really how I do it? It doesn't seem to be more than 3 if statements.

    I would think that somehow like a FSM that it would lock into the true state and not even tick the others, or have I done something wrong.

    BTW I really am impressed with your work and was entertained by your video, it really helped me lots.
     
  13. AngryAnt

    AngryAnt

    Keyboard Operator

    Joined:
    Oct 25, 2005
    Posts:
    3,045
    Whoa. Not really sure why I've not received any notifications on responses here. Sorry about that.

    @bigdaddio
    With a component selected, you should be seeing a description field of that component at the bottom of the inspector. Does that help?

    @Red:
    Any Unity supported .net language should work just fine.

    @Mingo:
    Active context can be set on each component - in the inspector. How is this not working the way you expected it to?

    Regarding number of tree instances, that is of-course highly case specific, but judging by your description, I'd say you should have one tree per simulated agent.

    @pj17903
    That's odd. It shouldn't be required. I'll look into it.

    @PolishRenegade
    Not sure I completely understand what you're going for, but it sounds completely doable.

    @TommyBear
    Connections can be deleted from the inspector when an output node is selected. For the root connection, though, you unfortunately need to delete the receiving node. This is changing for 1.3.

    @pixelstudio
    That is highly implementation specific. It's hard to guess your scenario, but you can tick a tree from any bit of code you like.

    @longshot
    Sounds like expected behaviour to me. Evaluation of a parallel always happens at the end of the parallels tick - after all children have been ticked. I've updated the description of the parallel node for 1.3.

    Thanks for the explanation help by the way. Documentation is not exactly my favorite chore ;) I'll see if I can't update it soonish.
     
  14. bigdaddio

    bigdaddio

    Joined:
    May 18, 2009
    Posts:
    220
    Yes I have seen that the descriptions are shown. I guess that I could use more documentation. Especially code examples.

    As an in my example I have an AIUpdate method like you show in your video.
    void AIUpdate()
    {
    Debug.Log("AIUpdate.");
    m_Tree.Tick ();
    }

    Now when I tick this posts that it has ticked. I set my frequency to 4, it seems that it ticks like this

    AIUpdate
    Action1
    AIUpdate
    Action2
    AIUpdate
    Action3
    etc.

    So with 4 actions does that meant that if I fall through to the last action it effectively gets ticked once per second? And should it even tick Action1 and Action2 if Action3 is true? How would I make it Tick only Action3 and i would think that when Action3 goes false it should reset and run through the list.

    It then seems that the priority selector and regular selector are effectively the same, I do not know how to change an action's priority.

    If you need to get a subscription it is no longer the default, you need to go to the advanced reply ;)
     
    Last edited: Jan 5, 2011
  15. AngryAnt

    AngryAnt

    Keyboard Operator

    Joined:
    Oct 25, 2005
    Posts:
    3,045
    This all really depends on your tree structure. I really think you should re-read the description of the various flow control components to find the right one: Sequence / Selector / Priority selector / Parallel.

    Like its description says, the priority selector works by querying the agent about the top priority out of a given set of priority options - via the SelectTopPriority. This makes it radically different from the regular Selector, which just tried to tick the leftmost outgoing connection and, given failure, continues towards the right - until success is returned by an output.

    Again, all of this is in the component descriptions. I would also recommend watching the video on AIgameDev which I link to from the Behave documentation.
     
  16. NTDC-DEV

    NTDC-DEV

    Joined:
    Jul 22, 2010
    Posts:
    593
    Sorry for being unclear, let me rephrase.

    As an example of what I mean, lets say a user (player) in a first person perspective has to accomplish different tasks in a virtual environment (activate X objects, in a specific order with GUI feedback to accomplish an Y goal).

    Can your framework be used by a non-programmer to create the Behavior Graph (the logic and conditions) and then given to a programmer to actually integrate the procedure inside the game?

    Something like creating a development pipeline where an instructional designer (expert of creating educational activities) uses Behave to make the conditions and logic. Gives that to a developer who adapt the script to interact with the objects inside the virtual environment.

    Does it make sense to use Behave in that manner?
     
    Last edited: Jan 7, 2011
  17. AngryAnt

    AngryAnt

    Keyboard Operator

    Joined:
    Oct 25, 2005
    Posts:
    3,045
    It is definitely possible, but very much depends on your exact case. From this stand point, I'd recommend you go through some material on Behaviour trees to see if the method fits. I'd recommend starting off with the AIgameDev video I link to from the Behave documentation and then go look for more written material.
     
  18. psyclone

    psyclone

    Joined:
    Nov 17, 2009
    Posts:
    245
    Maybe just create an example unity project we can look at, as a quick way of documenting :)
     
  19. Werit

    Werit

    Joined:
    Dec 12, 2010
    Posts:
    38
    I need a little help getting my head around this :)

    Say I wanted to do a simple AI, that would do to things: Move from point A to B, and do some action when attacked.

    Some external entity would tell the object where B is via some means.

    In the tree, it would have a check ShouldIMove() where it would look for the info.

    If there, it would then go to another action Move() where I would need to put movement code. Should I use functions like Lerp/Slerp there?

    Since moving from A to B will take an amount of time, would it have to keep going down the same path until complete?

    Lets say the object comes under attack (or some other 'event'), it sounds like I would need another check prior in the tree

    Apologies for the improper vocabulary. I did watch the aigamedev videos, but it is all a bit too much at the moment :) Any help on how to structure these simple tasks would be appreciated.
     
  20. newmonian

    newmonian

    Joined:
    Dec 24, 2010
    Posts:
    3
    Just trying to figure this out now and it seems to be quite the impressive tool, but an updated example project would be more helpful (to me at least) than any documentation will ever be. Any chance of one showing up in the near future?

    From what I've seen of it so far it's an awesome tool to make behavior trees simple, fantastic work.
     
  21. Werit

    Werit

    Joined:
    Dec 12, 2010
    Posts:
    38
    Ok, think I can better describe what I am asking....

    To move from A to B, there needs to be a few steps.

    1) Rotate to face the dest (over time)
    2) Actually move, using translate (over time)
    3) Check if at Destination and stop

    How should a tree which does this be built?
     
  22. chirhotec

    chirhotec

    Joined:
    Mar 30, 2010
    Posts:
    47
    Hi, I'm new to Behave and Behavior Trees in general. I have gone through the AiGameDev video on BTs, and the video tutorials for Behave, and have been able to follow Emil's code example.

    However, I still haven't quite been able to apply it to my situation yet, probably due to my inexperience with Behavior Trees. Does anyone know of any more resources that show some explicit examples of creating desired behaviors with BTs?
     
  23. Redz0ne

    Redz0ne

    Joined:
    Oct 19, 2010
    Posts:
    332
    AA: from what i can tell playing around with it, is it just a matter of making little scripts (like... "check if in front of player" "check if within distance") and then going on to the next behavior? also, i don't know about the rest but it seems a little complex to implement it... maybe i'm not doing it right (i KNOW i'm not doing it right) but is there a step-by-step or some reference about how to implement it for someone that's not all that good with coding? (like, "using 'behave' for newbs?")
     
  24. Werit

    Werit

    Joined:
    Dec 12, 2010
    Posts:
    38
    Is there anyway to detach a child node in the tree editor?
     
  25. AngryAnt

    AngryAnt

    Keyboard Operator

    Joined:
    Oct 25, 2005
    Posts:
    3,045
    @spaceshooter
    Like @chirhotec, I believe what would benefit you the most would be some more general behaviour tree information. In general AIgameDev.com has a lot of really good articles and examples on the subject, but Ricardo (arges) from the community has also written a few Behave specific examples.

    From a quick googling on "behaviour tree examples" I found this one from him:
    http://www.arges-systems.com/articles/13/behavior-trees-implementing-team-players/

    Although it is using an old version of Behave, the behaviour tree design is fully valid.


    @psyclone, @digitalConundrum and @Red
    Yea I know I'm behind on the whole example front. Sorry about that - with little time on my hands I've been focusing on bugfixes and features over docs. I'll see what I can do about an example project.

    Although, @Red, you do have the right idea.


    @spaceshooter
    Outgoing connections can be rearranged and removed in the inspector when an output node is selected. A lot of people have been overlooking this, so in the next release of Behave, I've made this more clear.
     
    Last edited: Jan 18, 2011
  26. Werit

    Werit

    Joined:
    Dec 12, 2010
    Posts:
    38
    @AngryAnt Thanks!

    I'm also curious about how to best make tree's reusable. So I did make a Movement Tree, this should be used by other tree's. I didn't see a way to link to another tree in the editor, am I just missing it or is there a different way to go about it?
     
  27. AngryAnt

    AngryAnt

    Keyboard Operator

    Joined:
    Oct 25, 2005
    Posts:
    3,045
    @spaceshooter
    Use the reference component. It is available in the far-right end of the component bar at the bottom of the tree editor. The drop-down below it allows you to select which tree the reference should handle.
     
  28. Werit

    Werit

    Joined:
    Dec 12, 2010
    Posts:
    38
    I don't seem to have that component. Decorator is at the far right bottom of my editor window. It is version 1.2.
     
  29. AngryAnt

    AngryAnt

    Keyboard Operator

    Joined:
    Oct 25, 2005
    Posts:
    3,045
    @spaceshooter
    This means that you only have one tree within your current collection. You can add another tree to the collection by selecting it in the Behave browser and choosing "tree" in the "create" drop-down.

    Alternatively if you already have a subtree in your tree editor which you would like to transform into its own tree, so that it may be referenced by other trees, you may do so by right-clicking the topmost node of that subtree and clicking "form new tree".
     
  30. Werit

    Werit

    Joined:
    Dec 12, 2010
    Posts:
    38
    I did manage to make a simple movement behavior tree. Maybe it will help others learning, so here is a picture of it:



    WaitForOrder and EventCheck allow the object to be told what to do and respond to events (like being attacked).

    FaceDest rotates the object

    AmNotThere checks if the object has arrived at the destination. I'm not really happy with this one yet, since it feels a bit hackish. It is a Decorator which returns success when not at the destination, so Move is ticked. When at the destination, it returns a Failure so we do not go down to move. At that point it goes back to WaitForOrder.

    I think there is probably a better way to handle AmNotThere, since it isn't really failing. I think passing a failure result up is going to cause me some problems if I want to do something after moving.

    Suggestions welcome :)
     
    Last edited: Jan 18, 2011
  31. Werit

    Werit

    Joined:
    Dec 12, 2010
    Posts:
    38
    Aha, that did it.

    How do you recommend the code behind the tree be stored for re-usability (TickMoveAction() for example)?
     
  32. AngryAnt

    AngryAnt

    Keyboard Operator

    Joined:
    Oct 25, 2005
    Posts:
    3,045
    That is really all up to you. Behave gives you many options via its close tie-in with your code and its facilitation of references.

    For references, see Tree.Set*Forward at http://angryant.com/behave/documentation/tree. The close code tie-in enables you to for instance define some handler on a base class and then inherit from that class - adding more handlers and more specific functionality.
     
  33. chirhotec

    chirhotec

    Joined:
    Mar 30, 2010
    Posts:
    47
    For high-level planners, a very low frequency is sometimes desired. I'm trying to set my frequency to 0.2 (once every five seconds); however, the following error pops up in Unity when I try to compile the Library (I also tested it for a float value greater than 1, and had the same result):

    Compile Error
    Internal compiler error

    Strangely, no error shows up in the debug log.


    Also, how would the system behave if you use a sub-tree with a different frequency value?
     
  34. AngryAnt

    AngryAnt

    Keyboard Operator

    Joined:
    Oct 25, 2005
    Posts:
    3,045
    @chirhotec
    The compile error you are getting is a known issue which is fixed in the next release of Behave. the current workaround is to use integral frequency values:

    https://github.com/AngryAnt/Behave-release/issues/8

    As mentioned in the tree documentation, the frequency value has no direct impact on the predefined Behave runtime. It is merely there for designer convenience. I believe I show an example of how to make use of it in one of the videos:

    http://angryant.com/behave/documentation/tree
     
  35. chirhotec

    chirhotec

    Joined:
    Mar 30, 2010
    Posts:
    47
    @AngryAnt: Thanks for your timely response yesterday; And I'll try to be more diligent in checking the issues next time around.



    I've been reading through all the Arges tutorials and related stuff on AI Game Dev, but am still stuck conceptually on what I'd think would be a relatively common task.

    I'd like to have the NPC AI switch between behavior subtrees based on proximity to the player. For a simple example, I have a character with two main behaviors, Idle and Presentation, neither of which are just one action (though I've represented Presentation as just an action, until I expand it later on). When the player moves out of range, the NPC will go into its Idle behavior. When the player moves into range, the NPC should go into the Presentation behavior. Either way, if the proximity condition every changes, that subtree should abort and switch the other subtree.

    Images of all of these tree are attached, below.

    I think these should be combined with a Selector, since only one of them should run. In this example, a sequence would work since you are always switching between two disjoint subtrees, but it wouldn't expand well for future added behaviors.

    I've come across three possible solutions to the problem:

    Check Proximity in every Action

    Arges System's tutorial (shift in focus section) suggests checking the proximity condition within the Tick of the Action that may need to abort. I'm sure this would work, but I haven't tried this approach because it seems this is something that should be handled by the tree structure and because it makes it harder to maintain / reuse the code once you start adding those checks all over the place.

    Decorator Approach

    For the decorator approach, I created a simple "EnsureProximity" decorator that returns Success if the player is within range, and Failure if the player is not within range. I figured I could use this for both subtrees by inverting the result when the actions should run only if the player is out of range.


    When I run it, TickEnsureProximityDetector detects the user is not within range, returns failure and doesn't go into the subtree. However, that is considered Successful to the selector, so it thinks its done with everything, and restarts the tree.

    I also tested it with a Sequence instead of a Selector, however, it didn't work either, but for slightly different reasons. The first EnsureProximity successfully prevents the subtree from running. However the second returns Failure in the Tick method, so its subtree is not ticked. It then returns Success (task completed), but that is inverted before the Sequence sees it, making the sequence fail out.

    I guess I don't understand why the decorators / inverters work the way they do; or at least not how to effectively apply them to perform this task.

    Assertions via Parallel Combiner

    AiGameDev.net suggests on the BT video tutorial and on their page about parallels, that parallels are good for this kind of assertion checking.

    I tried that method too, but couldn't get it to work properly either. Unfortunately, I can't remember why anymore, my brain is a little fried and I have to leave the office.

    I think it had to do with the EnsureProximity action only being ticked the first time the parallel is ticked. After it passes that first time, the subtree is ticked and it has to complete all of its work before the EnsureProximity will be checked again. (I guess I was expecting EnsureProximity to tick every time that sub tree is ticked).


    Anyways, if anyone has any suggestion on how to fix either of these approaches (or both, as a learning example), it would be greatly appreciated.
     

    Attached Files:

  36. AngryAnt

    AngryAnt

    Keyboard Operator

    Joined:
    Oct 25, 2005
    Posts:
    3,045
    Yea decorators + inversion + tick return value works differently than it does for actions (which is why I have separate tick handlers for actions and decorators). You would have to do the inversion in the implementation - not on the component. If you want control over it via the tree editor, you could make the behaviour of the implementation dependent on the decorator parameters (you can specify a string and float parameter in the inspector). However the simplest solution would probably be to just have a WithinRange and NotWithinRange implementation.

    To have your tree looping - that is re-start once it finishes, you need to instruct it to do so from your implementation. Here's an example of how that could be done:

    Code (csharp):
    1.     Tree m_Tree;
    2.    
    3.    
    4.     IEnumerator Start ()
    5.     {
    6.         m_Tree = BLMyBehaveLibrary.InstantiateTree (BLMyBehaveLibrary.TreeType.MyCollection_MyTree, this);
    7.         while (Application.isPlaying)
    8.         {
    9.             yield return new WaitForSeconds (1.0f / m_Tree.Frequency);
    10.             AIUpdate ();
    11.         }
    12.     }
    13.    
    14.    
    15.     void AIUpdate ()
    16.     {
    17.         if (m_Tree.Tick () != BehaveResult.Running)
    18.         {
    19.             Debug.Log ("Reset");
    20.             m_Tree.Reset ();
    21.         }
    22.     }
    The reason why your parallel efforts are not working out is because you think of it in terms of sequences and selectors. If you want a child of a parallel to be constantly ticked, that child should be returning the result of Running. Once you want a child to influence the parallel towards terminating, that child should return Success or Failure.

    If you want a single child to be able to terminate a parallel, you should configure that parallels termination to be dependent on one child returning - not all. In your specific case, your parallel conditions should return Running until the point where they are no longer valid. Then they should return Failure or Success - whatever makes sense. Your parallel should then be set up to terminate once a single child terminates. This will result in parallel termination once any of the conditions or the tasks returns non-running.
     
  37. chirhotec

    chirhotec

    Joined:
    Mar 30, 2010
    Posts:
    47
    My current implementation of AIUpdate (what you provided in the Tree to Code video) already repeats. I just assumed that if you ticked a completed tree it restarts.

    The fix you suggested to the parallel implementation made a lot of sense, and cleaned up some of my understanding about the system. The only issue now is that on each given tick, all children are ticked even if a tick to the left is completed (and the component completion is set to One). Maybe this could be an option, so that parallels can be used for assertions?

    As for the decorator approach, I created two decorators to avoid the added complications of inverting the results. And I was pleasantly surprised to see that the decorator is ticked every time any descendant block is ticked. However, the underlying issue still exists: With no way for a decorator to return Failure by itself, I don't see how it can be used for assertions.
     
  38. AngryAnt

    AngryAnt

    Keyboard Operator

    Joined:
    Oct 25, 2005
    Posts:
    3,045
    I'll take the suggested option for parallels into consideration. Thanks for bringing it up. And yes, validation does indeed happen *after* all children have been ticked. In Behave 1.3, I've clarified the Parallel description some more.

    Decorators are indeed not suited for assertions, but rather interrupts and loops. Use sequenced actions for assertions.

    Glad you're finding the product useful :)
     
  39. chirhotec

    chirhotec

    Joined:
    Mar 30, 2010
    Posts:
    47
    Very useful indeed. And thanks for checking the thread and helping out so much!

    Does this mean that the Init {Name} Decorator function should only be called the first time that its reached in a branch (unless it exits, and the branch is reached again)? Because it seems like the decorators are being reinitialized if the children complete successfully.

    In my case I want a Decorator that will repeat an action over time. I initialize the time in the InitRepeatForTimeDecorator and return success. In the TickRepeatForTimeDecorator, I check to see if the time has lapsed, returning Success if it has, and Running if not.

    However, when the children runs and returns Success, it triggers the whole thing to restart. So the RepeatForTime gets reinitialized, the timer gets reset, and it never breaks out.

    Here's my Decorator code, for whats its worth:

    Code (csharp):
    1.  
    2.     public BehaveResult InitRepeatForTimeDecorator (Tree sender,
    3.         string timer, float time, IAgent agent, object data)
    4.     {
    5.         endTimes[timer] = Time.time + time;
    6.        
    7.         DebugPrintTick("Init", "Decorator", "RepeatForTime",
    8.             timer + " from " + Time.time + " to " + endTimes[timer]);
    9.         return BehaveResult.Success;
    10.     }
    11.    
    12.     public BehaveResult TickRepeatForTimeDecorator (Tree sender,
    13.         string timer, float floatParameter, IAgent agent, object data)
    14.     {
    15.         DebugPrintTick("Tick", "Decorator", "RepeatForTime",
    16.             timer + " ? " + Time.time + " > " + endTimes[timer]);
    17.  
    18.         if (endTimes.ContainsKey(timer))
    19.         {
    20.            
    21.             if (Time.time < endTimes[timer])
    22.                 return BehaveResult.Running;
    23.             else
    24.                 return BehaveResult.Success;
    25.         }
    26.         else        
    27.         {
    28.             return BehaveResult.Failure;
    29.         }
    30.        
    31.     }
    32.  
    This could be accomplished by adding code to the action that also checks the timer, or adding another action that runs in parallel and stalls until the timer is finished. However, both of those seem like its adding unnecessary complexity and making the components less reusable.

    Are there any concrete examples of using decorators to accomplish some standard operations, like Repeating child for a time period, delay the return of child until time has lapsed (holding, without repeating), allowing child to run for a maximum amount of time? (Also, checking for a flag to be triggered, or running a certain number of times, but those are just variations on the above, replacing time with a different condition)
     
  40. chirhotec

    chirhotec

    Joined:
    Mar 30, 2010
    Posts:
    47
    I think I actually figured out most of these, though some of them took an extra block to get the functionality just right.

    But I am still curious about whether that issue with the Decorator restarting is a bug, or by design.
     
  41. AngryAnt

    AngryAnt

    Keyboard Operator

    Joined:
    Oct 25, 2005
    Posts:
    3,045
    This is an (untested) example of how I would do a simple repeater:

    Code (csharp):
    1. public BehaveResult InitRepeatDecorator (Tree sender, string stringParameter, float floatParameter, IAgent agent, object data)
    2. {
    3.     if (repeaters[stringParameter]++ > floatParameter)
    4.     {
    5.         return BehaveResult.Failure;
    6.     }
    7. }
    8.  
    9.  
    10. public BehaveResult TickRepeatDecorator (Tree sender, string stringParameter, float floatParameter, IAgent agent, object data)
    11. {
    12.     return BehaveResult.Running;
    13. }
    14.  
    15.  
    16. public void ResetRepeatDecorator (Tree sender, string stringParameter, float floatParameter, IAgent agent, object data)
    17. {
    18.     repeaters[stringParameter] = 0;
    19. }
    A bit later I could look at how your time based repeater could be adapted to work similarly to this.
     
  42. AngryAnt

    AngryAnt

    Keyboard Operator

    Joined:
    Oct 25, 2005
    Posts:
    3,045
    Moved to the new Assets forum section.
     
  43. Minassian

    Minassian

    Joined:
    Nov 22, 2010
    Posts:
    40
    Hi.
    Thanks a lot for this AA, I'm reimplementing the IA of my game from scratch with this and everything is looking much cleaner and extensible.
    Still struggling on getting a few behaviours done but that's only my inexperience with BTrees.

    I have a question about the interface.
    How can I recreate a link (with the objective of reordening childs) without having to erase nodes ?
     
  44. AngryAnt

    AngryAnt

    Keyboard Operator

    Joined:
    Oct 25, 2005
    Posts:
    3,045
    Hey Minassian. Great to hear you find good use of my tool. Connection rearranging is not very easy to spot in the current version of Behave -this is fixed in 1.3.

    To rearrange connections in 1.2, select the output node with the outgoing connections you wish to rearrange and then in the inspector, select a connection in the connection list and hit the tiny up-arrow above the connection list to move it upwards in the list.
     
  45. Minassian

    Minassian

    Joined:
    Nov 22, 2010
    Posts:
    40
    Ah missed it !
    Easy enough, thx.

    I have a few design questions but I'm gonna read the hole thread to avoid reasking.
    Cheers.
     
  46. Minassian

    Minassian

    Joined:
    Nov 22, 2010
    Posts:
    40
    I can't believe I wrote a huge post and lost it because this forums does not allow to open a link in a tab.
    Anyway, catarsis out of the way, here I go again.

    I think I have a similar problem to chirhotec, I'm still figuring out how the best way to implement events.
    Checking proximity in every action is out of the question for me. I woulnd't know how to implement it in a maintanable way.
    Haven't digged seriously into the decorator approach yet.
    Here's my shot at assertions via parallel combiner.
    I'm trying to make a vehicle wander around the map, and escort another vehicle when it enters its radius.
    If this other vehicle stops being escortable for some reason, it'll return back to wandering.
    Parallel is configured with 'One' component completion, and 'Success' child completion.



    This is the pseudo code of the escorting methods:
    Code (csharp):
    1.  
    2. // This method's responsibility is to look for escortable targets in receivers area of sight.
    3. // Any target receiver may have is reseted.
    4. // This method cannot fail, only success and running results will be returned.
    5. public BehaveResult TickLookForEscortableTargetAction(Tree sender)
    6. {
    7.     if(target != null) resetTarget();
    8.     if(TickSeeEscortableUnitAction(sender) == BehaveResult.Success)
    9.         return BehaveResult.Success;
    10.     else
    11.         return BehaveResult.Running;
    12. }
    13.  
    14. // Returns wether the receiver sees any escortable units.
    15. // If return value is a sucess, then receivers target will be set to the escortable target seen.
    16. // If a failure is returned, then target is not changed.
    17. // This never returns a running result.
    18. public BehaveResult TickSeeEscortableUnitAction(Tree sender)
    19. {
    20.     if(target != null  target.canBeEscorted()) return BehaveResult.Success;
    21.     newTarget = getNearEscortableUnit();
    22.     if(newTarget == null)
    23.         return BehaveResult.Failure;
    24.     else
    25.     {
    26.         target = newTarget;
    27.         return BehaveResult.Success;
    28.     }
    29. }
    30.  
    So its basically the same method but conceptually changed the result value, reusing the code.
    By returning the current target if its escortable in TickSeeEscortableUnitAction() I ensure not to look it up twice when going from wandering to escorting.

    This works pretty well so far, but I'm not sure yet how extensible it is to more complex behaviours, or if it's the most correct way of doing it.
    I don't like the fact of having two nodes (functions) for the same thing, I feel that somehow that would need to be delegated to the tree.
    Sugestions welcome.
     
    Last edited: Feb 2, 2011
  47. chirhotec

    chirhotec

    Joined:
    Mar 30, 2010
    Posts:
    47
    @AA, meant to ask this earlier, is this a good place for general discussion of how to implement BTs with Behave, and best practices, or should we split to a different thread, and keep this one focused on release notes?

    @Minassian, having only begun doing game AI in the last couple weeks I'm no expert, but here's how I did mine with decorators:



    Note that the use of the series component depends mostly on where this component fits in with the rest of the Behavior tree. Since the two branches are mutually exclusive, and these decorators never return Success (either Failure or Running), any of the options will work most of the time.

    Both decorators work essentially the same way. You provide a flagName bool value to check against, and they return Running while that boolean is set to true or false. After the boolean switches to false or true, the decorator halts the child by returning Failure (which is regarded as success to the decorator's parent, btw). You can do the actual checking of that boolean value somewhere else, either in the update / fixedupdate function, external function calls to your object, etc.

    Here is the returned result based on what the decorator and child return each tick. Note that in my code, I actually call these "RepeatWhileFalse" and "RepeatWhileTrue". This makes it more explicit that if your child returns Success or Failure, and the decorator is still Running, your child will automatically restart, even if you only mean it to be a one time thing.


    The WhileTrue is similar with the conditions reversed.

    Additionally, when the child repeats, the decorator is actually restarted as well. For this reason (and because I do the variable checking / setting in other places), I don't initialize the flag to any value in the Init<X>Decorator function, unless the flag doesn't yet exist. Instead, if you need to ensure a value is set to true or false before that section is called, use SetFlag / ClearFlag Actions in your BT.

    If, instead of repeating the child, you wanted to do something where you run the child only once and stall until the flag is tripped, you'd need something like this:



    You can see the implementation in the attached code file, BaseAgent.cs. The main things to note are: (1) I had to rip out a lot of my project code, and I didn't test it afterward, so you'll need to do some clean up to make sure it compiles. (2) To keep things generalized, I use a dictionary to keep track of flagName / boolean value pairs, and passed in the string name as a decorator. (3) Since there are a lot of decorators / actions that I reuse, I created the BaseAgent to store common pieces. You'll need to create an agent that subclasses it and adds the extra functionality for that particular agent.

    For the last week or two I've been meaning to write up a tutorial on what I've learned about implementing things with BTs and decortors using Behave, but I've been swamped lately, and at the earliest, I wont have time until this weekend. In the mean time, I hope this helps. And thanks for the enjoyable diversion from work :)
     

    Attached Files:

    Last edited: Feb 2, 2011
  48. AngryAnt

    AngryAnt

    Keyboard Operator

    Joined:
    Oct 25, 2005
    Posts:
    3,045
    @chirhotec:
    I think as long as people check the available resources first - the aiGameDev video, this thread and such before posting, general BT design questions are just fine in here.
     
  49. ratamorph

    ratamorph

    Joined:
    Sep 2, 2007
    Posts:
    458
    I have a workflow question.

    Say there's more than one person working on a single behave library (more than 1 AI designer making trees and collections). How can I keep the library on sync on the asset server so that the server version reflects all of the changes made by the designers?

    I have't found a good way around this since the library is not a text asset so it won't let me diff or merge.
     
  50. Five Elements

    Five Elements

    Joined:
    Feb 11, 2011
    Posts:
    1
    I was trying to use behave 1.2 in unity 3.1 (with iPhone basic license). I created one tree in a collection by following the video tutorial (http://angryant.com/wp-content/uploads/2010/09/Behave-starting-from-scratch.mov). Everything went well, but when I clicked the "building library debug" button, I got the following error:

    System.SystemException: Error running gmcs: Cannot find the specified file
    at Mono.CSharp.CSharpCodeCompiler.CompileFromFileBatch (System.CodeDom.Compiler.CompilerParameters options, System.String[] fileNames) [0x00000] in <filename unknown>:0
    at Mono.CSharp.CSharpCodeCompiler.CompileFromSourceBatch (System.CodeDom.Compiler.CompilerParameters options, System.String[] sources) [0x00000] in <filename unknown>:0
    at Mono.CSharp.CSharpCodeCompiler.CompileAssemblyFromSourceBatch (System.CodeDom.Compiler.CompilerParameters options, System.String[] sources) [0x00000] in <filename unknown>:0
    at System.CodeDom.Compiler.CodeDomProvider.CompileAssemblyFromSource (System.CodeDom.Compiler.CompilerParameters options, System.String[] fileNames) [0x00000] in <filename unknown>:0
    at Behave.Editor.Compilers.Unity.Compile () [0x00000] in <filename unknown>:0
    UnityEngine.Debug:LogError(Object)
    Behave.Runtime.Resources:LogError(String)
    Behave.Editor.Compilers.Unity:Compile()
    Behave.Editor.Compiler:Run()
    Behave.Editor.Compiler:DoCompile(IBehaveAsset, Boolean)
    Behave.Editor.Compiler:Compile(IBehaveAsset, Boolean)
    BehaveMenu:Compile(Boolean) (at Assets/Behave/Editor/BehaveMenu.cs:55)
    BehaveMenu:Compile() (at Assets/Behave/Editor/BehaveMenu.cs:31)
    BehaveAssetEditor:Update() (at Assets/Behave/Editor/BehaveAssetEditor.cs:34)
    UnityEditor.EditorApplication:Internal_CallUpdateFunctions()


    I searched this thread and found that andy0305 and longshot had got the same problem, Could you tell me how you resolved it? thanks.