Scripting Helpers is winding down operations and is now read-only. More info→
← Blog Home

Two scripting tips which will make scripting easier

This small blog post is about some tips on scripting. There are several handy mechanics in Lua which you can use to make your code much shorter, for example. We will also talk about defensive coding and why that is useful in creating errorless scripts which can be used by everyone.

Using the or, and and not operators to shorten code

Most people use these operators only for booleans (false and true). However, they can also be used with non-boolean values. Lua considers true as true and everything else except nil and false as truthy - meaning it will evaluate it as true in some statements, such as when used with the or, and and not operators. It’s only evaluated as false when it’s nil or false.

a or b

Returns a if that’s true or truthy, else return b

a and b

Return b when a is true or truthy, else return a

not a

Returns false if a is true or truthy, else return true

Take a note of the fact that these do not necessarily return a boolean: they return the actual values you put in those operators. Using this, you can use them to shorten if-then blocks.

The not not a statement converts a to a boolean. This is handy in some cases. The returned value will either be false or true but never something else. It's not nil either.

What’s also cool is that the a or b variables are only evaluated when necessary. You can use this to "short-circuit" certain statements:

if Mouse.Target and Mouse.Target:FindFirstChild("Edible") then 
    print("The target is edible!")
end

In this code, Lua first evaluates if Mouse.Target is considered truthy. If that’s the case, it will evaluate the second statement. In other words, this can never error!

Defensive Coding

Above example is also an example of defensive coding. If you write defensive code, you make sure every case is handled correctly. If you script fast, you will often end up creating code in which you are sure something is never called with, for example, nil. Take this little function for example:

function IsEdible(target)
    return target:FindFirstChild(„Edible”) ~= nil
end

Note that the function returns a boolean, as ~= returns a boolean. There is nothing wrong with this code. However, if you call it with nil, it will error. It’s a good practice to do this:

function IsEdible(target)
    return (target and target:FindFirstChild(„Edible”)) ~= nil
end

Note that due operator precedence we must use () (brackets) here - and has a lower priority than ~= and thus first target:FindFirstChild("Edible") ~= nil would be evaluated if we would not include brackets. This is not what we want as it would error if target is nil.

The above code is already much better. IsEdible is a good function name, as we assume that it will return if the target is edible. We can always make this little code better though. If the code is only for yourself, you will understand it. But even then it’s handy if you provide error handling too and provide warnings. Your code will be much easier to debug for yourself and others can also understand your code better. A true defensive coded function would make sure every case is handled and throw warnings or errors.

function IsRobloxInstance(input) -- This function returns if the input is a roblox instance.
    return ((type(input) == "userdata") and getmetatable(input)) and input.ClassName ~= nil
end

function ThrowWarning(Source, …) -- Simple output formatter.
    local arguments = {…} -- … = all arguments passed to the function after the first argument.
    -- This is called a „variadic” function, with a variable number of arguments.
    for _, value in pairs(arguments) do -- We can just traverse the table of those arguments here
        print("["..Source.."] "..tostring(value))
    end
end

function IsEdible(target)
    if IsRobloxInstance(target) then 
        return (target and target:FindFirstChild(„Edible”)) ~= nil
    elseif target == nil then
        ThrowWarning("IsEdible", "The target argument is nil")
    else
        ThrowWarning("IsEdible", "Please provide a roblox instance")
    end
    return false --In the case nothing is returned, return this default value: false.
end

Above code shows code which cannot error. However, it can mess up the things you expect. Let’s say that a GUI has to pop up if something is edible. If you accidentally pass a table to IsEdible for example, the code will not crash and you will get a warning message, but nothing will get on the screen, even when you just checked that Mouse.Target is edible. It depends on yourself if you think this debugging style is better. There is one main reason why you should code like this, though: if you put your game online the game can have unexpected behaviour (such as not showing GUIs), but the code itself will not crash - so you are not forcing people to reset.

As I said, it really depends on yourself if you like this or not. I code like this. I make sure that the code cannot error (though I must admit that I sometimes don’t write scripts completely defensive, which actually errors then later…) and that every warning or error will be pushed to an output. Just try this yourself! It’s also very handy to code like this if you work with more people. They will get the warnings when they are using your functions wrong. Like that, they don’t have to know how the functions themselves work, but they will know what they have to pass to the functions in order to work correctly.

Do you have some short scripting tips yourselves? Post them in the comments!

Posted in Scripting Tips

Commentary

Leave a Comment

OldPalHappy says: December 17, 2016
This is one of the most useful blog posts ever :3