Scopes for Functions

It is the most frequently asked question, this is due to the fact that a lot of developers still mix up with the scope of a variable, what about a function? Did you think that functions also have scopes? Yes they have and let us look at the scope of a function in this tutorial.

Let's develop this sample code as we learn about function scopes.

Simple function

local function main()
   print("Hello from CoronaSDK")
  end

  main()
This shall work perfectly fine and print Hello from CoronaSDK when we run it.

Forward Declarations

There are cases when we need to pre-declare a function, so here's how we do it
local main

  local function init()
    main()
  end

  function main()
   print("Hello from CoronaSDK")
  end

  init()

In this example, we call init, which in turn calls main and for forward declaration, we declare that main is a local variable by declaring it then we just define main as a function, note that we did not add the local in front of it, if we did, it would become a new declaration and would not work for us.

Encapsulated Function


What if we wanted to have a function that is only available from within a function?
local function main()
    print ("Hello from CoronaSDK")

    function insideMain()
      print("This is a function inside of main, not available to be called from any other")
    end

   insideMain()
  end

This example is not very illustrative of where this can be used, so let us look at a practical example
local function spawnObject()
   local resultingObject = display.newRect(10,10,100,100)
   
   local function onTouch(event)
     if "ended" == event.phase then
      print("touched rectangle")
     end
   end

   resultingObject:addEventListener("touch",onTouch)

   return resultingObject
  end

  local xTemp = spawnObject()

You can see that the function that handled touch is well within the function spawnObject, this is one cool way of having all related function to the object inside, more like a class without creating a class. These function shall be available to the object to be invoked internally.

Member functions

Member functions are a bit different than normal functions as they become part of the object than being just local to the object. so, in the previous example, let us say we want to change the color of the resultingObject, we do not have a function as yet, let us add a function that will help change the color of the object.

local function spawnObject()
   local resultingObject = display.newRect(10,10,100,100)

   function changeColor(theColor)
     resultingObject:setFillColor(theColor[1],theColor[2],theColor[3])
   end
   
   local function onTouch(event)
     if "ended" == event.phase then
      print("touched rectangle")
     end
   end

   resultingObject:addEventListener("touch",onTouch)

   return resultingObject
  end

  local xTemp = spawnObject()
We cannot access this function from outside of the spawnObject function even though it is not local as there is no way to have a handle to it. So we just make a small modification to the same and we get

local function spawnObject()
   local resultingObject = display.newRect(10,10,100,100)

   function resultingObject.changeColor(theColor)
     resultingObject:setFillColor(theColor[1],theColor[2],theColor[3])
   end
   
   local function onTouch(event)
     if "ended" == event.phase then
      print("touched rectangle")
     end
   end

   resultingObject:addEventListener("touch",onTouch)

   return resultingObject
  end

  local xTemp = spawnObject()
  xTemp.changeColor({255,0,255})


As you can see after running this code, a pink/magenta rectangle is seen on the screen.
If we were to call the changeColor from within the function spawnObject, we would have a bit of a dilemma, how would we access it? we would not have a handle on the function. So, in cases like these, since it is inside of the function and we are referring to another function that is part of the function, we use the keyword self and we call it as
self.changeColor({255,0,255})

Cross calling

This is based off a question on the forum, where the user wanted to know how to call a particular function that was inside of another function. Now by the sound of that sentence it is such a complicated and complex thing to achieve, but in reality, let us see.

We want to be able to call a function that checks for the bounds of the object and make it wrap on the screen like the asteroids game. Now in any thing that we want to do, it is better to actually put what we want to achieve on paper before we attempt to actually achieve that. So, we want that the player object wraps around the screen. We shall call this via a runtime event listener. Since it is not inside of the function, we cannot use the self, though both could be part of the main.lua, self would not work, and the only way is to use the function names. Also since we have determined initially that one function is inside of a function and the other is outside, we need to be able to cross call. We would in the way that we have just seen in the previous example.

local function spawnObject()
   local resultingObject = display.newRect(10,10,100,100)

   function resultingObject.changeColor(theColor)
     resultingObject:setFillColor(theColor[1],theColor[2],theColor[3])
   end
   
   local function onTouch(event)
     if "ended" == event.phase then
      print("touched rectangle")
     end
   end

  function wrap(event)
   if resultingObject.x < 25 then resultingObject.x = 25 end
   if resultingObject.x > 743 then resultingObject.x = 743 end
  end

   resultingObject:addEventListener("touch",onTouch)

   return resultingObject
  end

  local xTemp = spawnObject()
  Runtime:addEventListener("enterFrame", xTemp.wrap)

The same code can be made even more structured and cleaner as



local function spawnObject()
   local resultingObject = display.newRect(10,10,100,100)

   function resultingObject.changeColor(theColor)
     resultingObject:setFillColor(theColor[1],theColor[2],theColor[3])
   end
   
   local function onTouch(event)
     if "ended" == event.phase then
      print("touched rectangle")
     end
   end

  local function wrap(event)
   if resultingObject.x < 25 then resultingObject.x = 25 end
   if resultingObject.x > 743 then resultingObject.x = 743 end
  end

   resultingObject:addEventListener("touch",onTouch)
   Runtime:addEventListener("enterFrame", wrap)

   return resultingObject
  end

  local xTemp = spawnObject()

Now every time a new object is spawned, it is fully encapsulated, having its own functions for enterFrame and for touch.

Advanced Function

Functions in Lua are an amazing thing, they are nothing bu pointers to a memory address that holds the code and therefore can be modified on the fly, this is lots of flexibility and also dangerous, useful for obfuscation too.

local hide = print
 local print = math.random
 local function main()
   hide("What do you have to hide?")
   hide( print(5) )
 end

  main()

Now at first glance that is a bit strange looking code, but you know why, coz we have assigned hide to the function print and the local variable print to the function math.random.

More Advanced Function

Functions can also be stored as part of tables, but for this tutorial we shall stop here, the functions as part of tables shall be *perhaps* part #3 of the tables in lua series. So you can read up on those at Part #1 or at Part #2

Comments

  1. Hi,
    when i try to call changeColor from within the function spawnObject und use "self" i get an error from CoronaSDK.

    Instead of using self i use:
    resultingObject.changeColor({255,0,255})

    and it worked!

    ReplyDelete

Post a Comment

Popular Posts