Splitting Code Into Several Files - noooway/love2d_arkanoid_tutorial GitHub Wiki
In this part I'm finally going to split the main.lua into several smaller files.
In Lua, a main program can load external files using require function.
When a file is required, the code inside it is executed.
By default, the execution takes place in the global environment of the main program.
Thus, every variable that is not declared local, will be visible in the main program.
The local variables are accessible only from the file, and do not pollute the global environment.
If the external file has a return statement, that would be the result of the require statement
in the main program.
Typically, inside the external file a temporary table is created; all the definitions, that are going to be exported into the main program, are added into that table. Then, the table is returned into the main program.
Auxiliary functions and variable are declared local and are not added into it.
For demonstration, suppose there is a greetings.lua file with the following content:
local greetings = {} --(*1)
greetings.hello_message = "Hello from Greetings module."
function greetings.say_hello()
print( greetings.hello_message )
end
local secret = "IDDQD"
function greetings.reveal_secret()
print( secret )
end
function make_mess()
mess = "terrible"
end
return greetings --(*2)
(*1): declaration of the greetings table, where all the module definitions will be added.
Typically such table is declared local.
(*2): this table is returned at the end of the module.
It is possible to load and test this module from the Lua interpreter:
> greet = require "greetings" --(*1)
> greet.hello_message --(*2)
Hello from Greetings module.
> greet.say_hello() --(*2)
Hello from Greetings module.
> greet.secret --(*3)
nil
> greet.reveal_secret() --(*3)
IDDQD
> mess --(*5)
nil
> greet.make_mess() --(*4)
stdin:1: attempt to call a nil value (field 'make_mess')
stack traceback:
stdin:1: in main chunk
[C]: in ?
> make_mess() --(*4)
> greet.mess --(*5)
nil
> mess --(*5)
terrible
(*1): "greetings" is required. The .lua extension is omitted. The returned table is
assigned to the greet variable. For interpreter to be able to find the "greetings.lua",
it has to be launched from the same folder where the file is stored.
(*2): both hello_message and say_hello are accessible from the greet table.
(*3): secret is declared local and can't be accessed from the interpreter.
However, greet.reveal_secret function has access to this variable.
(*4): the make_mess definition is not prefixed neither with local nor with module table.
This function becomes defined in the global namespace.
(*5): mess variable is initially empty in the global namespace.
After the call to the make_mess function, the mess variable
becomes defined in the global scope, not in the greet table.
It can be seen, that with such a simple approach to module creation,
it is necessary to be careful not to accidentally put a variable or function declaration
in the global scope. Every declaration should be either local or go into greetings table.
A workaround can be provided by environment manipulations.
I plan to address this issue in the Appendix.
Regarding the game code, following this scheme, it is necessary to move each table in a separate file and add a return statement returning this table.
local levels = {}
levels.current_level = 1
levels.gamefinished = false
levels.sequence = {}
.....
return levels
After that, in the main.lua it is necessary to require these files:
local ball = require "ball"
local platform = require "platform"
local bricks = require "bricks"
local walls = require "walls"
local collisions = require "collisions"
local levels = require "levels"
.....
Apart from that, the rest of the code in the main.lua and modules doesn't change.
The only thing I want to introduce in this chapter is vector module
from HUMP library.
It defines a class for a 2d-vector, which can be used to simplify arithmetical
manipulations on coordinates.
local vector = require "vector"
local ball = {}
ball.position = vector( 200, 500 ) --(*1)
ball.speed = vector( 700, 700 )
ball.radius = 10
function ball.update( dt )
ball.position = ball.position + ball.speed * dt --(*2)
end
function ball.draw()
local segments_in_circle = 16
love.graphics.circle( 'line',
ball.position.x, --(*3)
ball.position.y,
ball.radius,
segments_in_circle )
end
(*1): Instead of ball.position_x and ball.position_y there is now a single 2d vector ball.position,
holding x and y components inside. Same for the ball.speed.
(*2): HUMP.vector defines several operations on vectors, such as addition. This allows to simplify
the code.
(*3): Individual components of the vector can be accessed using table indexing notation.