So, I may have skipped ahead a little in the previous post. We need a system to control which emotion is currently strongest in a creature, and what they do as a result.
Before we go into that though, let me introduce you to the Other Most Important Node in BehaviorTrees. The first one was the “Sequence” node, which we originally discussed back here.
The second one is the “Selector“. It’s basically an inverted Sequence:
– A Sequence runs though the nodes in it’s purview, advancing to the next one when the previous one succeeds, and Failing if one fails.
– A Selector does the opposite: it runs through the nodes in it’s purview, advancing to the next one if the previous on fails and Succeeding if any of them succeed.
This is brilliant for when you have multiple ways of achieving the same goal: it means that if the first method fails, they can fall back to the second, then the third and so on. For example, for an omnivore that prefers meat:
The selector would start the Hunt behavior. If Hunt failed, maybe because there was no prey to be found or the would-be prey had really big, scary teeth, it would advance to Scavenge. If Scavenge also failed, it would advance to Eat Tree, and so on.
Only when one of these food-finding strategies succeeded would the ‘Seek Food‘ selector as a whole succeed.
Selectors are rarer than Sequences, but no less important: without them, the tree wouldn’t be capable of ‘intelligent’ decision making.
Okay, now that you’re familiar with that concept, let me introduce you to “EmotionController“.
This is a unique composite node near the top of the tree. It has six “Emotion” nodes.
Each Emotion is a Selector, and will be filled with ways of sating that emotion: the ‘Seek Food‘ selector above would belong with the Hunger emotion. At this point it becomes closer to a cause-and-effect Needs system rather than a fuzzier Emotion one. I’m okay with this: it makes the creature’s behavior a bit more understandable.
It was this structure that lead to me requiring a way for creatures to respond to Pain, which lead to the implementation of Sleep, as discussed in the last post.
The EmotionController itself isn’t a selector though: it’s behavior is a lot more complicated. I’ll explain it as best I can…
Each Emotion has a Value, determined from a constantly evaluated Base equation (for example, hunger is always inversely proportional to energy), and a more dynamic Floating one (for example, being attacked triggers a flash of fear which fades over time). This value determines the current priority of the emotion.
When any emotion succeeds or fails, the creature will reassess the urgency of all it’s emotions and act on the strongest. The concept is similar to a Repeat node, except with a variety of options to choose from depending on the strongest emotion at the time.
At this stage, the design makes the creature’s very single-minded. If they start seeking food, they will continue seeking food until they either find it or exhaust every possible food-seeking strategy (or die a horrifying death, but that’s a given). This is a good strategy in general, but it makes no concessions to interruptions. Waiting to finish your meal before reacting to the Murderus deathicus that is currently tearing your limbs off is not the model of intelligence that I was going for.
So I introduced an interrupt mechanism to the EmotionController. If an emotion exceeds the Currently-Being-Acted-Upon emotion (I may need to work out a more concise name for that), the creature will immediately fail the previous emotion (and any child routines that happen to be running) and begin acting on the new one.
This strategy is better, but it has a major problem: it leads to behavior loops, where a creature gets stuck between two states. Imagine a creature with low Energy and Health: it eats for a frame to raise Energy, then interrupts to pain, sleeps for a frame to raise Health, and interrupts back to hunger. Rinse and repeat.
The solution I’m using for this is an urgency threshold for each emotion, specifically for interrupts. Fear and Anger, for instance, have a very low threshold: if they exceed the currently prioritised emotion, the creature will react *immediately* and flee or fight whatever triggered the interruption. Pain, hunger, discomfort and amorousness all have much larger modifiers: to interrupt hunger and cause sleep, pain needs to exceed hunger plus a large modifier.
This doesn’t actually prevent loops: it just lengthens them. Hypothetically, a hungry creature on the edge of a strong temperature gradient like a lava-field could venture into the field to eat, only to be interrupted by discomfort, retreat back to the comfortable area, and interrupt back to hunger again. But with a sufficiently high urgency threshold on the relevant emotions, this behavior is likely to be much rarer.
It’s not particularly unrealistic either: biological organisms get stuck in loops all the time…
I’ve been spending quite a bit of time tweaking the emotional systems, to the point that it now works reasonably well. Some emotions require unique attributes to ensure correct behavior: for instance, “discomfort“, “pain” and “fear” have no maximum, so that in extreme situations (spreading lava, for instance) they will be prioritised even if other, more limited emotions (like hunger or amorousness) are near 100%.
Even with very basic placeholder behaviors (the ones implemented so far are “eat tree“, “wander“, “sleep” (idle + healing) and “move to more comfortable area“*), the system is proving its worth. We can plonk down a temperature device and watch the creatures respond to the added discomfort and flee. Plus there’s signs of improvement in their life cycle: rather than the uncontrolled randomness of before, new creatures act sanely: they seek food initially, then sleep to convert that food to health, which they can use to begin a pregnancy and produce babies.
Additionally, if they manage to gather food well enough to reach a “well fed and healed” state, they then start to act on the lower-priority emotions: discomfort and amorousness.
Finally, the new system is much more communicable. The debug method for showing their priority emotion at the moment is simply printing text above their head, which looks terrible above crowds, but “hungry: seeking food” and “exhausted: sleeping” are infinitely more interesting than “approaching tree because REASONS” and “attacking own offspring because SHUT UP THAT’S WHY“. I’ll need to find a way to communicate that information, because it really adds to the sense of character you get from the creatures.
It’s rapidly becoming apparent that this update is going to have some far-reaching consequences on the game as a whole. And, as always, I have no way of predicting what they’ll be. It’s as much a process of discovery for me as anyone!
I love this job.
*footnote: I really need a more concise term for “move to a more comfortable area”. A term for travelling to a more hospitable climate… oh, of course! “Migrate“!
… and I’ve implemented a much-requested feature without realising it again, haven’t I?