Categories
Advanced Tutorials

Script variable scope

Preface

When building a game in Crayta you will often need to store information about the state of your game; game score, current level, number of enemies, etc are all examples of information you would store using variables. You may use these variables while coding your Lua scripts to build working blocks of code, much like you would in any other programming language.

While it is very easy to declare variables that hold supported Lua data, their scope will be different depending on how and where we declare said variable.

In this tutorial we explore the different scopes available for Lua variables in Crayta.

Variable scope is a subject of study and practice in any programming language. Something that the developer has to understand and use accordingly.

In Unity with C#, the scope of a variable or property is set using special keywords, public and private. These define if other script classes have access to variables or not.

In C# you can’t define global variables, since everything has to belong in classes.

Local Variables

In Lua there is a keyword used to declare variables with a local scope, named local. Local variables have their scope limited to the block where they are declared. A block is the body of a control structure, the body of a function, a for loop etc.

Here is an example to illustrate this:

function CollectableScript:Init()
  local name = self:GetEntity():GetName()

  if name == 'cottageChair' then
     -- works, variable name is available since it was declared in a parent scope
     local nameLowerCase = name:lower()

     -- works, variable nameLowerCase is available since it was declared in this scope
     Print(nameLowerCase)
  end

  -- doesn’t work, variable nameLowerCase not available in this scope
  Print(nameLowerCase)
end

Global Variables

Global variables in Lua do not need declarations. You simply assign a value to a global variable to create it. This variable is available in any scope of this script.

For example let’s test this code:

local CollectableScript = {}

function CollectableScript:Init()
    
  local name = self:GetEntity():GetName()

  if name == 'cottageChair' then
      myGlobalVar = 'aGlobalValue'
  end
End

function CollectableScript:OnInteract()
    Print(myGlobalVar)
end

If we assign this script to a number of entities, with one of them named cottageChair, then each time we interact with any of these entities we will get aGlobalValue printed in the console.

A global variable has the same value across all instances of the script.

Global variables have a greater performance impact compared to local variables, so as a preference local variables should be preferred. In addition global variables produce poor code architecture, breaking abstractions and object oriented paradigms, making maintaining a codebase difficult.

Self Variables

Crayta uses an object oriented approach in writing reusable code. Each Lua script contains the definition of a class-style type that can be reused (instantiated) by any number of entities that have a script component of this type attached.

The methods declared in this script will run on a different context each time. That of the entity the script is attached to.

For this reason it is quite useful to be able to declare variables that can be used by any method of the same script, but not between script instances.

Crayta makes available in any script a special variable named self. We can use the dot notation to declare variables that are available on a specific script instance this way.

Let’s rewrite the previous example to accomplish a different task using self.

local CollectableScript = {}

function CollectableScript:Init()
    
  local name = self:GetEntity():GetName()
  self.nameLowerCase = name:lower()

end

function CollectableScript:OnInteract()
    
    Print(self.nameLowerCase)
end

Now if we attach our script to a number of entities, whenever we interact with one of them we will get their name printed in lowercase to the console.

Using self.nameLowerCase we make sure that we declare a new variable unique to each script instance executing.

Accessing from other scripts

Local and global variables are accessible only within the same script. On the other hand variables defined using self can be easily accessed by other scripts. Variables in Lua can hold not only data values but also references to functions.

This is powerful and allows you to easily extend your code to read data and methods found in other scripts.

Here is an example of how we can access the nameLowerCase variable from another script:

local AnotherScript= {}

AnotherScript.Properties = {
      { name = "collectable", type = "entity"}
}

function AnotherScript:MyMethod()
    
    Print( collectable.collectableScript.nameLowerCase )
end

We can access the script instance of a script with its name, on any entity that has it attached. Any variables or methods declared on self are available now using the same notation.