It’s clear that developing AI for a modern game is a challenging undertaking. Our games have become increasingly complex in terms of their environments, in terms of the depth of their story, and in terms of the gameplay itself. Players have become more discriminating, widespread multiplay has offered them an alternative to playing against the computer, and their expectations have been raised by other games that successfully delivered compelling AI. Worse, when a game provides compelling AI in a limited domain, even at the expense of other features, it raises the bar for the entire industry. Improvements in graphics and animations have allowed us to approach photorealism, simultaneously causing players to expect a similar level of behavioral fidelity and chewing up processor resources that are needed for many generic AI techniques. And at the end of the day, most game AI is still done using a procedural, rather than a declarative, approach (for a discussion of the relative merits of procedural vs. declarative AI, see my article in AI Game Programming Wisdom 4).
At the same time, despite the difficulty and expense of creating game AI, we continue to throw all of that work away at the end of each project, and start fresh for the next game. Sure, we may retain a few tools – a scripting language here, a behavior tree architecture there – but the bulk of the work, the actual behavior, is tossed on the scrap pile.
Code reuse has been applied successfully in other areas of game development. We don’t typically build a new renderer for each game. We don’t typically reinvent our physics engine, or change the code that controls animation in any fundamental way. Sure, we’ll extend what we’ve got, add new features – maybe better water effects, maybe a new blending technique that allows us to generate better facial animation – but I would argue that the fact that we retain the bulk of our old code is precisely what allows us the freedom to really dig into the areas that provide the most bang for the buck. We’d never have time to focus down on getting the God rays, ripple distortion, and the atmospheric particle effects when swimming underwater looking just right, for example, if we had to rebuild the renderer from the ground up each time we built a game.
What’s more, there are middleware companies that have been building libraries of reusable code for years – building a solid product, whether it is a renderer or a facial animation system or just a tool for making really good looking trees – and then iterating and improving on it while continuing to sell it for use in hundreds of games.
My article in Game Programming Gems 8 discussed one potential approach to code reuse for Game AI, but here I’d like to discuss a different (although perhaps complimentary) approach. The insight driving this approach is that while there is AI middleware as well, generally speaking, with the exception of path-planning middleware, it hasn’t really taken off. It seems to me that one of the reasons it hasn’t taken off is that it’s providing code reuse at the wrong level of granularity. What you get with most of the AI middleware packages is a development environment in which you can build your behavior. In other words, you get a fairly standard reasoning architecture (typically either an HFSM or rule-based architecture) plus some tools. The problem is that building the reasoning architecture is really only about 20% of the job. The other 80% - the hard 80%, the 80% that requires endless iteration and polish, the 80% where your AI succeeds or fails – is using that architecture to create the actual behavior for your characters. What we need
On a side note, as I mentioned above, one area in which AI middleware companies have succeeded is path planning – a domain where they’re controlling behavior, not just providing tools for specifying it. A subtle difference, perhaps, but an important one.
So why aren’t we reusing AI behavior? It seems to me that it boils down to two reasons. First is the perception that AI behavior is extremely game specific. That there’s so much that is unique about each game that it’s impossible (or at least inadvisable) to share behavior between games. Even for two very similar games (say, for instance, two WWII first-person shooter games), it is essential to customize the behavior for each game, to give each game the unique feel that is what makes it cool.
The second problem is that it’s not clear that we know how to do this behavior sharing. What would we be saving? Finite states? Behavior subtrees? How would the game interface with those objects, and how would they interface back into the game?
So let’s take both of those problems in order. First, the issue that each game is a delicate flower, pure and perfect, and wholly unique from every other game that’s ever been made. Honestly, that’s bullsh… erm, I mean, I respectfully disagree.
So as random examples, let’s take the last two games I worked on. The most recent was Red Dead Redemption. This game was not a standard space-marine-fighting-demons Doom-style shooter. It was set in the early 1900s, with era-appropriate weapons. Nevertheless, it had characters armed with pistols, rifles, shotguns, grenades-like weapons, and occasionally even machine guns. The various weapons had variations in how much damage they delivered, optimum range, rates of fire, and so forth. The characters could do things like fire, duck behind cover, and reload. There were first aid kits that would restore your health. Common team tactics such as using suppressive fire and flanking an opponent were viable (though not necessarily used by the AI). Any of this sound familiar?
The game I worked on before that was Iron Man. Again, this was not a standard FPS – in fact, it was quite a bit more of a departure than Red Dead. Nevertheless, the vast majority of characters had weapons like missile launchers, machine guns, or rifles. They could use cover. They could reload. They could use team tactics.
Both games focused on things other than high quality AI opponents for tactical combat, and honestly both struggled during development to create good combat behaviors for their characters. That’s not to say that the end result wasn’t good – I’m proud of the result in both cases – but significant time and money could have been saved during development if we’d had an existing library of FPS behaviors as a starting point. What’s more, having that library would have allowed us to spend our time polishing and tuning the behaviors we wanted specifically for that game, rather than reinventing behavior that has been commonplace for years. I think this is the most important point. Having reusable behavior would not decrease your ability to customize and tune the behavior in a game, it would increase it. The time normally spent rebuilding basic functionality could instead be spent on customization. Those resources could be dedicated to the aspects of the game which make it unique, rather than being expended on aspects of the game that are, honestly, fairly generic.
The ability to find a common core of behavior isn’t unique to shooters. How many strategy games have a handful of resources, a rock-paper-scissors approach to combat, and technologies that improve the units in fairly similar ways? How many fantasy RPGs have tanks, healers, and DPS? How many games have background characters that just need to act in generic and believable ways? Again, the details aren’t the same from game to game, but the general shape of the problem is. A general solution could, in theory, get us 80% of the way there, and let us focus on the last 20%, rather than spending all of our time rebuilding that first 80% - or worse, the first 60%.
This brings us to the second problem. It does us no good simply to *want* to reuse behavior. We also need to *be able* to do so. Despite a decade of attempting to do so, we have not yet agreed on common standards for game AI. There is no equivalent to the texture-mapped triangle, or the animation skeleton, that we can rely on being the same in every game ever built. What’s more, there’s no evidence that there will be a general solution to that problem any time soon. Game AI is just not something that lends itself to standardized solutions.
With that said, the general shape of an AI is pretty standard. The AI accepts data from the outside environment – often called “sensing.” It then processes that data – often called “thinking” – and selects one or more things to do – often called “acting.” My suggestion would be to share the entire core of the AI – the “thinking” part – and provide interfaces to the game engine which specify what the AI expects to see on the sensing and the acting side.
As an example, on the sensing side our shooter AI would want to know things like the position of the character, its enemies, and its allies. The position and size of any cover locations. The priority for taking down each enemy, and for protecting each ally. The weapons it has to choose from, their ranges and damage characteristics, magazine capacity and current load, and the amount of ammunition available. It could then process that information and return actions such as “crawl to this position,” “shoot at this target,” or “reload your weapon.”
The end result is a core set of behaviors which, while genre-specific, would still be applicable in a great many games. For instance, the combat AI that I described above could not only be used in most FPS games, but also many RPGs, and even to control individual units in some RTS games. In order to reuse the behavior you’d first implement the wrapper which handles interfacing the sensing and acting steps to the game code, and then you’d go into the core of the AI and tune the existing behaviors to match your creative vision.
Shooter AI is one extremely common area in which reusability could work for us, but it isn’t the only one. A similar approach could be taken for high-level strategy AI, for sports games, for the background characters that fill out the world, or for any other area that seems widespread enough to be worth the effort.
Of course, all of that is easier said than done. The key challenges seem to be:
* Building a core set of behaviors which provide the basic functionality that’s desired in a large set of games. Obviously this behavior set will grow and change over time, but we need to start by getting the basics implemented.
* Providing the means to add and remove behaviors for specific games, as well as the means to tune the decision making process used to select those behaviors.
* Similarly, providing the ability to add and remove sensor data and actions from the external interface so as to be able to customize the AI for each game.
Put that way, this seems like a large but solvable problem. Sure there’s significant work there – but there was significant work to build shared libraries for physics, or for rendering, or for network code as well. And once that work is accomplished, hopefully we can get over the hump of constantly reimplementing the basics, and on to the challenges of achieving behavioral photorealism.
Posted by kdill4 at October 12, 2010 01:43 PMInteresting. As a former purveyor of AI middleware I think you're on the right track.
Back at the very start of the IGDA SIG on AI we were basically trying to agree on that interface. It was a thankless and fruitless task. It is easy to handwave and say 'it needs locations, cover points' etc, but when you come to really specify it in a way that isn't too dependent on the rest of the engine. That's really hard. And without that you can't build those higher structures.
My view on this has changed somewhat over the years. I now suspect that the growth of AI middleware will grow out of pathing (before I thought pathing was by-and-large a sideshow).
Because pathing is actually a planning task, planning is nicely subvenient to a whole range of behaviors. So to get where you're going, I think it has to be through tactical pathing, then its easy to go from there to action planning and then you've got a system and a set of intelligence that is a strict superset of current generation static state-based approaches (incl. BTs). Planning is hard, and slow, but it does remove the amount of mid-level behavior engineering you need to do.
Posted by: Ian at October 12, 2010 02:58 PMThrowing away a lot of code means throwing away the general idea. You usually have to move up to a really abstract idea before it starts to sound reusable (re: the general shape of an AI is pretty standard). There are usually dosens of implementations that match those general ideas depending on the fidelity of the simulation or amount of memory or other resources you can use.
Making AI code is a bit like maintaining a garden. You start with a pile of mud and a seed and carefully inspect and nurture it until you get something that resembles a living thing.
With AI code you might start with some implementation of a BT and some A* there and some animation system integration but the end result will be specifically handcrafted for the type of game you are making.
I don't see that as defect, it is the process of making games.
In order to reuse a game, you sort of need to store a snapshot of the code just before you went and implemented the things that makes your game special. Making such components as part of the game making process is really hard because you need to say no to many feature requests (which on the other hand might be vital to your game).
As open source AI toolkit developer, that is something I can try to do. If you try to fit my toolkit into your game, you will either need to write some glue code or tear the toolkit apart. I think both options are equally good and I encourage both. I don't mena my code to be silver bullet, it is merely a length of pathfinding 2-by-4 you use as starting point for your creation.
The price you pay when making something in isolation is that it never fits perfectly. But on the other hand, it can serve a good purpose as a starting snapshot to get your new project up and running. And you can also carry the ideas and lessons learned to your next project. Although, the temptation might be to rewrite it because now you know how to make it better (insert blatant reference to Hero's journey here).
In the end you end up building a Frankenstein, a very dear monster. The problem of reusing him the next time around is that you can see the flaws, and that does not match your perfect vision of an artificial being.
Posted by: mikko at October 12, 2010 11:48 PMKevin, I have three thoughts for you.
1) No one reads this blog anymore (because we only post new content twice a year, or less). If you actually do post something, you need to check the junk folder to release comments aggressively flagged as spam. I just released Ian and Mikko's comments from Oct. 12th for you.
2) I agree with the point you are making. I have not read your article about procedural vs declarative representations, but the funny thing is that the points you are making here are the same as those that motivated the development of GOAP (beginning in 2002). (See pages 7 & 8 of 3 States & a Plan). Modularizing behavior into Goals and Actions, and assigning Goal Sets and Actions Sets to characters facilitated behavior re-use. We shared many Goals and Actions between different types of characters within FEAR, and between two titles: FEAR and Condemned. To my knowledge many of these Goals and Actions have been re-used in the FEAR and Condemned sequels too. Declarative representations FTW.
3) In my current research, character behavior (and dialogue) is represented as recorded gameplay traces. Long term, I believe this is the ultimate solution to behavior re-use, because it introduces a clear separation between the behavior's content and 'rendering' of that content. In other words, if three years from now, I decided to implement a new AI system (aka a new 'AI renderer'), I could import the same 'database' of behaviors that I was using previously, rather than reimplementing them from scratch. This brings AI much more in line with how graphics works -- you can import the same 3D models into many different rendering engines.
Jeff
Posted by: Jeff Orkin at October 16, 2010 08:42 PM@Mikko: Imagine if you could start your game with a fully functional FPS AI, and instead of having to spend two years building all that behavior, you could spend two years tuning it to do exactly what you want, improving and extending it, and adding the unique behavior that really does need to be unique to your game. Sure, it isn't a perfect starting point - it isn't going to match exactly what you want in your new game - but it's a hell of a lot better than starting from a blank page.
I'm not saying that I know how to get there - but I can't see any reason why it's not doable. Some of the ideas in my GPG8 article start to build in that direction, and it sounds like Jeff has had some past success and ideas for the future as well.
@Ian: I guess my mental model is, "If you build it, they will come." Don't try to get agreement up front. Instead, when you write your next game, decouple the AI from the engine. Force interactions to go through a wrapper, and make sure that the wrapper is game independent. Bam, you've got your interface. Sure, it might not be perfect - but it's a start. And honestly, it doesn't require all that much extra work.
Then, when you work on the game after your next one, drop that AI in and see how much use you can get out of it. Unless you've completely changed game types, I bet the answer is "more than none."
And if you really get it right - if you find that you have, say, a core library of shooter behaviors that define most of what you need to get a game up and running - wrap the thing up and sell it as middleware. Hell, it didn't cost you anything to develop - you were writing the game anyway. Worst case is, nobody buys it... you've still got it to reuse in all of your own future games.
Posted by: Kevin Dill at October 19, 2010 12:49 PMThe Sims is an interesting case to examine. The "interaction system" serves as the core for the AI in all Sims games, because it works so well for that type of game. Each interaction defines the behavior of a sim (ex: eat food interaction), so once the architecture is in place, the gameplay programmers focus mostly on creating different interactions. I've worked on 3 different types of Sims games (The Sims 3, MySims, Sim Animals), and we ended up sharing AI code for some of these games, but not all. MySims and Sim Animals used the same engine, so naturally, Sim Animals was able to re-use a lot of the existing code in MySims. However, for Sims 3, the interaction system was built from scratch. The main reason for this, in my opinion, is that it would just be simpler to re-create than to try to create some central modular system. But why is this the case? Well, the AI in The Sims 3 was in C#, and the AI in the other two games was in LUA. But even if the AI was in C++, I still think the interaction system for these games should have been built independently, as they were. The Sims 3 needed a much more complex interaction system (ex: adding a hierarchy to the evaluation of motives). So if we were to pull over all the complexity of the interaction system in Sims 3, for Sim Animals, it would have made it more difficult to create Sim Animals.
I really like the idea of building reusable AI, and I think we should work toward that goal. I am mainly pointing out several practical things that we must consider when creating reusable AI, based on what I've learned from The Sims. To summarize, here are a few things we must consider:
1. Games are made on different platforms, and AI is often written in different scripting languages. The reusable AI code will have to be in a non-scripting language like C++. How will this reusable code interface with the AI written in script?
2. Games have different levels of complexity in AI. How can we hide this complexity for simpler games, and expose it for more complex games, without burdening the programmer?
Posted by: Alex Kring at October 20, 2010 01:43 PMI actually think the largest problem with having a reusable AI component will be performance.
It's true that many games split the AI update into Sense - Think - Act. I think your insight that much of the reusable code is inside the Think stage is also correct. I'm excited about the possibility of reusing behaviour components.
The issue is that a lot of the performance cost of most AI is actually in the Sense phase. It's very easy for humans to gather data about their surroundings, but very computationally expensive for AI agents to do so. If we make the interface for the reusable component appear between Sense and Think, then constrain choices on the format and frequency of sensing data.
But unfortunately, those choices are often critical to performance. How expensive are raycasts? Are they batched/multi-threaded? How expensive is querying the navigation mesh/graph? What sort of information is stored there (in addition to static navigation info)? How are cover points stored, accessed, and updated? Etc.
I think this performance problem makes marketing an AI toolkit very difficult.
All that being said, attempting to create middleware that helps us make games is a laudable goal, and I do think that eventually these kinds of libraries will become popular.
Posted by: chriso at October 21, 2010 11:14 AM