6. Mistakes everyone makes
A short list of what goes wrong in the first weeks, with the error each one produces. Come back to this page when a script does not do what you meant.
A dot where a colon should be
Calling a function on something needs a colon. With a dot the function is not told what to act on. The Console says something like bad argument #1 or attempt to index a nil value.
The other way round is a mistake too. Input:IsDown("Jump") is wrong, because Input is a plain table of functions and not an object. The API reference shows which is which: entity:Destroy() is written with a colon there, and Input.IsDown(action) with a dot.
Forgetting local
No error, and that is the problem. Two scripts that use the same global name change each other's values.
A typo in a name
A name that does not exist is nil, not an error. The error comes later and elsewhere, when the nil is used: attempt to perform arithmetic on a nil value (field 'helth'). Read the name in the message. It is usually the typo.
Setting up your code editor with the engine's definitions catches most of these as you type. See Code editor setup.
Counting from 0
Lists start at 1 and end at #items.
Using = for a comparison
One = assigns. Two compare. Not equal is ~=, and != is an error.
Joining with +
+ is only for numbers. "Score: " + 10 fails with attempt to perform arithmetic on a string value.
Joining nil fails too: attempt to concatenate a nil value. Wrap a value that might be nil in tostring(...) when printing it to find out what it is.
Expecting 0 and "" to be false
Only false and nil are false. Write the comparison you mean: if coins > 0 then.
Changing a copy
Reading a Vec3 from an entity or a component gives a copy. For one axis, self.Entity:Move(0, 1, 0) is shorter.
Forgetting deltaTime
OnUpdate runs once per frame, and frames are not the same length on every computer. Multiply anything that happens over time by deltaTime, the seconds since the last frame.
Waiting in a loop
Nothing else runs until OnUpdate returns. Check once per frame with if, or use Timer.After and Timer.Wait.
Using something that is gone
An entity that was destroyed cannot be used. The Console says so, with the line. When an entity might be gone, ask first:
Forgetting return at the end of the file
A script file must end with return and its table. Without it the engine has nothing to run, and the script does nothing.
Reading an error message
Assets/Scripts/Player.lua:27is the file and the line. Double-click the message in the Console to open it.attempt to index a nil valuemeans a dot or colon was used on something that isnil.(field 'Camera')names what wasnil: hereself.Camera, a field nobody filled in in the inspector.
Most errors are a nil where a value was expected. The message says which name. Work back from there to why it has no value.
Coming from another language
One trap in the table: a and b or c gives c when b is false or nil. Use a full if in that case.
Where to go from here
- Scripting overview: everything the engine gives a script.
- Recipes: short answers to common needs.
- Your first game: the tutorial, if you have not done it.
- The API reference, when you want to know exactly what a function takes.
- Programming in Lua by Roberto Ierusalimschy, the language's author, for the parts of Lua this course left out: metatables, coroutines and the string library.