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

ScriptResponsibility
UtilityAction.csConsideration (an input plus an AnimationCurve response curve) and UtilityAction (considerations multiplied together, times a weight)
VillagerAI.csNeeds 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

  1. Each input is normalised to 0..1 (Sense): hunger, tiredness, need for money, has money, threat nearby.
  2. Each consideration runs one input through its response curve, giving 0..1.
  3. 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).
  4. The action you are already doing is multiplied by Commitment Bonus, so near ties do not flicker.
  5. The highest score wins. Scores are recalculated every Decision Interval.
ActionConsiderations (curve)Weight
EatHunger (x²), Has Money (any at all), Threat Nearby (falling)1.0
SleepTiredness (S-curve), Threat Nearby (falling)1.0
WorkNeed Money (rising), Tiredness (falling), Threat Nearby (falling)0.9
FleeThreat Nearby (x²)1.0
Idlenone: a constant fallback0.1

Scene setup (about 8 minutes)

  1. New 3D scene. Add a Plane at the origin.
  2. Add three Cubes as locations: Food at (-6, 0.5, 0), Bed at (0, 0.5, 6), Workplace at (6, 0.5, 0).
  3. 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.
  4. Threat: add a Sphere named Threat far away, e.g. (20, 1, 20), and drag it into Threat.
  5. 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

  1. 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.
  2. Weighted random from the top few: pick randomly among actions within 10% of the best score so several villagers do not act in lockstep.
  3. Targets: score Eat once per food source (distance as a consideration) and walk to the best one.
  4. ScriptableObject actions: move each UtilityAction into a ScriptableObject asset so designers can share action sets between NPC types.
  5. Debug overlay: draw the score table above the villager’s head with OnGUI or a world-space TextMeshPro label.

Source files

2 items under this folder.