Wednesday, September 14, 2011

Repost: Simplifying Behaviour Tree Logic

Link: http://altdevblogaday.com/2011/09/12/simplifying-behaviour-tree-logic/

I have #AltDevBlogADay in my massive RSS subscription collection. I like to skim over the articles during my lunch break. Sometimes you can find some interesting articles on there that'll teach you something new or get you thinking about something old.

This posting by Jesse Cluff the other day was a good refresher on the finer points of Behavior Trees over [H]FSMs for behavior selection. There was one particular excerpt that I completely agreed with and wants to discuss here.

"So why use behaviour trees at all? Finite state machines are actually pretty decent (useful, well understood, and very importantly, intuitive). They have one critical weakness though. All the transitions between states are explicit."

I think he nailed it with this statement. Given a choice between a tree or a state machine, I'll always side with the tree for just this very simple reason. Most engines I've come across that have taken the route of FSM for behavior selection have suffered from the same basic problem: Inability to scale over time. I hear the argument a lot: "This NPC will be very simple and won't have a lot of state changes, so why go through all the trouble of making a large tree structure for only a few states?" If you're ever asked this, remember the appropriate response. "In 6 months time, he'll have a lot more state changes than you're thinking right now. And your 'quick, easy solution' is going to become so muddled and unmanageable, you'll want to rewrite it again and again."

The behavior FSM tends to make the "state" be the "behavior". Remember, a behavior is basically a form that takes input from the world (perception events, gameplay triggers, timers, etc.) and determines what the best way to handle that new information is. But what is required in the machine's state is also the transition rules. In the case of a behavior FSM, those transition rules are better defines as "the conditions that have an NPC change from one behavior to the next" e.g., the player has moved within 5m of me, so I need to stop attacking and start running away.

This means to satisfy the requirements of a behavior FSM, your behaviors will include the inputs needing attention as well as the conditions to change behavior. Sometimes those conditions will be "what must be true to enter this other behavior." Sometimes those conditions will be "what must be true to enter THIS behavior." But in either case, your behavior will have some set of conditions written into it, as well as a list of all other behaviors it can transition in to or out from.

And that means your behaviors aren't going to be module enough to be reused. So when that NPC starts growing and becoming more complex, you'll be modifying all of your behaviors over and over to incorporate that new complex logic. And that problem is going to scale linearly over time, becoming more and more troublesome.

Further on down the road, when you need to make a new type of that NPC (one that doesn't run away because he's braver, and he doesn't want to take cover because he has thick body armor), you're going to be tempted to reuse most of those behaviors. But to do that, you'll be having a lot of fun putting conditional checks around all those transition links and what-not to stop him from running away and taking cover.

The behavior tree on the other hand? It keeps all the transition logic outside of your "behavior" by rule of thumb, freeing your behaviors to be what they're meant to be: input/output decision lists. Need to make a new one? Just find where best it fits in the tree. No need to touch the other behaviors around it. Need to make a new archetype? Just make a new tree and reuse the behaviors you need, omitting the ones that aren't called for.

The tree is superior when you consider the practical ramifications of typical development cycles.

Kevin

4 comments:

  1. Nicely summed up. I think of how the Crysis 1 FSM-based behaviours ended up with a central switchboard of sorts, a core if-else that interpreted many of the behaviour-change signals. It was quite similar to a behaviour tree really.

    ReplyDelete
  2. Hey Kevin, this is Pavel back from Crytek :-) ... what you write here makes a lot of sense although I think it should be possible to make behaviours reusable even in the context of FSM.

    I haven't tried it full-scale so I'm not sure what problems there would be down the implementation road but wouldn't it possible to have your FSM states be just behaviour containers?

    I mean, a state would still take both behaviour inputs and transition conditions. However, it would pass the inputs to the behaviour it holds internally and run the transition decision logic itself.

    Very much like you implement a linked list using linked list nodes who keep the prev/next pointers and (a reference to) the payload. That way, the payload type knows nothing about iteration, and I would expect it to be possible to design your FSM implementation like that - what do you think?

    ReplyDelete
  3. This comment has been removed by the author.

    ReplyDelete
  4. I've never bought any of the arguments that label behavior trees as any more complex than FSMs. Where's the complexity? An FSM and behavior tree, comparatively featured, are equally straightforward to implement. Neither makes any assumption about granularity, leaving it entirely up to the developer. Neither makes any assumption about scale, either, except we know behavior trees are better equipped to handle growing complexity.

    People often cite behavior trees as inappropriate for simple characters, but it just ain't true.

    ReplyDelete