Using External Modules in Corona
When it comes to Corona projects, there’s really only one file you need to get things working, and that is–of course–your main.lua file. However, as soon as your project grows to any length, you’ll start to wish you could put most of the code into separate files, rather than cramming everything into a single main.lua file.
If that’s you, then today is your lucky day, because I’m about to show you exactly how to do that. It’s called “external modules”, and you’ll soon be able to take advantage of the concept in your Corona projects with little effort.
Before I get started though, it’s worth mentioning that there’s already a Modules and Packages guide on this website, but in case that’s not enough for you, I’ll be going a little more in-depth here today.
Creating a new module
To create a new module, simply create a new text file and save it with a .lua extension and place it in your project folder (the same folder that your main.lua file resides). For the sake of this example, let’s say you called it example.lua.
Now, at the top of example.lua, you need to include one very important line:
module(..., package.seeall)
There are different arguments you can pass to the function above, but the line shown above will cover just about all of your module needs. You can view the module documentation for more technical details.
And that’s it! You have successfully created an external module… one that is absolutely worthless. Next we’ll make it actually do something.
Let’s create a function within example.lua, helloWorld(), that will simply print the words “Hello World” to the terminal screen when the function is called:
-- example.lua
module(..., package.seeall)
function helloWorld()
print( "Hello World" )
end
Notice how I didn’t make the function local? If you want your functions to be accessed externally (by say, your main.lua file or other modules), you’ll need to make sure the function is in global scope. Don’t worry, I’ll show you how to localize these functions soon so they’re not affecting performance.
Also know that not all functions placed in external modules need to be global. If you have a function that’s only used by that module and doesn’t need to be accessed externally, then it can (and should) be made local.
Accessing functions from other modules
Now that you have a function in example.lua, let’s go back to you main.lua file and make use of the external module:
-- main.lua
local example = require( "example" )
local helloWorld = example.helloWorld
helloWorld() -- output: "Hello World"
Let’s break this down line-by-line so you get a better understanding of everything.
local example = require( "example" )
This line localizes example.lua into a table called example. The function require() searches the project directory for whatever file you tell it to. In this case, example.lua. You can only require-in lua files, so always leave off the extension here.
The variable example will now hold all of the global variables and functions of example.lua. Let’s look at the next line:
local helloWorld = example.helloWorld
Remember how you got all nervous when I told you that functions in external modules need to be global? Well this is where you can put your heart at ease because this line will localize (cache) the helloWorld() function that’s found within example.lua.
In the line above, example is simply a table, and example.helloWorld is a reference to the helloWorld() function within example.lua.
And now for the final line:
helloWorld()
Since example.helloWorld was assigned to the variable helloWorld, and since example.helloWorld is a reference to a function, you can now call helloWorld as a function and it will be treated as such (and a local one, at that!).
The above line of code will execute whatever you told helloWorld() to do in your example.lua file.
What’s the point?
Now, it’s a little difficult to see benefit when looking at a nearly blank main.lua file and an equally small external module, but imagine if you had several functions in example.lua that needed to be accessed by main.lua, or maybe even other files. It would greatly help in keeping your project organized.
A common scenario is if you are creating a game with several screens, and each of the screens share lots of code (such as menu creation, heads up display, etc). You could put all of the shared code into an external module, and then simply call those functions from the individual level files (instead of tons of redundant code).
This also helps when updating your project as well, because imagine if you had 30 levels and you needed to make a simple change to your pause menu. Would you rather modify one function in a single module, or would you rather modify the same code across 30 different level files? Now the use of external modules becomes a little more clear.
Basic screen loading
One of the most common reasons people want to use external modules is to split up their project into separate “screens” or “levels”.
There’s already plenty of user-contributed scripts out there, such as the highly acclaimed Director Class by Ricardo Rauber, but while we’re on the subject of external modules, I’ll go ahead and show you a really basic method of splitting your screens up into separate Lua files.
Here’s level1.lua:
And here’s how you would load level1.lua from a different module, such as main.lua:
local levelGroup = require( "level1" ).loadLevel()
The above line will put the text “Level One” on the screen, which is in a display group (levelGroup, because loadLevel() returns a display group). You would do the same for other levels, and use other variables to store their display group.
From there, you can remove the display group (level), or do specific things with the items within the display group. It’s a simplistic solution, but it shows you just how easy it is to use external modules to help organize and streamline your projects.
Study other developers’ scripts
To get a better understanding of how external modules work, and for more ideas on how you can use the concept to your advantage, I recommend browsing the Code Exchange to download some of the user-contributed scripts that are available for you to use in your projects (most of which will require you to use an external module).
Once you study how others use external modules, and after putting them into practice a few times, the concept will become a lot clearer, and you’ll wonder how you went so long without it!
Thank You!
I had not noticed the earlier tutorial for this and this was just was I needed for my current (and epic
) project.
Oskar
Thanks Mr Beebe! This is very helpful having some further explanations on why things work the way they do. Normally all we get is “This does this” but not the “why this does this.” So I’m grateful for your blogging efforts.
If only I had this article when I started Corona…
I’ve been advised to stay away from the module function.
Instead I tend to use a format of:
modulename=M
-- manually shift external stuff I want into local variables.
-- not actually required if you just copy _G into what we're about to do,
-- but a matter of personal preference
local display=display
setfenv(1,M)
[[ fields and functions ]]
return M
Might be worth showing how some basic object oriented programming principles can be applied to modules as well. This allows you to think in objects like balloons or rockets.
For example put this in file called Balloon.lua. And you have means to create balloons each with it’s own state while reusing the same logic.
module(…, package.seeall)
function new (color)
local balloon = {}
balloon.color = color
balloon.type = “round balloon”
balloon.state = “full”
function balloon:printInfo()
print(self.color)
print(self.type)
print(self.state)
local freeMem = collectgarbage(‘count’);
print(“GC Count : ” .. freeMem/1024 .. “MB”)
end
function balloon:pop()
if self.state == “full” then
self.state = “popped”
print(self.state)
–self:removeSelf()
–collectgarbage(“collect”)
freeMem = collectgarbage(‘count’);
print(“GC Count : ” .. freeMem/1024 .. “MB”)
print(gcinfo ())
end
end
return balloon
end
–Here is the main.lua file.
local BalloonFactory = require(“Balloon”)
–Create two instance of the balloon.
local b1 = BalloonFactory.new(“RED”)
local b2 = BalloonFactory.new(“BLUE”)
Jon, thanks for this.
I suppose variables can also be referenced from these external modules. (like properties). if there were a variable in the external module, can it also be called as exmaple.varname based on your example?
@Altaf: Yup, you’re exactly right
@Everyone else: Thanks for all your comments, input, and suggestions!
Thanks for the great tutorial.
Should you cache other global functions (such as the math functions) before using it withing your module function? In other words, does caching a function locally cache every other function called internally within it?
When you cache a function, to a certain extent it does cache functions within that function (because look-up is reduced), but as a rule of thumb you should try to localize (e.g. cache) as many functions/variables as you possibly can while still maintaining the functionality of your app.
Thanks for the great tutorial Jon.
What was a bit confusing for me when starting to work with my own classes in corona, was calling a function like a variable, ie example.helloWorld, and not example:helloWorld(), until i found out, that the dot (.) is calling a class method and the colon(:) is calling an object method. i read somewhere, that in lua, the colon is just a shortcut to the dot call, without the need to pass the object itself, so it can be accessed via “self” in the function…
maybe this is of interest for anyone…
I guess there is something I don’t quite understand still about modules. I see the benefit but for the life of me I have never managed to get one module see another module.
For example lets say I have a particle module and I also have another module to do with something else along side main.lua. At times the particle module needs some information from other module yet nothing I do can get them to see each other.
There is never any issue for main.lua seeing and calling functions in each of the other modules but the modules can never seem to see each other.
Any ideas on this kind of issue?
Thank you for your clear and to the point articles.
@Mike R – all you have to do is call the require() function within the other external module. Calling require in main does not make it accessable to all other modules, only accessable to main.
@Jonathan Beebe or anyone else… My question is this: is there a difference between caching a function locally (as in your example) as opposed to only localizing the value/object the function returns (as in Bob’s example)?
If so what is the benefit?
Should the function be localized everytime you use it, (as in if I use it within another function that is in the same scope?). Or should I only localize it once at the top level, and then use it within that scope?
hi, what if the external function has parameters? what is the syntax to localise such functions?
What about this:
timer.performWithDelay( 250, doBellyDance )
But sometimes you want to utilize .performWithDelay and you want to pass parameters to that function specified. How do you accomplish this?
Hi Jerome, you can do it like this:
timer.performWithDelay( 250, function() doBellyDance( arg1, arg2 ); end )
Or, a little more code but a more recommended method:
local callBellyDance = function()
doBellyDance( arg1, arg2 )
end
timer.performWithDelay( 250, callBellyDance )
@Jonathan: I love you.
So now, this post is no longer cool right due to the “A better approach to modules”
@Jonathan Beebe:
Whats the difference between “local table functions” vs “local functions”???
So, your function would be a “local table function”, because you assigned a function to a local variable, right? Or here I have another one:
--do here
end
var() --call
Here I have a normal local function:
--do here
end
var()
What are differences? (Dis)Advantages?
@jack0088: There really is no difference, except one very small difference. Normal functions can call themselves from within the function, and table functions cannot (unless a forward declaration was specified above that function). See ‘Function Definitions’ in the Lua manual (section 2.5.9): http://www.lua.org/manual/5.1/manual.html#2.5.9
how would i pass a local variable(a table from main.lua) to an external module
and then manipulate this table from inside the external module
and then pass that back to the main.lua as a table.
And then how to access the above table from the external module
and then perform any cleanup/garbage collection.
I know thats a lot of “and thens” but this is what holding me back from
producing a quality “built/designed” program.
Great ! but if I only want to include code ? No use of function. I just need to have some piece of code outside my main.lua, and i want to include it where I require to be loaded.
I am not sure to make a difference beetween a call like
and
All work.
I found it helpful to reference the below posts among others I will add as I come across them when learning about using modular coding techniques in Corona especially in light of the deprecation of the Lua Module() function.
http://developer.anscamobile.com/content/modules
http://blog.anscamobile.com/2011/08/tutorial-simple-modular-coding-technique/
http://blog.anscamobile.com/2011/09/a-better-approach-to-external-modules/
http://blog.anscamobile.com/2011/06/understanding-lua-tables-in-corona-sdk/
http://lua-users.org/wiki/ModuleDefinition
http://www.ludicroussoftware.com/blog/2011/11/21/build-missile-command-with-the-corona-sdk/