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:
- One phase drives both legs, advancing by the distance the being travels divided by its stride, so a faster walk takes longer, slower steps and the feet never skate: a foot in stance is held at a point of the ground. A foot is down for about 0.62 of the cycle walking, less running.
- A foot rolls: the heel strikes with the toe up 12°, the foot goes flat, the heel lifts and the ankle goes over the ball.
- The walk follows the ground: when a foot starts to swing, the ground's height is asked at the point it will land.
- The pelvis is as high as both legs allow (97% of full reach), so it dips in double support and rises over the stance foot by itself; it sways, turns and tilts with the stride, the chest turns the other way, the arms swing against the legs.
- Stopping finishes two more steps, each landing beside the pelvis, then stands with both feet flat; turning on the spot steps. Walking backward turns the roll about (the forefoot lands first).
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:
| Setting | Value |
|---|---|
| Stride | 0.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 roll | the 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 swinging | H * (0.012 + 0.018 * clamp(v / 1.4, 0, 1.5)) |
| Arm swing | 0.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 |
| Pelvis | turns 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 forward | 3° at 1.4 m/s, up to twice that, walking forward only; the head leans back 0.8 of it |
| Idle | a 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) |
| Stopping | two more steps, then both feet flat |
| Hard landing | the 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:
| Layer | What it holds |
|---|---|
| Deciding | BehaviorTree (with a Blackboard), UtilitySelector, StateMachine, each ticked at a fixed rate |
| Sensing | SightCone, hearing, Memory: what a character can see, hear and still believe |
| Steering | seek, flee, arrive, pursue, evade, wander, path following, obstacle avoidance, separation, alignment, cohesion, and blends of them: each an acceleration |
| Crowds | OrcaWorld: 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 groups.
groups: [17, 17]are flocks that share a traveling point; each of theloners: 6is a group of its own with its own point. The number of birds is the groups' sizes added to the loners (40). around:andradius:.around:is the place the flock keeps over (a part stands for its place, and only itsxandzcount); it is not the part optionhome:, the place a part is brought back to.radius:is not a wall. It is a soft leash: a bird feels nothing within 60 per cent of it (54 m for 90 m), and beyond that a pull toward the center that grows as the square of the excess, 8 m/s each second at 90 m; a bird may go farther. The points each group travels to are chosen within 70 per cent of it (63 m), and change every 25 to 40 s.- The look.
look: birdnames a design, the look of one bird, which the flock draws once for each bird as an instance: the birds are still not parts, and the design's parts are not in the tree. The flock gives each instance its place, its facing (rotation(forward:, up:), banked into a turn, Values: rotations) and the phase and reach of its wing beat.tint: birdTintis a function of the bird's number,fn(k: count) color, that gives each bird its own tint (Materials: the tint); here a brightness from 0.8 to 1.2 and a little warmth. - Time. The flock steps on the simulation clock in fixed sub-steps of 1/30 s (a step that is not a whole number of them ends with a shorter one), each step's length scaled by the day's rate: a paused day stops the birds, a day at ten times flies them ten times as fast.
- Where it appears. The flock is a child of
meadowSky, and a part's children are drawn only while it is (Parts): the birds are gone wherever that sky is not (the labyrinth beneath the island); night and rain do not hide them. - The seed. The flock is made by the package's own hash of the seed and a bird's number, not by the
hashof Expressions, so the sameseed:gives the same flock as the island's, wherever the flock is made.
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.
| Option | Default | What it sets |
|---|---|---|
neighbors, sight, view | 7, 12 m, 270 deg | how 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, separation | 1.8 m, 3.5 | birds closer than apart push each other away, the nearer the harder, with this weight |
alignment, cohesion | 1, 0.9 | the 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, wander | 2.2, 0.8 | the pull toward the group's traveling point, and a small drift that turns slowly so no flock is a conga line |
steerLimit | 12 | the strongest acceleration a bird steers with |
leashFrom, leashPull | 0.6, 8 | the 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 m | the 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, heightHold | 12, 16, 4, 0.25 | the 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, climbCost | 9 m/s, 0.6, 0.35 | the 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, bankEase | 8, 0.08, 0.9 rad, 0.15 s | how fast its facing follows its velocity; the bank for each unit of sideways steering; its limit; how fast the bank follows |
flapRate, climbRate, diveRate, glideRate | 7 Hz, +6 Hz, -2.5 Hz, 1.5 Hz | the wing beat flapping level, added when climbing hard (3 m/s up) or taken off when diving at that rate, and gliding |
flapReach, climbReach, glideReach | 0.75, +0.25, 0.15 | how far the wings beat, from 0 held out to 1 |
flapFor, glideFor | 0.4 s ..= 0.8 s, 0.3 s ..= 0.8 s | how 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, waypointHeight | 25 s ..= 40 s, 0.7, -3 m ..= 7 m | how often a group's traveling point moves; the share of radius it falls within; and its height about cruiseHeight |
startRing | 40 m | a group starts about a point this far from the center, round it by group number |
speedVariety, steerVariety, heightVariety | 0.9 ..= 1.1, 0.85 ..= 1.15, -6 m ..= 6 m | each 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
- The gait on every host, because poses are acted on (spells, the bull-leap).
- Minds in the simulation (the server, when one is connected).
- The face in each view, near the eye.
- The flock in each view, when declared
lives: client.
How it connects
- Animators: behaviors are started and replaced as animators are.
- Visitors: beings, and actions a mind or a person produces.
- Subscriptions: poses and faces combined by formula.
- The vocabulary: package names a world may use.
- A villager: the worked example.
Open questions
- A behavior and a formula both writing the pose. The walk writes
gait.pose, a handle on the pose it makes, and the being'sbody.poseis aderivefrom it, so there is one writer for each (A villager). What other behaviors name as their pose (a mind writes actions; a face rig writes its units) is each package's contract, which should say which values it writes. - Behaviors as animators. Minds, the gait and the face rig fit "one motion per value" only if a being's actions, pose and face count as values. The animators page lists value animators only; it should say so, and what happens to a mind's own state when it is replaced.
- "Near the eye." The face matters only within 40 m of the viewer; the notation can say where a value lives but not that a behavior matters only near the viewer.
- A library's events. A library's contract must list the events it raises (footsteps).
- Kind names.
patrol,graze,followandfleeare a library's vocabulary. A world that names a kind the library lacks meets the unknown-name problem in its purest form. - Agreement between products. The walkable map's agent (0.4 m step, 40°, 0.3 m from walls) must agree with the body's capsule (0.4 m step, slides at 45°, 0.24 to 0.27 m round); the model has no way to say any of them is the same fact.
- Locomotion as a choice. The capsule, gravity and the step height are built in. A world with no ground, or with gravity per surface, needs to name its locomotion as it names its gait.
- The ceiling on world logic. A world that wrote a behavior by hand because the library lacked it (a follower that finds routes) shows the limit: the rule that worlds compose and do not build tools says it belongs in the library.
- A shared flock needs exact stepping on every machine or the birds sent; the choice of where a value lives does not yet say what it costs per kind of behavior.
- State stepped each tick: answered (working hypothesis, 20 Oct) by an animator the world writes as a step function,
stepAnimator(Animators: your own). The wing beat is part of each bird's state, worked out in the same step. Bending each bird's wing mesh to match it is a case of posing a being from code (below). - Posing a being from code (a design question): a pose built from bone turns, what
adddoes with a delta pose laid over the gait's, and how a computed pose reaches a visitor's avatar, which is not in the content's tree. It includes the poses written by a ragdoll, a clip or a step: the ragdoll's, a get-up clip's and a step animator's pose are written by the engine, and how content names and reads them is open.