Use 4 spaces instead of tab characters.
Add 1 space between blocks of codes unless the block contains only 1 line of code.
Example:
function Test()
print(“Hello”)
if GetName() == “Brian” then
print(“Brian!”)
else
print(“I don’t”)
print(“know you!”)
end
end
Use local whenever possible.
If the variable or function is only used in 1 file, make it local to that file.
“Private” functions are emulated using the following pattern:
local function RegenHealth(self)
self.health = 100
end
function Player:Respawn()
RegenHealth(self)
end
Constants have lowercase “k” as the first character.
local kMaxHealth = 100
or
Player.kMaxHealth = 100
Constants are conventional only. Nothing enforces constant variables.
Note: Prefer the local version in the above example whenever possible. Better not to expose private constants to the global namespace.
Only use parentheses in an “if” or “for” statement when necessary.
BAD:
if (self.buttonPressed) then
GOOD:
if self.buttonPressed then
GOOD:
if self.buttonPressed and (self.name == “Brian” or self.name == “Brian2”) then
Use double quotes.
BAD:
‘Hello World!’
GOOD:
“Hello World!”
The one exception to this rule in the codebase is enumerations. ‘They use single quotes’.
Standard spacing.
BAD:
local kTypeOne = “Monster”
local kTypeTwo = “BigMonster”
local kTypeThree = “Merchant”
GOOD:
local kTypeOne = “Monster”
local kTypeTwo = “BigMonster”
local kTypeThree = “Merchant”
While some people like formatting in the first example it makes searching the code base more difficult. When you can always expect 1 space between the =, you can search with confidence. Format for search, not for humans.
Multiline tables.
Braces should line up for better visualization. 4 spaces before each element.
BAD:
local bigTable = {
a = 1,
b = 2,
c = 3 }
GOOD:
local bigTable =
{
a = 1,
b = 2,
c = 3
}
Single line tables should be declared in this way:
local smallTable = { 1, 2, 3, 4 }
Functions are camel case starting with a capital letter.
BAD:
local function helloWorld() end
local function helloworld() end
local function Hello_World() end
GOOD:
local function HelloWorld() end
Comments.
Should be avoided unless they are explaining something the code doesn’t already explain.
BAD:
-- This function returns the player’s health.
function Player:GetHealth()
return self.health
end
Comments always have their own line.
BAD:
local health = self:GetPlayerHealth() * currentBonus -- currentBonus is based on shields.
GOOD:
-- currentBonus is based on shields.
local health = self:GetPlayerHealth() * currentBonus
Inheritance.
Inheritance should be kept to 1 level ideally. No more than 2 levels.
BAD:
class BaseClass
class LiveClass (BaseClass)
class LikesHatsClass (LiveClass)
class PlayerClass (LikesHatsClass)
GOOD:
class BaseClass
class PlayerClass (BaseClass)
Writing hotload compatible code
In order for hotload to work, you must make sure that you don’t keep data that needs to survive the hotload in variables initialized at file-level.
Basically, if you initialize ANY variable at file-level, you must treat that variable as if it is constant - any changes you make to it will go away on hotload.
This will NOT work:
kMyData = {}
function OnInitialization()
kMyData[key] = value
end
because on file reload, kMyData will of course be reset to the empty set, and unless you call OnInitialization whenever the file is reloaded, it will fail.
To fix it, simply do
function OnInitialization()
kMyData = {}
kMyData[key] = value
end
Ie declare the global variable in the function (that will only be called once). While it may feel better to “declare” the global variable at file-level, it will not work with hotloading.
If you really want to declare a global variable, you can do a conditional like this:
kMyData = kMyData or {}
Note! This will not work for file-scoped (local) variables, because when the file is reloaded, all variables declared local is destroyed before the file is reloaded. So
local kMyData = kMyData or {}
would always reset kMyData to {}.
The usual caveats apply to running with hotloaded codes; if things starts looking weird, restart the game. Also, sometimes you may have to create a new object to get the new behaviour (so if you are playing around with rifle animation code and the changes doesn’t seem to “take”, try going to the readyroom and back to create a new rifle).
This is because code is actually data. So for example, if you do this:
kMyFunction = kMyFunction or function()
Log("First version")
end
kMyFunction()
… then change to “Second version” and reload, you will still see “First version”.
When this is too annoying, you can lift out the relevant part of the code to ensure changes go through in existing objects - the name lookup will link in the new code.
local function CodeIWantReloaded()
Log(“First version”);
end
kMyFunction = kMyFunction or function()
CodeIWantReloaded()
end
kMyFunction()