Advertisement

Over-stuffed AI Models in Game Dev

Started by April 21, 2021 08:16 PM
8 comments, last by hplus0603 3 years, 7 months ago

Hey, folks! Something I'd love your opinion on:

A mistake I've seen at a few of my jobs, is game engines trying to use a single ai approach to do too many different things.

For example, one of my employers released a pretty well-known FPS game a few years ago, in which enemies could do things like: engage with the player, choose their own targets, choose their own cover, etc. They were able to string together lists of behaviors in response to a situation, which was pretty dang cool! For instance, if they were backed into a corner, they might make a plan for how to escape. And that plan might be different, based on the player's weapon, etc.

These enemy agents used Behavior Trees for their ai…but I mean for all of their AI. For instance, every possible escape plan they could make was hand-coded as a branch of their behavior tree. BT logic was even used to make them choose their next goal (things like attack, heal, etc.), based on what was best for them at the moment. All of it was technically possible to do with a BT, but it was very difficult to do, and the resulting BT graphs were very complex, and very difficult to manage.

I see this as a bad habit, but one that I think a lot of devs just don't think about. Their game uses the one model (in this case, BTs), so that's the model they're going to use to solve every ai problem in their game.

The challenge is - BTs probably weren't the best tool to solve some of those problems. NPC planning could probably have been done more simply using GOAP, or another type of action planner. Goal-formation might be handled much better by a Utility AI. The devs were really twisting themselves in knots to make a Behavior Tree graph handle some of those needs. And I saw it result in a lot of compromised features and crunch.

I've seen the problem pop up on another team, but this time every ai problem was solved with finite state machines - again, because that's just the model that team was using for everything. Maybe the toughest example of this I've heard was an indie team, who was trying to solve every AI problem with a Utility AI. That included navmesh pathfinding and behavior states.

I'm more of the mind that different ai models can be used to handle different ai responsibilities. For example, your game might use a finite stat machine to organize and execute behaviors. But if it needed an agent to make a decision - say, to choose between 10 possible goals - then that FSM could call outside of itself to a separate Utility AI. That Utility AI could weigh those options, and return a result to the FSM, which could then respond to it accordingly. Similarly, if the agent then needed to make a plan to reach that goal, the FSM could pass that goal to an external GOAP system, which would hand back an action plan.

I'm wondering what you all think of that type of approach, and if there's a reason why you wouldn't want to use it. Also, have you seen this type of problem where you've worked, or elsewhere? I feel like maybe the industry teaches new folks bad ai patterns when they work on projects that over-use ai models like this.

Cheers! Thanks for any opinions.

They must be understandable by those who use them. A mistake outsiders make is that they assume complex == good. They don't realize -- and sometimes veterans forget -- that the primary goal is fun. If designers cannot tune them to maximize fun, they're a bad fit.

I have a problem with your description in how you keep them as distinct, when all of them are concepts with overlap. Each concept is independent of the others. Behavior trees can be implemented as state machines or as other models, and utility functions can be implemented as transitions between nodes, so ultimately all three can be implemented as a single implementation. Goal oriented systems try to reach that goal by computing the utility of various actions toward reaching the goal, and the steps involved are state machines. Repeat however you want, they're all different components of the same thing.

Ultimately whatever system you develop needs to be adjustable by designers and other implementers. In that regard each of those concepts is highly useful to the game industry. They stand in stark contrast to AI techniques like recognizers, backprop networks, RFBs, and similar that can be used to recognize inputs but are terrible at creating gameplay. Those systems can also be used in games, and can be quite useful in systems like recognizing gestures or voice control or other signal processing. But they're the wrong tools for game design, which historically works best with simple trees that can be graphically manipulated, or utility functions where designers can adjust numbers to see how the weights are calculated.

Use the tools that actually solve the problems you have. If the designers are comfortable using a technology, and the technology solves a problem, I recommend against using switching merely because it is new and shiny, only because it solves a real problem better than what you already have.

Advertisement

Good stuff, thank you.

frob said:
I have a problem with your description in how you keep them as distinct, when all of them are concepts with overlap. Each concept is independent of the others. Behavior trees can be implemented as state machines or as other models, and utility functions can be implemented as transitions between nodes, so ultimately all three can be implemented as a single implementation. Goal oriented systems try to reach that goal by computing the utility of various actions toward reaching the goal, and the steps involved are state machines. Repeat however you want, they're all different components of the same thing.

This is a great point, and you're right. Different concepts, not different models.

…I guess I talk about them as if they are different models, because that how they've been treated where I've worked. I appreciate the reality-check.

frob said:
A mistake outsiders make is that they assume complex == good.

Glad/sad to say this isn't something my burnt-out husk of a developer soul suffers from. Aghhhhh….sigh. :p

What you are describing is the difference between a coder and programmer / software engineer.

The coder's job is to type code without further thought. The programmer's job is to engineer the code first before typing it. The thought process you went through to demonstrate what was wrong with this code is what engineering is, aka

“The application of scientific and mathematical principles to practical ends such as the design, manufacture, and operation of efficient and economical structures, machines, processes, and systems.”

The American Heritage® Dictionary

In other words, it's the application of different principles (as opposed to using 1 principle everywhere) to solve different problems in an efficient way (you can't solve something efficiently if you don't adapt your solutions for each problem).

Like @frob said, it's code written by game designers which are coders but not engineers. It's like when you say “programmer art” when referring to low quality art made by programmers. Well, this is “designer code”. There's no shame in that . It's just a natural thing to happen in indie games because small indie devs have to do everything themselves and nobody is good at everything :D

aganm said:
it's code written by game designers which are coders but not engineers.

I get your meaning, but I just want to specify: These were dedicated engineering teams with veterans on them - not designers writing code.

aganm said:
What you are describing is the difference between a coder and programmer / software engineer.

The coder's job is to type code without further thought. The programmer's job is to engineer the code first before typing it.

I've never worked at a studio that employed programmers who didn't enjoy thinking about how to design their code. Actually, I'm not sure I've ever met a working programmer in games who typed code without further thought.

Advertisement

Carrion Without Me said:
I'm not sure I've ever met a working programmer in games who typed code without further thought

Then you never worked in an indie studio …. or at least one of those bad ones ¯\_(ツ)_/¯

The issue of BTs is the same issue every visual design language suffers from (and I haven't ever seen someone writing behavior trees in a declarative language so it is almost visual scripting): Some lines of written code are the equivalent of a dozen of visual nodes in a visual scripting environment. You either have specialized nodes and a lot of them, which is also considered bad, or you try to assemble features from existing but more simple nodes and in the end, need a lot of those as well. So however you try to solve that, it gets complex in the end!

Obvious opinion: to combine different techniques and algorithms, you need a framework that allows interoperabiity, e,g, putting “choose a target” in a BT without caring whether it's scripted, a further BT piece, the output of asynchronoous background planning recomputation, etc.

This requires an important engineering investment that might or might not pay off with good AI quality, high performance and fast development compared to simplistic specialized techniques, stretching general methods to inappropriate uses, compromises because something isn't feasible, and outright hacks. Many would choose the easiest path.

Omae Wa Mou Shindeiru

Jumpin in here: My experience has been that “ensemble” methods end up being the right approach for when you want to integrate more than one approach.

This can be done recursively, or it can be done as an ensemble of “voting participants,” or it can be done as a serial chain of differently-flavored tasks. You might have a state machine that's great at navigating the map while not in combat, and a behavior tree that's great at aiming/shooting/covering/reloading. You might also have a neural network that finds the best places to snipe from, or whatever. Synthesizing this into a single data model that all the components can draw upon, is the first necessary "trick" to get there, and knowing when to turn off and not use each particular component is the second “trick” to get good performance.

If you look at Unreal Engine, it leans somewhat hard on the behavior trees as the “high level concept,” but it also allows you to write plug-ins for those trees that do something else. All of them then share state through a “blackboard” which contains the shared state between the different pieces of the AI logic. However, for animation, it uses state machines, which can also talk to the same entities. It wouldn't take much to raise the level of abstraction such that you could use state machines, behavior trees, and other things as plug-ins for “themselves,” to achieve an ensemble that assembles the overall behavior from the individual pieces chosen specifically for what they are good at.

enum Bool { True, False, FileNotFound };

This topic is closed to new replies.

Advertisement