I just started learning Lua and LOVE2D, I'm trying to make text input boxes, I've got 4 instances within an array and I'm trying to call their draw function within the love.draw()
TextBox = {
x = 0,
y = 0,
width = 256,
height = 64,
text = '',
active = false,
colors = {
background = { 186, 163, 182, 220 },
text = { 20, 20, 20, 255 },
border = { 48, 44, 50, 230 },
activeborder = { 236, 234, 115, 240 }
}
}
function TextBox:init(o, x, y)
local o = {}
o.parent = self
o.x = x
o.y = y
return o
end
function TextBox:draw_self()
-- Draw the box
love.graphics.setColor(unpack(TextBox.colors.background))
love.graphics.rectangle('fill',
textBoxArray[i].x, textBoxArray[i].y,
TextBox.width, TextBox.height)
...
end
main.lua :
function love.load()
...
textBoxArray = {}
for i=1,4 do
... -- Declaring xx and yy here
textBoxArray[i] = TextBox:init(nil, 40+xx, 40+yy)
end
end
function love.draw()
for i=1,4 do
textBoxArray[i]:draw_self()
end
end
I get an error when I try to call :draw_self(), :init() works just fine and I couldn't figure out what the issue might be. What's the proper way to call a method when the instance is within an array?
What's the proper way to call a method when the instance is within an array?
textBoxArray[i]:draw_self()
is the correct way to call a "method" if textBoxArray[i]
has been set up properly: obj:method(...)
is (roughly) just syntactic sugar for obj.method(obj, ...)
. That is, indexing obj
with the string "method"
must return a method which takes obj
as the first parameter.
This isn't the case though; your constructor doesn't set a metatable. Let's take a look:
function TextBox:init(o, x, y)
local o = {}
o.parent = self
o.x = x
o.y = y
return o
end
After this, o
has fields x
, y
and parent
. Accessing any other field will yield nil
. In particular, textBoxArray[i].draw_self
will be nil
for all i
. You're thus trying to call a nil
value, which throws an error. o.parent
doesn't have any special effect (assuming you're not using an OOP library). Instead, the standard pattern is to do self.__index = self
followed by setmetatable(o, self)
. (There are various ways to vary this pattern. For example you might want to separate the metatable and the class table, and you might not want to do an "unnecessary" self.__index = self
in every constructor call. A nice property of this "standard pattern" is that it makes inheritance easy though.)
Side note: The unused o
parameter doesn't make sense. If you want to allow passing an existing table, use o = o or {}
rather than local o = {}
. Otherwise, just remove the parameter.
Let's take a look at draw_self
now:
function TextBox:draw_self()
-- Draw the box
love.graphics.setColor(unpack(TextBox.colors.background))
love.graphics.rectangle('fill',
textBoxArray[i].x, textBoxArray[i].y,
TextBox.width, TextBox.height)
...
end
The "method" is called draw_self
, yet you're operating on the global TextBox
table except for the usage of textBoxArray[i]
. Both of these should be completely replaced with usage of self
. Currently you're using method syntactic sugar (function TextBox:init(...)
is equivalent to TextBox.init = function(self, ...)
- it adds an implicit self
parameter) here despite not using self
at all.
Your love.load
is fine. Your love.init
is also fine except for the o
quirk; depending on how you refactor your constructor, you'll probably want to remove the unused nil
value.