I'm playing around in Unreal Engine with intention to create some kind of a small town scale Sims-like simulation. I don't need that much micro-managing as in Sims.
At first, I want to start with a bunch of simple NPCs that walk their route (using NavMesh, most probably) between work and home, based on time of day. But, depending on different factors, NPCs might choose to visit a shop or a cafe while on their way. Also, there are different jobs that require some flexible behaviors for cases when I closely follow an NPC or interact with it. Unreal Engine has Behavior Trees for AI but I find it somewhat limiting for my purposes, therefore I started looking into Utility AI.
Currently I'm watching the GDC lecture videos about Utility AI and trying to figure out how do I go about implementing it in Unreal and to apply to my sandbox-like simulation.
By the way, are there any Utility AI code projects that could be plugged into UE4?
As my town will have many citizens (hundreds? thousands? the more, the better, as long as my PC can handle) and I want to track them all on a world map (just dots), I would like to make it work efficiently. Of course, I will turn off animations and meshes (with their collisions and physics) when an NPC gets offscreen. But what about AI? How do I switch to narrower set of parameters and actions to choose from when NPC is offscreen? And does it really matter in practice? Should I leave the AI evaluating all the possible action scores and apply some checking only inside the action itself (if NPC is offscreen, then I do simple waiting for some sensible time instead of doing the full simulation)?
These thoughts led me to some other questions and ideas that might be useful for optimization, but I'm not sure how they would work in practice. So, I hope somebody else has already thought about this and tried it out and can share the experience.
In essence, the idea is a new abstraction I call Situation. It could also be considered just some kind of a context, but that word is so overused, that's why "situation" was my choice.
Basically, a Situation is something like a filter that contains a predefined (at design time) set of parameters and actions that are available, adequate and make sense at the particular Situation. For example, if an NPC enters a cafe, there is no need to continue evaluating the entire list of utility actions for being on a street or at a workplace. I don't want an NPC to decide that it should start running home as soon as possible (because of some not so carefully tweaked parameters) while waiting for an order in a cafe. Instead, I would want to make such action not available in that particular situation. Less actions to choose from = less parameters to tweak to prevent an NPC from doing something totally weird, right?
I guess, this could also be visualized as a tree of utilities. Or it could turn into a hybrid of utility AI (for high level actions, like "Where should I go next?") and behavior trees ("What sequence of predictable, adequate actions I should attempt to perform while being in this situation?"). But I would like to avoid mixing multiple systems just to keep it less confusing. Or am I totally wrong here and I shouldn't attempt to use Utility AI for cases when an NPC should be very predictable, almost following a well-known scenario?
Next, I was wondering, how an NPC would enter/exit the situations. How do we, humans, limit ourselves to some subset of activities to avoid doing something stupid "just because we can" or "I really wanted to do that at that very moment, even if that was highly inappropriate"? I guess, we do that based on our reaction and reminding about the environment we are in. "Hush, you are in a church now" - a mother reminds again and again to a child who starts to become too active.
The rough idea is as follows.
Unreal Engine has good environment query system and I can leverage that. Although, for performance reasons, I'll try to do it in reverse to human sense. If a human being sees a cafe and enters it, he/she is doing the query: "Am I in a cafe now?" In my case, it might be the cafe itself (through a trigger volume) triggering an event on the NPC: "You are in a cafe now." and NPC has the logic: "I should limit my activities to something I can normally do in a cafe and completely forget actions that are not sensible in a cafe."
And when the NPC exits the cafe, it again receives a trigger volume "exit" event, which in turn removes the filtered restrictions and allows to execute evaluation of the full set of actions.
If we assume that an NPC can be only in one situation at once, it looks like a simple stack with push/pop. Enter the cafe? Push the list of cafe actions and run utility AI evaluations on them. Exit the cafe? Pop the action list and now you are where you were before (with possibly some updated state parameters - you spent some money in the cafe and you reduced your hunger).
But is it good enough? Can there be a combination of situations where I need a set of actions for multiple situations at once? In this case my idea breaks down because a stack is not enough anymore. So, trying to combine multiple situations can get messy really fast and I'd better go with evaluating the entire list of utility functions instead of trying to combine all those multiple situations. Still, I could use this approach in cases when I 100% know that I need a limited set of actions.
What do you think about this rambling? Am I overcomplicating things (actually, because of trying to simplify them through not evaluating entire set of utility actions all the time)?