How can we efficiently model high level game behaviors that are mostly dependent on ‘what goes on in the head of a NPC (non player character)’? In this article, I’m discussing approaches and sketching a model. I am mostly concerned about specifying in-game behavior concisely and correctly. If you want to apply what follows to an actual game, you’ll need to consider processing overheads, and scale behavioral models accordingly.
I started with three ideas in mind:
- Call a decision function at regular intervals. Whenever the function is invoked, the character checks their situation and issues an instruction regarding what to do next. I call this a ‘decision loop’ and this approach is common in many games that require simple behavior. The problem with a decision loop is that we need either doing a lot of book-keeping or reiterating many considerations every time. It’s not just memory or processing time. Practically, this approach is counter-intuitive to most people, and doesn’t scale up to complex behavior.
- We might layer several decision loops running at various speeds. The faster loops are used to take short term, tactical decisions. The slower loops are used to make decisions that are more on the long term. I find this idea quite attractive, but it would need to be developed to become practical – how do slow paced decision loops affect behavior? In a few trivial cases that might just involve carrying out a one-off action. In other cases, that could mean we need to make changes to faster loops. How do we translate ‘migrate to the south’ into the everyday behavior of a NPC?
- Have actors execute high level scripts or programs. The idea is to have a separate execution context for each actor. The script controlling each actor is structured like any program in C or whatnot and runs on a ‘lazy thread’, usually waiting for input describing the result of low level interactions in the game.
Affording a separate thread dedicated to effecting an actor’s behavior could make it much easier to specify and implement such behavior. However, we may not be able to use whatever language the game is written in to specify behavioral scripts, or maybe not very directly. Also, it’s not too likely that we can allocate system/native threads to our actors (I’ll come back to these issues in a later post).
OK, I’m not trying to write ‘regular AIs’, just NPCs. So the main problem can be divided in two questions:
- What is/are convenient ways to specify the behavior of NPCs.
- What do such specifications entail – what kind of software do we need to write so that the NPCs will behave according to the specification?
An example
I take the example of a farmer. Every day, they get up, get dressed, go to the field. They work in the field until twelve and then meet their wife near the well for a snack. Afterwards they work some more and go to the pub at the village. Once they are drunk they go home, undress (if able to) and sleep (A).
This doesn’t cover all the many behaviors of a farmer. Some examples:
- They could find a pot of gold while digging and decide to become a banker (B)
- They could encounter an aggressive animal while working. Then probably they would run and / or defend themselves (C)
- It could be raining hard, so they would go back home (D)
- If it rained the day before, they would try to avoid a puddle if they spotted one (E)
Script (A) is simple and linear. Starting from low level actions (move, plough, get dressed, drink, buy, sit…) that can be rendered as animations in a game, we would need to build each part of the script. That may seem a lot of work, but if we break down each part, we’ll soon find that we can reuse subscripts not just for this particular character but also for other characters. Also, I would argue that even a moderately realistic effort in this direction could make many games a lot more fun – after all, so many games have static NPCs, or NPCs that can handle only very restricted situations (e.g: shooting).
If we’re specifying script A, I would argue that many languages would do, ‘assuming we could use them for that purpose’. In other words, we would feel comfortable writing a program, maybe in C, Python or Javascript (many languages would do) that describes ‘a farmer’s day’ using a main procedure and it’s sub-functions. That would be more intuitive and definitely faster than implementing a decision function that gets called every time and representing the situation explicitly.
But then, surely we wouldn’t want to mix B-E with script A. This stuff could happen at any time and would need to be taken care of in priority. So we probably need.
- Detection routines or notifications to trigger a response to unexpected occurrences.
- A mechanism used to override a ‘main script’.
The actual specification of B-E could use a mechanism similar to script A, so it seems we have all the ingredients to offer a workable model.
Our model
Here’s the proposed model:
- The high level behavior of each character is defined by a set of scripts (To run these scripts, we may need a special interpreter).
- Scripts are arranged by priority. A higher priority script can override a lower priority script if needed – for example, fighting for life is more important than avoiding a puddle, so a ‘puddle event’ is ignored in the middle of a fight.
- A script can be attached to a condition that triggers the script. The script won’t actually take control of the actor unless it has suitable priority relative to the current script.
- A script can be invoked externally as a side effect of something else going on. Again, invoking means ‘attempting’. Another character might attempt starting a conversation with an actor under attack. That won’t actually start the conversation script.
- Once a script has completed, the interrupted script may resume, or maybe another, pending, higher priority script may start.
- Under special circumstances, a script may replace another (for example, our farmer can become a banker). This would probably require tagging scripts – the farming script is ‘occupational’, banking goes in the same category.
Needless to say, this can be improved in many ways – for example we may need to define ‘yield points’ – points at which it is graceful or safe to override another script… and so forth.
Pending, I would be very curious to hear about APIs allowing to do this or something similar… So please don’t hesitate to reply to this post if you have any idea.