Geloofwaardige NPCs bouwen: een architectuur voor emotie, intentie en reactie

Geloofwaardige NPCs bouwen: een architectuur voor emotie, intentie en reactie

Matthias GallMatthias Gall
·22 mei 2026·13 min. leestijd
Let op: dit bericht is automatisch vertaald.

Stel je voor dat je in VR staat, oog in oog met een personage. Het is geen metgezel die questmarkers volgt of een handelaar met dialoogopties op repeat. Het houdt oogcontact. De blik volgt de jouwe natuurlijk. Als je dichterbij stapt, verschuift de expressie subtiel en erkent de nabijheid. Als je praat, wacht het niet alleen op zijn beurt om te antwoorden. Het reageert.

Dat personage is Olivia Rhodes uit Lone Echo (2017) en Lone Echo 2 (2021), gemaakt door Ready at Dawn voordat het studio in 2024 sloot – toen het nog een dedicated AAA-team en het bijpassende budget had.

Video Voorvertoning

Als je op de afspeelknop klikt, wordt de YouTube-speler ingesloten en kunnen cookies van derden worden ingesteld en gegevens van YouTube worden verzameld.

Short van @MyPerseverance
Video Voorvertoning

Als je op de afspeelknop klikt, wordt de YouTube-speler ingesloten en kunnen cookies van derden worden ingesteld en gegevens van YouTube worden verzameld.

Short van @CyberStuffXR

We bouwen fundamentele NPC-systemen voor hergebruik in immersieve XR-producten en hebben bestudeerd wat studio's als Ready at Dawn bereikten – en ons afgevraagd:

Wat is de minimale architectuur om dat gevoel van aanwezigheid te creëren binnen onze eigen middelen?

Ons antwoord: meerdere onafhankelijke subsystemen die in een gedeelde prioriteitspipeline voeden, zodat geen enkele controller het hele personage hoeft te bezitten.

De uitdaging

De meeste NPC-systemen in games draaien om één ding: een behavior tree die bepaalt wat er hierna gebeurt. De NPC patrouilleert. Vecht. Zoekt dekking. Misschien zegt hij een dialoogregel als je langslopen.

Wat Olivia Rhodes levendig laat voelen, is de kloof die de openingscène illustreert: macrobeslissingen tellen minder dan microgedrag – blik, expressie, het gevoel dat ze reageert.

In een flatscreen-game kun je veel verbergen. Stijve animaties, dode ogen, robotische blik: cameradistance vergeeft alles. In VR sta je naast deze personages. Elk ontbrekend microgedrag valt niet alleen op – het is uncanny.

De vraag werd dus: hoe bouw je een NPC die niet alleen dingen doet, maar ook lijkt ze te beleven? Nabijheid verhoogt de lat, en daarom is de architectuur hieronder gebouwd.

De architectuur

Drie lagen:

Architectuuroverzicht

Laag 1: de behavior tree (dispatcher)

Een gedeelde NPC-state (idle, combat, follow, dialogue, enz.) stuurt een behavior tree die delegeert naar subtrees. Idle triggert affordance-scan of rondlopen. Combat triggert dekking zoeken en schieten. Follow houdt de NPC bij een doel met afstandsdrempels die flikkeren tegengaan als de speler beweegt.

Het belangrijke: de behavior tree beslist wat te doen. Niet hoe het eruitziet tijdens het doen. Die stuurt het gezicht niet. Niet de ogen. Niet de idle-blikken. Die vallen later, als gedrag en wereldcontext hun inputs hebben aangeleverd.

Laag 2: Smart Objects (affordances)

Tussen strategie en expressie staat de wereld zelf. In de scene geplaatste SmartObject-componenten dragen tags («PointOfInterest», «Cover», «Heal», «Comfort»), prioriteitswaarden en actiesequenties (Enter, Run, Exit). De huidige state van de behavior tree bepaalt welke tags worden gezocht. Een idle NPC zoekt «Comfort»-objecten. Een combat-NPC zoekt «Cover».

De scoreformule is simpel: prioriteit min afstand. Eén prioriteitspunt is ongeveer een meter loopinspanning. Een hoog-prioriteit object ver weg concurreert met een laag-prioriteit object dichtbij. De NPC maakt impliciet een trade-off bij elke scan.

De interactiestroom is async: navigeren naar het object, Enter-acties, Run-acties (onderbreekbaar bij iets belangrijkers), dan Exit-acties. Een navigatietime-out voorkomt dat NPC's permanent vastzitten op iets wat ze niet kunnen bereiken.

Dit is James J. Gibsons affordance-theorie toegepast op game development. Objecten hebben geen gedrag. Ze hebben affordances. Wat de NPC ermee doet hangt af van wat hij nu nodig heeft.

Een stoel kiezen, dekking, of een interessant punt om naar te kijken – dat zijn inputs downstream. De intent-pipeline negeert ze niet; ze concurreren met elke andere provider om dezelfde kanalen.

Laag 3: de intent-pipeline (arbiter)

In de intent-pipeline convergeert alles. Gedrag, affordances, emotie, dialoog, schade: elk subsysteem schrijft naar eigen kanalen; prioriteit lost op wat de speler daadwerkelijk ziet. AnimationIntents is een struct-gebaseerde bus met kanalen als UpperBody, FullBody, Face, Look en Eyes. Look-, emotie- en knippercontrollers bezitten elk een kanaal; anderen vullen de rest.

Elk kanaal gebruikt last-writer-wins op prioriteit. Willen twee providers in hetzelfde frame de bovenkant van het lichaam, wint de hogere prioriteit. De bus doet de merge.

Bij dialoogstart wordt de speler geregistreerd als hoog-prioriteit look-target: blik fixeert, idle-scan stopt. Loopt de NPC, wordt het pad vooruit het look-target tot aankomst, dan keert aandacht terug naar de ruimte. Een target afmelden volstaat om controle los te laten – zonder hand-off-code tussen systemen.

Diepe duik: emotie

Het emotiesysteem leunt zwaar op Russells circumplex-model van affect: emotionele toestanden op twee assen, arousal (kalm tot geactiveerd) en valence (onaangenaam tot aangenaam). Het is een nuttige vereenvoudiging, geen letterlijke implementatie. Nervousness (0 tot 1) en Happiness (-1 tot 1) zijn onze game-vriendelijke versies van die assen.

Ze sturen gezichtsblendshapes of animatorparameters via een data-gedreven EmotionProfile. De mapping is volledig designer-tunable: positieve happiness kan een glimlach drijven, hoge nervousness opgetrokken wenkbrauwen, enz. Verschillende NPC-types kunnen totaal verschillende profielen hebben. Zelfde code, andere data.

Het systeem draait in twee lagen:

Mood is continu. De twee assen drijven in de tijd. Gebeurt er iets (schade, dialoogevents, script-triggers), verschuiven de waarden. Optioneel drijft recovery ze langzaam terug naar geconfigureerde baselines. Een beschoten NPC blijft even nerveus – hij springt niet meteen terug naar kalm zodra combat eindigt. Herstel is langzaam en voelt natuurlijk.

Transient Emotes zijn eenmalige gezichtsuitdrukkingen op naam: «Surprise», «Pain», «Flinch». Elk heeft een attack-sustain-decay-envelope (zoals ADSR in audio). Hogere-prioriteit emotes overschrijven lopende. Een pain-emote onderbreekt een dialoogexpressie. Als de pain-emote uitfadet, keert de onderliggende expressie terug.

Interessant voor procedurele spanning: knipperfrequentie volgt direct uit arousal. Een kalme NPC knippert elke paar seconden. Een doodsbange knippert snel. Idle-look-frequentie schaalt hetzelfde: het nerveuze personage scant constant hoeken en deuren; het kalme kijkt af en toe ontspannen rond.

Video Voorvertoning

Als je op de afspeelknop klikt, wordt de YouTube-speler ingesloten en kunnen cookies van derden worden ingesteld en gegevens van YouTube worden verzameld.

Video: stemming via nervousness en happiness in expressie en knipperen
Video Voorvertoning

Als je op de afspeelknop klikt, wordt de YouTube-speler ingesloten en kunnen cookies van derden worden ingesteld en gegevens van YouTube worden verzameld.

Video: idle-look-systeem (groene look-targets) en overrides (roze look-target)

Diepe duik: look-systeem

De look-controller implementeert een multi-source prioriteitscascade. Drie niveaus:

  1. Override (hoogste): gescripte look-targets uit cinematics of sequenties
  2. Registry-targets: elk geregistreerd object, inclusief dialoogpartners, navigatiewaypoints en scene-objecten gemarkeerd als interessant
  3. Idle-gedrag: willekeurig rondkijken, frequentie gedreven door de Nervousness-as hierboven

Implementatiedetails die meer tellen dan je verwacht:

View-cone-filtering. Tijdens navigeren overweegt de NPC alleen look-targets binnen ±90° van zijn voorwaartse richting. Hij draait zijn hoofd niet abrupt achterom – hij moet eerst draaien.

Cooldown na navigatie. Na aankomst is er een kort venster zonder target-wissel. Dat voorkomt het schokkerige «aankomen en meteen het dichtstbijzijnde interessante fixeren» dat NPC's robotachtig laat voelen.

Hysterese in lichaamsoriëntatie. De body-orientation-controller draait de torso naar hoog-prioriteit look-targets. Met twee drempels: engage bij grotere hoek, release bij smallere. Staat het target net naast, trilt het lichaam niet heen en weer – het commit aan de draai en houdt vast tot het target duidelijk weg is.

Dialoog-blik is niet speciaal uitgewerkt. In VR wordt de hoofdpositie van de speler geregistreerd als look-target. Dat is alles. Dezelfde cascade die combat-targets en idle-wandelen afhandelt, handelt ook oogcontact tijdens dialoog. Het VR-hoofd is gewoon een andere registry-entry.

Diepe duik: damage cascade

Dit is het lastigste geval om alleen uit tekst te waarderen. Je wilt in VR op armlengte van de NPC staan wanneer hij geraakt wordt. Veel van ons B2B-werk heeft geen combat nodig; de cascade is nog steeds de stresstest voor compositie.

Eén schade-event wordt naar alle listeners gebroadcast. Dezelfde ontkoppelingsgedachte als de intent-pipeline, maar voor events in plaats van per-frame kanalen. Zeven providers reageren onafhankelijk:

  1. Mood-shift. Happiness daalt. Nervousness piekt. Gezichtsuitdrukking verandert, knipperfrequentie stijgt. Het personage ziet er geschokt uit en blijft dat tot mood recovery het langzaam terugtrekt.

  2. Pain-emote. Een transiënte gezichtsuitdrukking op hoge prioriteit overschrijft wat het gezicht deed. Attack snel (onmiddellijke grimas), sustain kort, decay laat de onderliggende mood-expressie terugkomen.

  3. Pain-geluid. Een kreun via de mond-audiobron van de NPC, dezelfde als voor spraak. Varieert met schade-intensiteit: een schamp versus een zware klap.

  4. Ragdoll-fysiek-impuls uit richting en magnitude in de schadedata.

  5. Blood VFX: deeltjes op de wond plus bone-parented decal dat met het lichaam groeit en vervaagt.

  6. IK-schadereactie. Handen naar de impact; een data-gedreven map koppelt wondpositie aan IK-targets, met ellebooghints zodat de arm niet onnatuurlijk vouwt.

  7. Impact look-target. Een tijdelijk look-target op de wond. De blik van de NPC gaat daarheen. Na enkele seconden afmelden – blik keert terug naar wat eerder belangrijk was.

De volgende video laat zien hoe dit samen speelt.

Video Voorvertoning

Als je op de afspeelknop klikt, wordt de YouTube-speler ingesloten en kunnen cookies van derden worden ingesteld en gegevens van YouTube worden verzameld.

Video: schadecascade – hoe de systemen samenwerken

Zeven providers, één event. De NPC houdt in niet als één enkele «hit reaction»-clip, maar als compositie van gezicht, ogen, lichaam, handen en aandacht die echte pijn vormen. Mood recovery (hierboven beschreven) doet de rest in de volgende tien tot vijftien seconden.

Zo voelt «levend».

Waar het schittert, waar het rekt

Nieuwe subsystemen passen netjes: interface implementeren, prioriteit zetten, de bus doet de rest. Emotion profiles, hit-to-effector-mappings en SmartObject-componenten zijn data-gedreven – designers tunen in de editor zonder code.

De architectuur gaat uit van goede assets eronder. Blendshapes, locomotion-clips en rig-setup moeten goed in DCC-tools staan. Het framework kan goed arbitreren, maar redt geen gezicht zonder expressie-sculpting, clips zonder layering of EmotionProfile-tuning die alleen op één mesh werkt.

De resterende gaten zijn tuning, niet design. Affordance-scoring houdt nog geen rekening met zichtlijn of sociale context. Soms hebben de visuele behavior tree in Game Creator en code-gedreven subsystemen zorgvuldige coördinatie nodig om synchroon te blijven.

Compositie als feature

Wat het framework bewust niet levert, is betekenis: geen wereldmodel, geen geheugen, geen redeneren over de toekomst. NPC's hoeven niet te begrijpen waarom ze iets doen – ze moeten er zo uitzien alsof ze het doen. Lichaamstaal, emotionele textuur en reactiviteit zitten in het framework; doel en verhaal in de applicatie. Die scheiding maakt het herbruikbaar over het XR-werk dat we bouwen.

Werk met ons

Geloofwaardige NPC's zijn niet alleen voor games. Beursgidsen, trainingssimulaties en productdemo's winnen wanneer een personage oogcontact houdt, op nabijheid reageert en herstelt van events in plaats van meteen terug naar idle te springen.

Als je een XR-ervaring plant waar aanwezigheid even belangrijk is als functionaliteit, neem gerust contact op. We verkennen graag hoe dit bij je project past of bouwen samen een prototype.

Gebruikte assets

Het NPC-framework voor Unity gebruikt de volgende betaalde assets van de Unity Asset Store:

  • Character Customizer and Character Customizer Modern Clothes Pack by Jordbugg voor het menselijke personagemodel
  • Guardians Bundle I by Dmitriy Dryzhak voor het troll-model
  • Game Creator 2 by Catsoft Works voor algemene triggers en actions, behavior trees en dialoog
  • FinalIK by RootMotion voor full-body biped IK
  • Pupett Master by RootMotion of Ragdoll Animator 2 by FImpossible Creations voor ragdoll-fysica
  • SALSA LipSync by Crazy Minnow Studio, LLC voor gezichtsanimatie
  • VR Interaction Framework by Bearded Ninja Games voor VR-input (optioneel)

Voor de demonstratie gebruiken we ook gratis assets van Sketchfab:

Meer van de blog