Some behavior is too large and too common for a world to write: walking with the feet planted on the ground, a villager's mind choosing where to go, a face that blinks, looks about and smiles. A world does not write these. It names a whole behavior from a package, gives it its numbers, starts it, and adds a few rules of its own on top. This is the top layer of logic in the model, the library call by name. Capability lives in the packages, not in the worlds: when a world needs more, the package's owners extend it, and every world gains it.

A behavior is started as an animator is: start(walkgait.walker(body)). It is stepped every tick and writes, not one number, but a whole channel of a being: its pose, its actions, its face.

How you write it

A village of villagers. The package names are real; the function names after the dot (walker, patrol, alive) are proposed spellings. Lines marked as illustrations are examples only.

field walkable: navmesh.mesh = navmesh.build(from: [land, solids],
                   agent: (radius: 0.3 m, height: 1.9 m, climb: 0.4 m, slope: 40 deg),
                   cells: (across: 0.2 m, up: 0.1 m),
                   costs: (path: 1, other ground: 3))

part villager                                   // a design (designs.html)
  field who   : person                          // body, face, clothes
  field home  : place
  field mind  : npcbehave.mind                  // a whole behavior, by name, with its dials
  field gait  : animator = walkgait.walker(body, seed: 3)   // built once; it writes gait.pose

  part body: who
    being(self)                                 // a being: it acts each step (visitors.html)
  on(created, {
    start(gait);                                    // writes gait.pose: walk, run, idle; feet planted
    start(mind(body, on: walkable));                // writes the body's actions
    start(facerig.alive(body, blink, gaze, idle));  // each view's own, within 40 m of the eye
  })

  on(dusk, start(npcbehave.goHome(body, home, on: walkable)))    // illustration: a world rule on top

part village
  part round   : villager(who: people.averageMan, home: cottageFront,
                    mind: npcbehave.patrol(places: [cottageFront, playground, bridgeCrest], dwell: sim(8 s)))
  part gardener: villager(who: people.petiteWoman, home: (-7, 345),
                    mind: npcbehave.graze(around: home, leash: 5 m, pace: 0.7, seed: 7))
  part follower: villager(who: people.tallSlimMan, home: (-25, 342),
                    mind: npcbehave.follow(nearest(visitors), keep: 6 m))
  part shy     : villager(who: people.tallAthlete, home: (-26, 340),
                    mind: npcbehave.flee(nearest(visitors), alarm: 5 m, calm: 9 m, then: home))

part meadowSky
  part birds: boidflock.flock(groups: [17, 17], loners: 6, cruise: 9 m/s, speed: 6 ..= 12 m/s,
                 around: cottage, radius: 90 m, cruiseHeight: 16 m, clearance: 9 m, seed: 7,
                 look: bird, tint: birdTint),
              lives: client

part bird                                       // a design: the look of one bird, drawn once for each bird
  part body: birdBody(), material: materials.birdBack
  part beak: birdBeak(), material: materials.birdBeak
  part tail: birdTail(), material: materials.birdBack
  part wings: birdWing(), material: materials.birdWing     // a two-sided sheet, built flat

fn birdTint(k: count) color {                  // bird k's tint: a brightness and a warmth from its number
  let shade = 0.8 + 0.4 * fract(k * 0.618034);
  let warm  = 0.95 + 0.1 * fract(k * 0.381966);
  color(shade * warm, shade, shade / warm)
}

The behaviors

walkgait: the walk

A phase-driven biped walk with the feet planted on the ground, an idle layer (breathing, a shift of weight) and a crouch on a hard landing, for the humanoid skeleton of the bonepose package. The person's skeleton is 22 bones: the 19 of the humanoid, which the gait covers, and a jaw and two eyes, which the face rig drives. Of the 19 the gait writes 17: the two hands it leaves at rest. A hand-built figure takes its skeleton from bonepose.humanoid(height:). The call is walkgait.walker(body, seed:); the walker writes gait.pose, a handle on the pose it makes, which the being's own pose is built from (below). How it walks:

Content says only "walks like a person of this size": a walker is made for a being's height and updated each step from its speed and turn. It runs on every host, because every host must agree on the pose (spells and the bull-leap act on it).

The walker's options and settings. walker(body, seed:, ownIdle: true, hipSpan: 0.0955). seed sets when the first shift of weight comes (2 s plus the seed's remainder after 5, in seconds) and the phase of the breath. ownIdle: false leaves breathing and shifting to another behavior. hipSpan is the distance from the pelvis to each hip, as a share of the height: 0.0955 for the plain humanoid, and a person's own (a quarter of the hip breadth over the height: 0.0479 for the average man), which the person's asset supplies. Everything else is the gait's fixed numbers, here for a being of height H at speed v:

SettingValue
Stride0.80 * H * sqrt(v / 1.4 m/s), from 0.2 H to 1.4 H (speeds below 0.3 m/s count as 0.3)
Foot down (duty)0.62 of the cycle up to 1.4 m/s, falling in a straight line to 0.40 at 4 m/s, never less than 0.30
Foot rollthe heel strikes with the toe up 12°, flat by 0.12 of the stance, the heel lifts from 0.55 of it to 35° at toe-off
Foot rise while swingingH * (0.012 + 0.018 * clamp(v / 1.4, 0, 1.5))
Arm swing0.2147 rad (12.3°) at 1.4 m/s, in proportion to the speed up to three times that; forearm bend 0.2 rad plus 0.6 of the forward swing
Pelvisturns 4°, lists 2.5°, sways 0.0135 H, more with speed up to 1.5 times; as high as 97% of the legs' reach
Lean forward3° at 1.4 m/s, up to twice that, walking forward only; the head leans back 0.8 of it
Idlea shift of weight every 4 to 9 s, 0.025 H to one side or the other over 1.2 s; a breath at 0.25 Hz (the chest pitches 1°, the shoulders rise 0.004 H)
Stoppingtwo more steps, then both feet flat
Hard landingthe pelvis crouches (Physics: landing)

A gait begun afresh (after a landing, a get-up, a set-down, or leaving a seat) starts at mid-stance with both feet flat on the ground beside the pelvis, and moves off from there.

npcbehave: minds

What a non-player character decides and how it moves, in four layers that share only plain values:

LayerWhat it holds
DecidingBehaviorTree (with a Blackboard), UtilitySelector, StateMachine, each ticked at a fixed rate
SensingSightCone, hearing, Memory: what a character can see, hear and still believe
Steeringseek, flee, arrive, pursue, evade, wander, path following, obstacle avoidance, separation, alignment, cohesion, and blends of them: each an acceleration
CrowdsOrcaWorld: reciprocal collision avoidance for many agents

On top are five example characters made of the layers: the patroller, walking a loop of waypoints and pausing at each; the grazer, an animal that wanders, stops and grazes, staying near home (a behavior tree ticked at 5 Hz); the follower, keeping a set distance from the player; the fleer, running from the player when he comes within an alarm distance and coming home once he is beyond a calm one, the two radii keeping it from flickering at the edge; and the guard, holding a post, investigating a sound, looking about, going back, chasing what it sees (a state machine).

The package depends on no navigation mesh, physics or scene: the world is asked through functions the caller passes at each call (a path query, a ray cast, the nearest walkable point), borrowed for that call and never stored. Every random choice is a hash of a seed and a counter, so a run repeats exactly.

A mind writes a being's actions: the same actions a visitor's keys would produce. Nothing downstream knows the difference, which is why a being can be handed between a person and a mind (visitors.html).

facerig: a face that is alive

A rig turns a few numbers (which muscles are pulling, where the eyes look, what is being said) into the deformation of a face, for any face mesh described by data. It splits the work three ways: the vocabulary, the Facial Action Coding System's action units (AU12, the lip corner puller); the shapes, a morph target per unit, built from the unit table and a description of the face's landmarks rather than sculpted (a morph target is a stored displacement of the mesh mixed in by weight); and the life, which moves the numbers. Each frame the rig composes, in order: units set by hand; expressions, each closing on its goal weight exponentially, reaching 99% in the time asked for, so a face may be 60% happiness and 40% surprise; speech, lip sync from text or phonemes, with coarticulation; idle drift of brows, lids, lip corners and jaw by a few percent, and a breath at a quarter of a hertz; gaze, with saccades timed by the measured main sequence (37 ms plus 2.7 ms a degree) and the head following; and blinks, 15 to 20 a minute at rest, the lids closing faster than they open.

The face is each view's own: two viewers may see the same villager blink at different moments, and that is correct. A face farther than 40 m from the viewer's own being is not stepped.

boidflock: a population

A whole population in one call, with no content per member: forty birds, two flocks of 17 and six flying alone, over the meadow round the cottage, cruising about 16 m over the ground at about 9 m/s and never lower than 9 m, between 6 and 12 m/s, from seed 7. Each bird follows its seven nearest neighbors in sight (keep apart, match heading, keep together) and steers round the ground. The birds are never parts with paths (parts.html, open questions). Declared lives: client, each view flies its own flock and forty birds cost nothing on the network; shared, they would need exact stepping on every machine or the birds sent, because a flock is not a formula of time.

The flock's tuned numbers

Every number that shapes the flight is a named option with a default, so a call that leaves them out flies the island's flock. The accelerations are in meters a second each second; radius is the flock's radius above.

OptionDefaultWhat it sets
neighbors, sight, view7, 12 m, 270 deghow many nearest birds a bird follows, how far it sees, and the angle it sees (not those more than 135 degrees off its heading)
apart, separation1.8 m, 3.5birds closer than apart push each other away, the nearer the harder, with this weight
alignment, cohesion1, 0.9the weight of matching the neighbors' velocity (it relaxes to it in about a second) and of moving toward their center (strongest from 4 m off)
goal, wander2.2, 0.8the pull toward the group's traveling point, and a small drift that turns slowly so no flock is a conga line
steerLimit12the strongest acceleration a bird steers with
leashFrom, leashPull0.6, 8the share of radius inside which there is no pull, and the pull at radius
lookAhead, clearanceMargin[0.8 s, 1.6 s, 2.6 s], 6 mthe times ahead along its velocity at which a bird reads the ground, and how much clearer than clearance it wants to be ahead
lift, floorLift, sidePush, heightHold12, 16, 4, 0.25the push up when the ground ahead is too near; the push up when it is lower than clearance; the sideways push away from rising ground (more than 3 m of rise, read from points 6 m round); and the pull back to its cruise height, held to 2 either way
cruise, speedPull, climbCost9 m/s, 0.6, 0.35the speed it settles to (times its own share), the pull toward it, and how much of gravity a climb costs in speed and a dive gives back
turnRate, bank, bankLimit, bankEase8, 0.08, 0.9 rad, 0.15 show fast its facing follows its velocity; the bank for each unit of sideways steering; its limit; how fast the bank follows
flapRate, climbRate, diveRate, glideRate7 Hz, +6 Hz, -2.5 Hz, 1.5 Hzthe wing beat flapping level, added when climbing hard (3 m/s up) or taken off when diving at that rate, and gliding
flapReach, climbReach, glideReach0.75, +0.25, 0.15how far the wings beat, from 0 held out to 1
flapFor, glideFor0.4 s ..= 0.8 s, 0.3 s ..= 0.8 show long a burst of flapping and a glide last; a bird glides after a burst when it has speed to spare (within 1 m/s of cruise and not climbing) and flaps again when it falls below that or climbs
waypointEvery, waypointWithin, waypointHeight25 s ..= 40 s, 0.7, -3 m ..= 7 mhow often a group's traveling point moves; the share of radius it falls within; and its height about cruiseHeight
startRing40 ma group starts about a point this far from the center, round it by group number
speedVariety, steerVariety, heightVariety0.9 ..= 1.1, 0.85 ..= 1.15, -6 m ..= 6 meach bird's own share of the speeds, of the steering terms and of the cruise height, drawn from the seed; so is each bird's starting facing, wander and wing phase

What it means

A behavior is a value

field mind: npcbehave.mind holds a behavior: a kind from the minds library with its dials. It can be passed, chosen per placement and swapped. That makes "a whole behavior by name" a language feature rather than a special case. The dials include world references (places), not only numbers.

Started as an animator, one writer per channel

A gait, a mind and a face rig are started like a transition, and each writes one channel of the being every step. The animators' rule then gives meaning to a swap without a new word: start a new mind, and it takes over writing the body's actions from the old one, whose own state is dropped. A villager sent home at dusk is on(dusk, start(npcbehave.goHome(...))), an illustration (animators.html).

start of a behavior returns a handle like an animator's, so ended(h) works on it: on(ended(h), remove(self)). A behavior that goes to a place ends when it gets there. npcbehave.arrive(body, place, stopShort: 1.2 m, delay: sim(1.25 s), turnRate: 3.05 rad/s) waits delay, turns to face its way at turnRate, walks to place and stops stopShort short of it. A figure's own turn is a quarter turn in 1.5 s, 1.05 rad/s; the looking-glass's reflection turns 2 rad/s faster, 3.05 rad/s in all, which turns it about in a second.

Combined by the author's formula

Behaviors meet other effects through formulas, as values do. The being's pose is the gait's pose with the hail blended over five bones and a spell's pose added on top: derive(body.pose, add(blend(gait.pose, hailPose, weight: wave, mask: bones([...])), spellPose)). The face shows the larger of its own smile and its answering smile: derive(face.happiness, max(smile, reply)) (subscriptions.html; the reply is an illustration).

Library plus world rules

The library does the behavior; the world adds rules on top: a preference for the walkway (a route keeps to it unless leaving saves more than two thirds of the distance), the places and radii, answering another's hail (an illustration), going home at dusk (an illustration). A library may also raise events the content answers: footsteps on the gait's foot coming down (walkgait.footDown, to be built).

Where each runs

How it connects

Open questions

← Animators | Contents | Visitors and their actions →