Hunger Explained
Overview
The hunger system in UnityStation is a modular living-creature system that tracks nutrient demand, distributes blood-borne nutriment to body parts, applies hunger states, and triggers alerts and status effects.
The hunger system manages:
- registered
HungerComponentbody parts - the mapping of nutriment reagents to consuming body parts
- per-tick hunger evaluation and state changes
- alert and status effect updates.
Key components
HungerSystem
HungerSystem is the central manager.
Responsibilities:
- collect all
HungerComponentbody parts viaBodyPartAdded/BodyPartRemoved - build
NutrimentToConsumefrom registered body parts - initialise hunger rates and stored fat via
StartFresh/InitialiseHunger - run
SystemUpdateeach tick - choose a hunger calculation strategy through
HungerCalculationMethod - update status effects when the hunger state changes.
Important fields:
NutrimentToConsume: A dictionary that maps what reagents body parts consume/release.BodyParts: list of all activeHungerComponentcomponents.BodyNutriment: default nutrient reagent assigned to unconfigured body parts.NumberOfMinutesBeforeStarving: target starvation window used when initialising fat storesCashedHungerState: last applied hunger state for change detection. Default is usuallyNormal.HungerCalculationMethod: runtime-selectedIHungerCalculationimplementation.
HungerComponent
Each body part that participates in hunger has a HungerComponent.
This component mainly exists as a data container, and should be used as such.
Key behavior:
- stores the part's
Nutrimentreagent type - stores a passive consumption rate in
PassiveConsumptionNutriment - stores a
HungerModifierthat affects part efficiency - tracks the part's
HungerState - computes healing from nutriment via
NutrimentHeal - derives blood throughput from its attached
ReagentCirculatedComponent.
HungerComponent is attached to individual body parts and is what HungerSystem registers and updates.
IHungerCalculation
IHungerCalculation is the hunger-state strategy interface.
Implementations compute a HungerState for the creature each update by inspecting LivingHealthMasterBase and the HungerSystem.
This design allows different species to have their own hunger calculation method, and allows unique GameModes and rules to replace hunger behaviors during runtime.
DefaultUnityStationHungerCalculation
This is the primary UnityStation-style hunger algorithm.
Workflow:
- Sum heart output from
creatureHealth.reagentPoolSystem.PumpingDevices - Call
NutrimentCalculation(...)with that efficiency and the currentNutrimentToConsume - Return the creature's overall hunger state by scanning all registered body parts via
CheckHungerStateOnAll(...)
NutrimentCalculation looks at all the body parts during the calculation, and decides if its hunger state is Starving or Normal.
CheckHungerStateOnAll checks for the lowest state a body part has, and it bases the entire creature's hunger state based on that. So if a single body part is starving, it decides that the creature is starving despite having 20 other well fed body parts.
BodyFat and Stomach
The hunger system also interacts with organs that represent stored energy.
Stomach
Stomach digests its StomachContents into the blood pool each LivingHealthMasterBase tick:
- it consumes
DigesterAmountPerSecond * RelatedPart.TotalModified - converts that reagent mix into blood using
ReagentCirculatedComponent.AssociatedSystem.BloodPool.Add - if the stomach is empty, it sets its own
HungerComponent.HungerState = Starving - if the stomach is near full, it sets its own
HungerComponent.HungerState = Full - otherwise it sets its own state to
Normal - when all associated
BodyFatstores are full, it spawns additional fat and reports weight gain.
BodyFat
BodyFat represents stored nutriment.
During ImplantPeriodicUpdate:
- if blood nutriment percentage is below
ReleaseNutrimentPercentage, it releases stored nutriment into blood - if blood nutriment is high and the fat store is not full, it absorbs nutriment from blood
- if stored fat exceeds
DDebuffInPoint, it applies movement speed debuffs viaIMovementEffect - it uses
AbsorbedAmountto derive its ownHungerComponent.HungerState: 0→Malnourished< 5→Hungry> NoticeableDebuffInPoint→Full- else →
Normal
This means fat stores are both an energy reserve and a separate contributor to hunger state on the stomach/fat body part.
Runtime flow
Registration and initialisation
- When a body part with a
HungerComponentis added,HungerSystem.BodyPartAddedregisters it and callsBodyPartListChange(). BodyPartListChange()rebuildsNutrimentToConsumeby grouping body parts bybodyPart.Nutrimentand summing their demand.StartFresh()assignsBodyNutrimentto any body parts missing a nutrient reagent, then callsInitialiseHunger(...).InitialiseHunger(...):- computes total blood throughput across all hunger body parts
- sets each part's
PassiveConsumptionNutrimentso the whole creature consumes ~1 unit of nutriment per minute - scales all stomach
BodyFat.AbsorbedAmountvalues so the creature lasts roughlyNumberOfMinutesBeforeStarving - applies a ±25% random variance to stored fat so starvation timing differs between creatures
- calls
BodyPartListChange()again and updates effects toNormal.
Per-tick update
SystemUpdate() runs each mechanical tick.
- it calls
HungerCalculationMethod.CalculateHungerState(Base, this) - if the returned state differs from
CashedHungerState, it callsUpdateStatusEffects(...) UpdateStatusEffects(...)removes the old status effect and applies the new one viaStatusEffectManager
Nutriment consumption logic
This is the core of the current hunger simulation.
Body part demand calculation
Each HungerComponent contributes demand based on:
PassiveConsumptionNutriment(baseline metabolic consumption)BloodThroughputfrom theReagentCirculatedComponent
These are aggregated in NutrimentToConsume[reagent].TotalNeeded.
Heart efficiency and delivery
DefaultUnityStationHungerCalculation uses two limits:
heartEfficiency: sum ofCalculateHeartbeat()from all pumping devicesavailablePercentage: the fraction of the required nutriment currently present inhealth.reagentPoolSystem.BloodPool
The effective delivery fraction is:
effective = Mathf.Min(HeartEfficiency, availablePercentage)
This means the actual delivery is limited by circulation and by available blood nutriment.
Blood pool removal
For each nutrient reagent type:
- compute
neededas the total baseline demand - add extra demand for damaged body parts using
HealingNutrimentMultiplier - remove
amount = needed * effectivefromBloodPool
Body part state updates
For each related body part:
- if
effective > 0.1: - set
HungerModifier.Multiplier = 1 - set
HungerState = Normal - if the part is damaged, apply healing via
NutrimentHeal(...) - else:
- set
HungerModifier.Multiplier = 0.5 - set
HungerState = Starving
Damaged body parts increase nutrient demand and may be healed when sufficient delivery is available.
Overall creature hunger state
CheckHungerStateOnAll(...) determines the final state from all registered HungerComponents:
- if any body part is
Full, the whole creature isFull - otherwise the creature takes the worst state seen across parts
- if
Starvingis reached, the check stops early
This means a single full body part can mark the creature as full, while starvation anywhere propagates to the whole creature if no full part exists.
HungerState values
The HungerState enum maps the following states:
FullNormalHungryMalnourishedStarving
HungerSystem.GetAlertSOFromHunger(...) maps several of these states to HUD alerts, while GetStatusEffectFromHunger(...) maps them to status effects.
Current mappings:
Full→ fat alert /FatStatusEffectNormal→ no alert,NotHungryStatusEffectHungry→ hungry alert /HungryStatusEffectMalnourished→ malnourished alert /NotHungryStatusEffectStarving→ starving alert /StravingStatusEffect