[In this reprinted #altdevblogaday in-depth piece, WhatIf Productions's Jake Kolb looks at different techniques for giving the creatures in your game feelings, examining what works and why.]
This article continues from my last post on "Implementing Creature Cognitive Replay".
Context: where we use this
"Giving your Creature Feelings" covers adding feelings to video game entities, such as Space-Marines or Mutated-Chickens, to better emulate player expectations or create a more believable and responsive simulation.
By attaching 'emotional-tags' (love, hate, excited, scared) and amounts to a creature's existing knowledge units, we can model emotional interactions and possibly relate to the lives of our creatures. The focus here is on simulated worlds where the creature behaviors are not scripted or fixed to always follow the same path. In this context, we regularly run "time-has-passed-so-update-this-creature's-thinking" code.
By giving creatures feelings, we add a second dimension to all the existing thoughts with an 'emotional' component. We can use that to re-order/prioritize knowledge, especially competing/contradicting thoughts that are deadlocked in importance/certainty, such as "Why did it have to be snakes? I hate snakes" (fear, snakes, .800) versus "I must find the ark" (desire, ancient_artifacts, .811 ).
Remember that inserting 'feelings' doesn't make your game/simulation a 'touchy-feely/psychologically-soft' affair; it deepens what your creatures can do with their thoughts and makes their behaviors more diverse over time as these feelings wax and wane out of sync with the knowledge in the thoughts themselves.
Goals: what we need
Means to attach an array of various feelings to any unit of knowledge
Provides means to weight decisions and distort existing priorities
Can store a series of feelings as sample-able field, like a texture map, to have metrics that can be scaled down when memory-size matters and to be able to filter for 'hindsight shifts'
Solutions: how we triedTechnique: Like-to-Dislike scale
Our first attempt at adding 'feelings' attached a single byte to each unit of knowledge representing -1.0 to +1.0 range of like to dislike. It complemented the 'certainty' value that determined how much trust we had about the truth or completeness of the information.
We had been making a prehistoric continent simulation that had around 1,000 villages of about 50 people each. In this simplified tribal village, each day hunter-gatherers left to search nearby areas for food. Before we added feelings, we had each villager make a random decision about a direction to go and return home if it found food.
We had lots of wandering folks who repeatedly went to places that yielded no food and other odd choices that gave us the unlikely-to-ever-happen-this-way-vibe. As we added feelings, we had them decide where each would go by sorting their 'thought list of known places' with the 'like' value (every known place started off neutral).
If they found food, they would increase the 'like' for that location and be more likely to head to favorite spots in the future. They would also scale their decisions by the 'like' score they had for other villagers already headed that way (we let them be telepathic and just read each other's data to simplify a dialog system).
This created cliques of villagers who found success together and popularized searching similar spots. At the same time, frequently returning to the same spots reduced the likelihood of finding food as the grazers (cow-like herbivore things) would move to new areas over time.
Adding 'jealously' further hardened these cliques as we let each villager raise/lower the like score for their peers based on whether their success was with or away from them. Not too much code and we suddenly had packs that regularly patrolled certain areas, lone wolves who cover the same territories at different times, and a variety of intriguing movement.
Pros: Simple amount of code gave us some diverse behavior.
Cons: Didn't adequately capture all the range or competing feelings we wanted to add in.
Instead of just 'like/dislike' scale, we let the designers add a bunch of opposing feelings such as love-hate, interested-bored, fear-desire, etc. We treated each pair as a dimension and would dot product them all to weigh each decision (thus folding many feelings into the decision instead of the positive/negative)
Pros: Increased the factors in decision-making and gave the designers more to work with for villager interactions.
Cons: Didn't really benefit until we had more means to express these feelings. We probably should have added thought-bubbles above their heads to help players grasp the depth of their decisions. Realized that opposing feelings often occur simultaneously, such as love and hate at the same time. Need to store two unsigned values instead of a polarizing single signed score. Lots of memory.
We started storing feelings in a history per villager each day. Then we added the ability to query feelings in the past and we used a max filter to let strong emotions overturn the accuracy of memory such as "it's always dangerous on the top of the mountain" instead of "actually, it was dangerous 3% of the time you went there".
Pros: Added a richness that satisfied designers.
Cons: Lot of code and processing went into providing subtleties that few could notice without a technique like a thought bubble to show. Tons of memory.
Survivor: who proved best & whyTechnique: Meme-Propagator using Mood
Using the contribution-vector approach, we compressed feelings and let villagers share them, saving memory but keeping a wider range of decisions.
At the end of each day a villager's individual thoughts were compared to others (a loop of memory XORs over the thoughts) and if it was over a threshold, such as 60% similar, we would collapse those thoughts and treat them as a 'group thought'.
We explained it as 'trending' or 'peer pressure' as it allowed us to consolidate many villagers with small amounts of processing and memory but still have a diverse society and unique individuals.
Pros: Space-efficient, faster than others. Still provides believable yet interesting decisions
Cons: Doesn't always produce expected results. "Irrational" or "psychotic" inconsistencies arise which may be realistic but can crush a game's narrative or simulation's utility if it spreads as a mood-meme.
Future: Connect feelings to facial-expressions and body language so players can read these changes without cartoony thought bubbles or subtitles.
String_t Name_p;//name of feeling,//generally taken from an enum of 10 or so based on the game's theme
Contribution_f[ Emotion_Dimensions_Cnt_k ]//an array of feeling state like love, hate, fear, desire, curious, bored, etc.}struct Feeling_t
Emotion_Vector_t Emotion_Vector__ptr;//An index to a vector of 10 or so dimensions that//we use to combine together to change the 'importance of thoughts of knowledge'//when the entity with this feeling makes decisions
Percent_t Strength_f;//how powerful is this feeling compared to others
Time_t Realized_time;//when this feeling arrived at this 'strength' level
How to code the feelings of this Watermelon Hunter...
[This piece was reprinted from #AltDevBlogADay, a shared blog initiative started by @mike_acton devoted to giving game developers of all disciplines a place to motivate each other to write regularly about their personal game development passions.]