Home
Quickstart
Let's spawn, move and remove an NPC. A "Non-Player Character" (NPC) is a video game character not controlled by the Player.
Desktop users can follow along.
Mobile users can watch the video then skip to the next section.
To follow along:
- open the Viewer on the right.
- click play and sync tabs.
Mobiles have the Viewer at the bottom; see the next section.
The video above steps through the commands below. They're a natural place to start, but far from the whole story.
spawn npc:rob at:$( click 1 )
move npc:rob to:$( click 1 )
move npc:rob to:"$( click 2 )"
move npc:rob to:$( clicks 2 )"
# respawn with skeleton key
spawn npc:rob at:$( click 1 ) granted:.
make npc:rob do:$( click 1 )
# dust to dust
remove robThen we've seen a Non-Player Character (NPC) driven by a Command Line Interface (CLI), which is why our site is called NPC CLI.
Duh!
Running a profile
There are two basic roles we play whilst developing NPCs: the Game Master controls the game, the Player experiences the game.
Desktop users might alternate between these roles, designing behaviours while trying to understand the Player's perspective. Mobile users should probably assume the Player role. This means they only need to open a terminal whose profile (initial script) sets everything up.
To use the quickstart profile in the Viewer, sync tabs.
source /etc/{util,core}.sh
source /etc/{util,core}.js.sh
import demoClickToMove from demo
awaitWorld
spawn npc:rob at:'{x:4.5,y:7.5}' \
as:soldier-0, granted:.
ptags always; click meta.floor |
demoClickToMove npc:rob &
/**
* @param {NPC.ClickOutput} input
* @param {NPC.RunArg} ctxt
* @param {{ npcKey: string }} [opts]
*/
export function demoClickToMove(input, { api, args, w }, opts = api.jsArg(args, { npc: 'npcKey' })) {
const npc = w.npc.get(opts.npcKey);
// catch permits override and ignores points too far from nav
npc.move({ to: input, close: 0.5 }).catch(() => {});
}Backdrop
The 3D worlds on this site implement Robert Pearce's Starship Geomorphs i.e. architectural drawings of starships blocks, connectable along their edges.
We use Eric Smith's PNGs to recursively define SVG symbols. Each hull symbol For example 102--hull.svg induces a 3D geomorph. Our maps amount to possibly edge-sharing rectangles, where each rectangle corresponds to a hull symbol.
For characters we use a basic Blender model, texture-mapped with simplifications of Minecraft skins.
Motivation
Our underlying motivations are:
-
NPCs indistinguishable from human-controlled characters.
A human Game Master makes this tractable. Can one human act indistinguishably from many humans playing individual parts? It also motivates realtime control systems.
-
Understand Recast-Detour.
Our NPCs are driven by Recast-Detour. We'll analyse this important C++ library in depth.
-
Generic scripting for three.js.
We interpret mvdan-sh syntax trees as JavaScript. In production one can run commands. In development, shell functions and processes are synced with JS modules.Technically this approach is independent of three.js.
What's next
We provide two blogs. Our main blog is about creating, combining and controlling NPC behaviour in realtime. Our dev blog is concerned with the underlying software (internal and external). They're separated in line with our underlying motivations.
main
🚧 Multiple NPCs
dev
🚧 Introducing the World