Skip to content

What vscript is

vscript is the programming language Knockout City’s gameplay is written in. The jump pads, the ball dispensers, the scoreboard, the hideout’s jukebox: each is a small vscript program. When you write one, the game runs it exactly the way it runs its own.

You don’t need to have programmed before. This page explains the three ideas everything else builds on. The next page has you write a script.

Everything in the world is an entityentity: A thing in the game world: a player, a ball, a trigger, an empty marker. On its own it only has a name and a place in the hierarchy; components give it behaviour.: a player, a ball, a jump pad, an invisible box that detects when someone walks into it. Entities are arranged in a tree, the hierarchyhierarchy: The parent/child tree entities form. Moving a parent moves its children with it.: an entity can have children, and children move with their parent.

An entity on its own does nothing. What it is and what it does comes from the componentscomponent: One script attached to one entity. The same script on ten entities is ten components, each with its own copy of the script's fields. on it.

Each .vscript file describes one kind of component. Put it on an entity and that entity gains its behaviour. Put the same script on ten entities and you get ten components, each with its own values.

Here is what a teleport pad looks like from the inside. Each chip is one script on that entity:

  • teleport_pad
    • padtransformmodeleffect
    • triggertransformbox_triggerteleport_pad← your script
    • arrivaltransformwhere travellers land
A pad built from four entities. The trigger entity carries the teleport_pad script, next to the box_trigger that detects overlaps.

The transform component says where an entity is. model draws it. box_trigger notices when something walks into it. teleport_pad is the script you’d write: it decides what happens then.

A script doesn’t run from top to bottom and stop. It waits. The engine calls it when something happens: when the component is created, once every frame, when something overlaps a trigger. Each of these is an eventevent: A function the engine (or another script) calls on your component when something happens: every frame, when it is created, when something overlaps a trigger., and your script handles the ones it cares about.

Once
type_createdset-up, e.g. switch on networking
createdthis component now exists
Every frame
early_tickbefore gameplay moves things
tickyour gameplay logic
late_tickafter movement is written
drawvisuals and debug drawing
At the end
destroyedclean up what you made
The lifecycle events, in the order the engine sends them. You write only the ones you need.

So writing a script is mostly answering the question: when this happens, what should my component do?

A script can compute things on its own, but to touch the game (find an entity, read a position, play a sound), it calls a nativenative: A function built into the engine, called with a $ in front: $entity.get_name(e). Natives are how a script reaches anything outside itself.: a function built into the engine. Natives are written with a $ in front:

let me: Entity = $entity.get();
let name: string = $entity.get_name(me);

The engine offers over two thousand of them. You’ll only ever use a handful, and the Natives reference lists them all.

For programmers

A script is a class and a component is an instance of it: fields are per-instance state, events are callbacks the runtime invokes, natives are the engine’s standard library. The syntax borrows heavily from Rust (let, let mut, fn, ->, switch arms with =>), but the semantics are simpler: no ownership, no generics, no closures, no structs of your own. Values carry their type at runtime, so a var can hold anything. Every script runs on the server and on each player’s game; see Server and client.