This site is for Ulster University students and staff. Enter the class access password to continue.
utility-ai (code example)
3 min read
Utility AI villager (Unity 6 / C#)
A small utility AI: a villager who chooses between Eat, Sleep, Work,
Flee and Idle by scoring every action from its needs. Companion code for
utility-ai. Plain C#; nothing to install.
Files
Script
Responsibility
UtilityAction.cs
Consideration (an input plus an AnimationCurve response curve) and UtilityAction (considerations multiplied together, times a weight)
VillagerAI.cs
Needs that drift over time, input normalisation, the decide-then-act loop, a commitment bonus, and the default actions and curves
How a score is made
Each input is normalised to 0..1 (Sense): hunger, tiredness, need for money,
has money, threat nearby.
Each consideration runs one input through its response curve, giving 0..1.
An action’s score is the product of its considerations, times its weight.
A single 0 vetoes the action (Eat with no money scores 0).
The action you are already doing is multiplied by Commitment Bonus, so near
ties do not flicker.
The highest score wins. Scores are recalculated every Decision Interval.
Action
Considerations (curve)
Weight
Eat
Hunger (x²), Has Money (any at all), Threat Nearby (falling)
1.0
Sleep
Tiredness (S-curve), Threat Nearby (falling)
1.0
Work
Need Money (rising), Tiredness (falling), Threat Nearby (falling)
0.9
Flee
Threat Nearby (x²)
1.0
Idle
none: a constant fallback
0.1
Scene setup (about 8 minutes)
New 3D scene. Add a Plane at the origin.
Add three Cubes as locations: Food at (-6, 0.5, 0), Bed at (0, 0.5, 6),
Workplace at (6, 0.5, 0).
Villager: add a Capsule at (0, 1, 0) and add VillagerAI. The five
default actions appear in the Actions list. Assign the Location of Eat
→ Food, Sleep → Bed, Work → Workplace (Flee and Idle need none).
If the list is empty, right-click the component header and choose Reset.
Threat: add a Sphere named Threat far away, e.g. (20, 1, 20), and drag
it into Threat.
Press Play and watch Current Action and Scores in the Inspector.
The capsule is green for Eat, blue for Sleep, yellow for Work, red for Flee and
grey for Idle.
What to try
Let it run for a minute. Work drains energy, so Sleep takes over; hunger rises
until Eat wins; eating spends money, so Work comes back. No transition was
written for any of this.
Drag the Money slider to 0 while it is hungry. Eat drops to 0 (vetoed by
Has Money) even though hunger is high.
Drag the Threat sphere in the Scene view towards the villager. Every action
with a “Threat Nearby (falling)” consideration fades as Flee rises.
Set Commitment Bonus to 1 and Decision Interval to 0. Look for moments
when two scores are close and the villager dithers.
Tick Compensate For Consideration Count. Eat and Work have three
considerations, Flee has one; compare their scores before and after.
Open the Eat action’s Hunger curve and drag the keys. Make the villager a
grazer (eats a little, often) or a feaster (waits until starving).
Extension ideas
Bucketing: give each action a priority bucket (for example Survival above
Daily Life) and only score lower buckets when no action in a higher bucket
scores above zero.
Weighted random from the top few: pick randomly among actions within 10%
of the best score so several villagers do not act in lockstep.
Targets: score Eat once per food source (distance as a consideration) and
walk to the best one.
ScriptableObject actions: move each UtilityAction into a ScriptableObject
asset so designers can share action sets between NPC types.
Debug overlay: draw the score table above the villager’s head with OnGUI
or a world-space TextMeshPro label.