As our skills grow, our programs become larger. By and by managing long sheet of code becomes difficult. Is it possible to conveniently split the program into smaller parts?
General approach is to put certain fragments of code into functions, which could be defined separately. In other languages they also could be called procedures or subroutines. Let's see some examples.
Despite the weird name we know from school, this calculation is extremely useful in graphic applications. Suppose
there are several points of interest on the screen (perhaps, rounded buttons etc) and user clicks somewhere - and we
want to know which point is "hit" (if any). Let user's click have coordinates X, Y
and button's center be at
Xc, Yc
- in this case the distance between two points is calculated according to
Pythagoras:
dX = X - Xc -- difference by X
dY = Y - Yc -- difference by Y
dist = math.sqrt(dX^2 + dY^2) -- pythagorean formula
We can combine it into single string, but anyway repeating such equation in several place, if necessary, will make our
program look messy. Some languages have ready hypot(dx, dy)
function, but not Lua
. No problem, let's define it:
function hypot(dx, dy)
return math.sqrt(dx^2 + dy^2)
end
dist = hypot(X-Xc, Y-Yc)
The last line shows how we use (or "call") the defined function: just its name and arguments in parentheses. Arguments
in the function definition (dx
and dy
) are then used as variables visible only inside the function - to which
actual values (passed upon the call) are assigned. The word return
is used to stop executing the function and
"return" the calculated value into the outer code, from where the function was called.
The cool bonus feature of Lua
is that we can arrange this new function to live along the other math functions:
math.hypot = function(dx, dy)
return math.sqrt(dx^2 + dy^2)
end
dist = math.hypot(X-Xc, Y-Yc)
In this case the function is "nameless" but it is immediately assigned to the math.hypot
variable so it is used the
same way as if such were its name!
Suppose we want to create a game with graphical element representing cockroaches or other bug. We start with designing the shape of our "protagonist":
graph.init()
graph.rect(0, 0, graph.w, graph.h, 'white')
graph.circle(100, 100, 15) -- body
graph.circle(100, 76, 9) -- head
graph.line(74, 100, 85, 100) -- legs follow
graph.line(115, 100, 126, 100)
graph.line(78, 85, 88, 93)
graph.line(122, 85, 112, 93)
graph.line(78, 115, 88, 107)
graph.line(122, 115, 112, 107)
It may take us some minutes to figure out suitable coordinates by trial and error. But now, when the first cockroach
is ready, how can we draw another, in different coordinates? We shouldn't recalculate this all manually for different
starting point insteand of 100, 100
- should we???
Of course no. Let's wrap it in a function:
graph.init()
graph.rect(0, 0, graph.w, graph.h, 'white')
function cockroach(x, y, clr)
graph.color(clr)
graph.circle(x, y, 15)
graph.circle(x, y-24, 9)
graph.line(x-26, y, x-15, y)
graph.line(x+15, y, x+26, y)
graph.line(x-22, y-15, x-12, y-7)
graph.line(x+22, y-15, x+12, y-7)
graph.line(x-22, y+15, x-12, y+7)
graph.line(x+22, y+15, x+12, y+7)
end
cockroach(100, 300, 'red')
cockroach(300, 100, 'green')
cockroach(500, 300, 'blue')
We added third parameter so we define not only coordinates, but also colors. As for coordinates, you can see we subtracted 100
from our initial "trial" findings everywhere and replaced it with x
or y
correspondingly.
You may remember in the previous lesson we complained that preparing strings defining colors in RGB form is tedious due
to necessity of concatenating string like rgb(115,200,90)
using three values and string pieces between them (so, 7
in total).
Now, create a function rgb
which takes three arguments and produces such a string. Of course inside it still be
concatenation, but it is in only one place. Copy the example with gradient filling and modify it so your function
is called instead of concatenations.