Plays a performance script - speech and stage directions in one string - against a character. More...
| Import Statement: | import Clayground.Character3D |
A performance script is written the way a director writes one: what is said and what is done, in the order it happens. Directives sit between asterisks and everything else is spoken.
Performance {
id: perf
performer: prof
searchRoot: view3d.scene
}
Component.onCompleted: perf.play(
"*point at battery* This is the battery. (2s) " +
"*face viewer* *happy* It stores the energy our circuit spends.")
The vocabulary and the exact grammar are documented in the plugin's README; the parser is scripting/performancescript.js and is Qt-free, so a script can be checked without a running engine.
Timing. Directives are instant: they are dispatched and the script moves on. Only two things consume time - a *pause* and a spoken line. A line ends when its time hint runs out if it has one, otherwise when the performer stops reporting that it is talking, and in either case no later than a backstop timeout, so a performer that never reports an end cannot hang the script.
The performer is duck-typed. Nothing here requires Character: a cue is dispatched to whatever method of that name the performer has, and a cue the performer cannot do is skipped and recorded rather than being an error. That is what lets the same script drive a plain Character and a kit's own character component unchanged.
Observability. A script is verified by reading state, not by watching it: running, done, cueIndex, currentCue, firedLog, skipped and errors are all readable from an inspector or a headless render, and debug narrates every cue to the console.
Marks. *mark the collector* - or *mark the battery, the switch* - resolves its names the way *point at* resolves its target and publishes the points in marks. Nothing here draws them: a sequencer has no view, and the overlay that does belongs to whatever is showing the scene. They live for the length of the line the cue precedes and are dropped when it ends, which is the whole of the rule an author has to hold: a marker says "this one, now", not "this one, still".
See also Character.
cueCount : int |
How many cues the current script has.
cueIndex : int |
Index of the cue currently being played, -1 before the first one.
currentCue : string |
The current cue as a one-liner, e.g. "point at battery".
debug : bool |
Narrates every cue to the console as it fires.
[perform] 1234ms cue 3/7: point at battery
done : bool |
True once the last cue of the last script has fired.
errors : var |
Parse errors of the last play(), each {at, directive, message}. A script with errors is not played.
extraVerbs : var |
Extra directive names the parser accepts, lower case.
Directives only one character has. They parse into custom cues and are dispatched to a handler registered with registerVerb(); an unregistered one is skipped and the customCue signal is emitted. registerVerb() adds to this list on its own, so setting this property is only needed for verbs handled through the signal.
firedLog : var |
Every cue that fired, each {ms, cue}, with ms measured from play(). The record of what actually happened, for a script that has to be debugged without being watched.
markNames : var |
marks : var |
The world points a *mark ...* cue currently raises, as vector3d, resolved exactly as a point target is. Empty whenever nothing is marked. Bind an overlay to it.
performer : var |
The character that acts the script.
Duck-typed. Cues call, when present: say(), tell(what, clip), setEmotion(), pointAt(), presentAt(), lookAt(), turnTo(), faceViewer(), thumbsUp(), gesticulate(), stopGesture(), stopSpeaking() / quiet(). Speech end is read from talking if the performer has it, otherwise from speaking.
resolveTarget : var |
Optional function(name) returning a vector3d, or null.
Overrides the searchRoot walk for scenes that name their subjects themselves. Returning null skips the cue - see skipped.
searchRoot : var |
Where the default target resolver looks.
A target name in a script (*point at battery*) is a QML objectName. The default resolver walks this node's children recursively for it and uses its scenePosition. Ignored when resolveTarget is set.
skipped : var |
Cues that could not be carried out, each {cue, reason} - an unresolved target, a verb the performer does not have, a custom verb whose handler threw. The script continues past every one of them.
spoken : bool |
Whether lines without a clip are voiced at all.
True routes them through the performer's say(), which for a Character means the speech engine - text-to-speech where the platform has it. False keeps the lab silent: a performer with tell() shows and mouths the line without asking for audio, which is the professor's narration mode. A line with a voiceOf clip is always played; this switch only decides what a bare line does.
viewerPosition : var |
Where "viewer" is: a vector3d, or a function() returning one.
Needed for *look at viewer*, and for *face viewer* when the performer has no faceViewer() of its own.
voiceOf : var |
Optional function(sayIndex) returning a clip url per spoken line.
The seam for pre-rendered narration: when it returns a non-empty url and the performer has tell(), the line is played from that file instead of being synthesised. sayIndex counts spoken lines in the current script from 0.
cueFired(string type, string arg) |
Emitted as each cue is dispatched: type is the cue's kind ("point", "say", "face", "emotion", ...) and arg its argument (the target name, the spoken text, the emotion). The hook for choreography AROUND the performer - a scene that moves its camera in when the character turns to the viewer listens for type === "face" here rather than polling state.
Note: The corresponding handler is onCueFired.
customCue(string verb, string arg) |
Emitted for a custom cue that has no handler registered.
Note: The corresponding handler is onCustomCue.
finished() |
Emitted after the last cue of a script. Not emitted by stop().
Note: The corresponding handler is onFinished.
int estimateMs(string text) |
How long the performer is expected to take over text, in ms.
The speech engine's own estimate when it can be reached (performer.character.speech), otherwise 72 ms per character - the rate measured off pre-rendered narration. This is what the backstop timeout is built from; a line with a time hint does not use it.
bool play(string script) |
Parses script strictly and plays it from the first cue.
Returns false and plays nothing when the script has parse errors; they are then in errors and summarized with one console error. Stops whatever was running first.
bool playFrom(int index) |
Plays the script parsed by the last play() from cue index.
A debugging aid: it skips straight to the cue in question instead of sitting through the ones before it. Returns false when there is nothing parsed, or the index is past the end.
void registerVerb(string name, var handler) |
Teaches the parser a directive and what to do with it.
name is a lower-case directive name, possibly several words ("board out"); handler is called with the directive's argument (the empty string when it has none). A handler that throws does not stop the script - the cue is recorded in skipped.
void stop() |
Halts the script where it is and silences the performer.
Disarms every timer, ends any speech (stopSpeaking() or quiet()) and drops any gesture (stopGesture()). Does not emit finished().