Scripting overview
Game logic is written in Lua 5.4, or in TypeScript. A project picks one when it is made. A Lua script is a .lua file that returns a table. A TypeScript script is a .ts file that ends with export = DefineScript({ ... }), and the editor compiles it to Lua: see TypeScript. Every example on these pages has a tab for each language, and the one you pick holds on every page.
If Lua is new to you, start with Learn Lua. To see scripts at work in a whole game, do the tutorial.
In Lua, the first line is for your code editor. It says what self is, so the editor can complete self.Entity. for you. In TypeScript, DefineScript does the same for this. See Code editor setup.
Add it to an entity with Add Component > Scripts in the inspector. There, Add script adds a slot to pick a script file for, and New script writes a new file in Assets/Scripts, named after the entity, and puts it on the entity in one click. One entity can have several scripts, and one script can be on many entities. Each entity gets a copy of its own.
The functions the engine calls
All are optional.
Inside them, self.Entity (this.Entity in TypeScript) is the entity the script is on, and self is that entity's own copy of the script, to keep values in.
Fields
The Fields table lists values the editor shows in the inspector, so each entity can have its own. Read one as self.Speed, or this.Speed in TypeScript.
A field shows the script's default until it is edited on an entity.
Pointing at entities and assets
Field.Entity() is a box in the inspector that takes another entity of the scene. Pick one from the list, or drag one in from the hierarchy. In the script it is that entity, or nil (undefined in TypeScript) when none was set.
The asset kinds are Field.Prefab(), Field.Material(), Field.Texture(), Field.Audio(), Field.Scene(), Field.Mesh(), Field.Font() and Field.Animator(). Pick from the list, or drag one in from the Assets panel. In the script it is an asset handle, which is empty when none was set: check with handle:IsValid().
Use these in place of Scene.Find("Some Name") and Assets.Find("Some/Path"). A name or a path in a script breaks without a word when the entity or the file is renamed. A field keeps pointing at the same thing.
When a group of entities is copied, a field that pointed at one of them points at its copy.
Limits and tips
Field.Number(default, { Min = 0, Max = 20 }) shows a slider. Any Field function takes a table with Tip, which the inspector shows when the mouse rests on the field:
Reading and changing an entity
self.Entity.Position.y = 5 (or this.Entity.Position.y = 5) changes a copy and does nothing, because reading Position gives a copy. Use Move, or assign a whole vector.
What scripts cannot do
There is no io, os or dofile. Files are reached through the engine: SaveData for saves, and fields or Assets.Find for assets. require works, for the project's own scripts.
Sharing code between scripts
Put functions that several scripts need in a file of their own. In Lua it returns a table. In TypeScript it exports them:
Other scripts get that table with require, giving the path from the project's folder. In TypeScript, import it from beside the script:
The file is run once, and every script that requires it gets the same table. Editing it while the game plays reloads it with the scripts that use it.
Errors
A script error is logged with its file and line, and shows in the editor's console. An error in OnUpdate or OnFixedUpdate switches that script off on that entity until the scene restarts, so a broken script logs once and not every frame.
Using an entity after it was destroyed is an error at the script's line, never a crash. Check entity:IsValid() when unsure.
Hot reload
Save a script while the game plays in the editor and the new code is in use at once. Each running copy keeps its values and gets the new functions.
Next
- Entities and components
- Recipes for common tasks
- The API reference lists every call