I’m taking a second pass at my thus-far glitchy and pretty unimpressive AI, and since I have a large number of environmentally-determined actions available to NPCs, the most appealing solution seems to be implementing an object-based AI like The Sims, where NPCs are essentially empty shells and Interactables (fridges, bathtubs, other people) hold all of the actual behavior, animations, stat adjustments, and so forth.
It seems like the cleanest, most beginner-friendly way to approach this is to view Interactable objects in-scene as Resources, and NPCs as Consumers: every interactable reports to a central Manager class and tells it a) what it can give NPCs (food, health), and b) where in the level it’s located (so NPCs don’t cross the entire map to get food when there’s a sandwich three feet from their origin point). The manager keeps track of remembering which objects are already in use by another NPC (and thus out of bounds), and which one are sitting vacant and ready to use.
So first of all, does that all sound reasonable? I’ve looked at a lot of incredibly complicated AI implementations, and concluded that I need something simple and beginner-friendly but reasonably efficient.
Assuming there aren’t any gaping holes in my logic, I imagine each NPC will go through a very basic loop for most of their life:
a) Ask for an object to use
b) Pathfinding to that object
c) Activate that object and execute whatever logic it contains,
d) Ask for another object to use
However, there are a two things that I can’t quite wrap my head around. First off, would the design I’m proposing be more suited to asynchronous AI (where each NPC manages their own logic), or synchronous AI, where a central manager iterates through a list of every NPC one by one and steps them through the logic of their current object?
The bigger question, which I’m having a lot of trouble answering for myself is structurally, how would you handle chaining actions together? I can say that making a sandwich has three discreet steps (gather ingredients, assemble ingredients into sandwich, eat sandwich), and I know roughly what each step will involve (subtract ingredients from fridge inventory, instantiate new sandwich item with stats based on cooking skill, delete sandwich and apply stat modifications to NPC), but I have no idea how to translate that into code. One general, clumsy idea would be to create a Step class that holds a list and an Activate() function that just reads
delegateList[0]();
Then each unique Action (sandwich-making, bathing, running from rabid dogs) would contain customized functions for each step of that action: AssembleIngredients(), MakeSandwich(), EatSandwich(), and the like. Presumably the last line of each step would be a call to the next function in the step, with the final function in the chain of Steps handing control back and asking for a new object. The obvious problem this raises, however, is managing cpu cycles: this would be running on 30-40 NPCs at once, and I don’t know how I would make each Action regularly cede control back to the main thread while remembering where it was. Using a coroutine is one option, but again I don’t want to have forty coroutines running at once, as that feels like extremely inefficient design.
I know this is an absurdly broad question with a greater scope than a single forum thread, but I’d love any feedback or references more experienced developers may have. ^^