Im learning to code lua and believe I have come quite far and recently picked up the LOVE2D engine. Ive decided to try some OOP to sort out my coding however all efforts seem to fail with the error
main.lua:31: Attempt to call field "Draw"(nil value)
function love.load()
Unicorn = {
name = "Default",
breedname = "Default Class of a unicorn",
description = "Quite a default Unicorn",
imgclass = love.graphics.newImage("UnicornA.gif"),
x = 20,
y = 20,
height = 0,
width= 0,
age = 0,
maxage = 20,
health = 100
}
function Unicorn.Draw(self)
love.graphics.draw(self.imgclass, self.x, self.y)
end
function Unicorn:new(o)
o = o or {}
setmetatable(o, self)
self._index = self
return o
end
alfred = Unicorn:new()
alfred.x = 50
harry = Unicorn:new()
end
function love.draw()
love.graphics.print("Unicorn Farm Simulator 2014", 0, 0)
alfred.Draw(alfred)
harry.Draw(harry)
end
To clear things up love.draw is a callback and so is love.load. I am creating the class Unicorn with a Draw function and a New function, the new function creates an Instantation of the Class ( Am i right with that vocabulary?)
Sorry about using full capital acronym for the language, I just presumed it stood for something!
Make self._index = self to self.__index = self, because _index is nonsense here.
You could read more about OO in Lua in this answer I have made.
Bonus:
Make Unicorn.Draw(self) to Unicorn:Draw(), to get rid of self. And use it in this way
alfred:Draw()
harry:Draw()
Use local as possible as it can be. Because local is faster and friendly to memory usage.
Related
Sorry in advance if this is an incorrect question. I'm fairly new to Lua and I'm not sure how to go about this. I want to access a variable stored in a table from a function variable.
As far as I know there is no self-referencing tables before constructed.
An example would be this:
local bigTable = {
a = {
foo = 0,
bar = function(y)
print(foo) --Incorrect
end
}
}
What would be the best approach for this situation?
What you want to do is to create a table first, and append the keys to it:
local a = {}
a.foo = 0
a.bar = function()
print(a.foo)
end
local bigTable = {
a = a
}
bigTable.a.bar() -- prints 0
local bigTable = {
a = {
foo = 0,
bar = function(self, ...)
print(self.foo)
end,
}
}
-- Somewhere else in the code...
bigTable.a.bar(bigTable.a) --> 0
-- or the shorter but (almost) equivalent form:
bigTable.a:bar() --> prints 0
I anticipate your next question will be "What does the : do?", and for that there's lots of answers on SO already :)
Note that there's potential for a performance improvement here: if the above code gets called a lot, the bar method will be created again and again, so it could make sense to cache it; but that's pointless unless the surrounding code is already fast enough that this one allocation would have a noticeable impact on its runtime.
Ok, so I'm trying to follow the instructions found here: https://www.lua.org/pil/16.1.html to do something resembling OO programming in Lua (and the LOVE game framework), but it's not working. Here's the core of my code. I have a generic Object class:
local Object = {}
function Object:load(arg)
end
function Object:update(dt)
end
function Object:draw()
end
function Object:new(arg)
o = {}
setmetatable(o, self)
self.__index = self
o:load(arg)
return o
end
return Object
and a Ship class that inherits from it:
Object = require('objects.object')
local Ship = Object:new()
Ship.sprite = love.graphics.newImage('assets/sprites/ship.png')
Ship.sprite:setFilter('nearest', 'nearest', 0)
Ship.px = Ship.sprite:getWidth()/2
Ship.py = Ship.sprite:getHeight()/2
function Ship:load(arg)
self.x = arg.x or 0
self.y = arg.y or 0
self.sx = arg.sx or arg.s or 1
self.sy = arg.sy or arg.s or 1
self.rot = arg.rot or 0
self.tint = arg.tint or {255, 255, 255}
end
function Ship:draw()
love.graphics.setColor(self.tint)
love.graphics.draw(self.sprite, self.x, self.y, self.rot,
self.sx, self.sy, self.px, self.py)
love.graphics.setColor({255, 255, 255})
end
return Ship
Now the problem is, I create two of these Ships as members of another object with this code:
self.ship1 = Ship:new({x=50, y=self.y1, s=2, tint={0, 0.5, 1}})
self.ship2 = Ship:new({x=750, y=self.y2, s=-2, tint={1, 0.5, 0}})
But when I draw them, I only see one - the second. As it turns out, it's like the code above doesn't assign the new instances of Ship to ship1 and ship2, but directly to self, for reasons that I can't understand. Did I do something wrong or is this a weird bug of the interpreter?
It is not bug of the interpreter, it is designed behavior of the language. o={} creates global variable, which is not expected by the programmer here. "Why it is so?" is a frequent question to the language creator. There are many efforts to take control over that behavior simpler .
o = {} without local creates global variable global variable is accessible and shared between all the functions in the program, unless you use fancy environment scoping techniques. Using global variable inside a function opens up doors for various side effects, you should be careful with side effects.
I've removed some of the syntactic sugar and added code above can be equivalently written as follows:
Object.new = function(table_before_colon,arg)
highlander = {} --global variable, there can be only one
setmetatable(highlander,table_before_colon);
table_before_colon.__index = table_before_colon;
highlander:load(arg) -- table_before_colon.__index.load(highlander,arg)
return highlander
end
local Ship = Object:new()
--global highlander == Ship
--Ship.new points to Object.new
function Ship:load(arg)--equivalent to: Ship.load=function(self,arg)
--code that sets fields of the `self` object and is called from within new
self.x = arg.x or 0 -- equivalently highlander.x=arg.x or 0
end
Now, the presence of the global variable would not matter, if nothing happened to it in the period from the start of the new till the new returns. But, apparently, your other code is similar to this:
local OtherObject = Object:new()
--otherObject == highlander
OtherObject.load = function(new_other_obj,arg)
--highlander == new_other_obj
new_other_obj.ship1 = Ship:new({x=50, y=self.y1, s=2, tint={0, 0.5, 1}})
--highlander == new_other_obj.ship1
new_other_obj.ship2 = Ship:new({x=750, y=self.y2, s=-2, tint={1, 0.5, 0}})
--highlander == new_other_obj.ship2
end
So, OtherObject.load calls other functions, and those functions also access and modify the same global variable.
local some_object = OtherObject:new()
returns the global variable as it is at the end of the call, which is last set to the ship2 inside the call to Ship:new inside the call to OtherObject.load inside call to OtherObject:new.
Solved! Apparently, what was needed was this little snippet:
function Object:new(arg)
local o = {}
setmetatable(o, self)
self.__index = self
o:load(arg)
return o
end
Making o local in all my new methods solved everything. I don't know how it worked exactly but I assume having it in the global variable space broke something when the meta tables are set.
I'm working on a game where a bunch of characters will be generated on the fly, based on some constraints defined either in the project or externally via mod files. I am using MoonSharp Lua (5.2) interpreter for interfacing with my C# code, and Lua tables to store the constraint presets. As an example:
require "Defaults"
AgePresets = {}
-- Single value
AgePresets.Newborn = 0
-- Simple ranges
AgePresets.Default = defaultAgeRange --referring to the Defaults require
AgePresets.Child = {1, 12}
AgePresets.Teenager = {13, 19}
AgePresets.YoungAdult = {20, 29}
AgePresets.Adult = {30, 40}
AgePresets.MiddleAge = {40, 60}
AgePresets.Senior = {61, 80}
AgePresets.Elder = {81, 99}
AgePresets.Methuselah = {100, 150}
AgePresets.Methuselah2 = {150, 200}
-- Weighted ranges // again referring to previously defined elements to keep things concise
AgePresets.Tween = {
{weight = 1, minmax = AgePresets.Teenager },
{weight = 1, minmax = AgePresets.YoungAdult }
}
This works fine, but from an end-user point of view, there's a lot of unnecessary typing involved. We are clearly working on AgePresets here but it is still mentioned as a prefix before every member name.
I could of course define AgePresets as an array, like AgePresets = { Child = {}, Teenager = {} } but the problem with that is then I cannot refer to previously defined elements in the array.
This doesn't work:
AgePresets = {
Child = {1,12},
RefToChild = Child, //attempt to index a nil value exception
Teen = {13,19}
}
What I ideally want to achieve is a clean, concise way for users to enter this data in, like in the first example but without having to put AgePresets. prefix before everything. How do I go about declaring a scope in a file such that all succeeding members defined in the file will be within that scope, while maintaining the ability to refer to other members defined previously in the scope?
AgePresets = setmetatable({}, {__index = _G})
do
local _ENV = AgePresets
Newborn = 0
Child = {1,12}
RefToChild = Child -- this ref is Ok
Teen = {13,19}
YoungAdult = {20,29}
Tween = {
{weight = 1, minmax = Teen },
{weight = 1, minmax = YoungAdult }
}
rnd = math.random(10) -- global functions are available here
end
setmetatable(AgePresets, nil)
You can mix the two styles: table constructor for fields that don't need to reference variables that aren't in scope yet, followed by assignment statements for the rest.
I would do that unless the order of the fields in the code significantly enhanced comprehension.
No matter how I approach Lua, I run into this error all the time, so I must not understand something inherit to the language:
attempt to call method 'func' (a nil value)
I've seen the error here a few times as well but the problem doesn't seem clear to me.
Here's my module:
actor.lua
Actor = {
x = 0,
mt = {},
new = function()
local new_actor = {}
new_actor.x = Actor.x
new_actor.mt = Actor.mt
return new_actor
end,
test = function(self, a, b)
print(a, b)
end
}
I'm using Löve.
main.lua
require "game/actor"
local a = Actor:new() --works fine
function love.load()
a.x = 10
print(a.x) --output: 10
a:test(11, 12) --error: attempt to call method 'test' (a nil value)
end
I'm also not sure when it's appropriate to use the previous styling over this in a module.
Actor = {
x = 0
}
Actor.mt = {}
function Actor.new()
print(42)
end
I'm honestly not sure what is more correct than the other but considering I run into a simple error either way, there's probably something I'm missing entirely?
It looks like you're trying to instance a kind of class made of metatables. You basically need to assign new_actor's metatable with Actor.mt. (Resuming the problem: when you're indexing new_actor you're not indexing Actor in this case)
setmetatable(new_actor, Actor.mt);
Even if the metatable is being added, it won't work until you put the meta "__index" event to index a table containing your class methods/values, in this case:
Actor.mt = {
__index = Actor
};
I'd suggest moving your class methods/values into a new table, like Actor.prototype, Actor.fn, etc... avoiding conflicts:
Actor.fn = {
test = function(self, a, b)
print(a, b)
end
};
Actor.mt = {
__index = Actor.fn
};
More about metatables in Lua 5.3 manual.
Right now I'm trying to use Lua to receive variables from barcodes sent out from an outside source. When I run this, there is a variable rotation from the local function rot(input) that appears to be "buggy". If I run this code exactly as it is with the print statements below, the rotation will appear and disappear. Please help me understand why this may happen?
Please note: There are two aspects of this code that I'm currently working on. A) Code128 is not properly retrieving the variables. B)My code can definitely be shortened. But I'm new and learning as I go. The main purpose for this thread is to help me understand why code will sometimes display the desired result, then won't the next minute?
Thank you.
Edited: I've updated the code a bit to make it cleaner. Condensed all of my string.match statements into tables with other barcode related fields. Still learning and looking to make it even more cleaner. I love learning this, but am still having the same problem with my local function rot(input) and getting intermittent results. Any help is greatly appreciated!
local function rot(input)
rotTable = {["R"] = "cw", ["I"] = "180", ["B"] = "ccw"}
for k,v in pairs (rotTable) do
if input == k then
rotation = v
else
rotation = ""
end
end
return rotation
end
local function barCode(input)
local bcID = string.match(input,"%^(B%w)")
if bcID == "BY" then
bcID = string.match(input,"%^BY.*%^(B%w)")
end
local bcTable = {
["BC"] = {"code128", 10, string.match(input,"%^BY.*%^BC(%u),(%d*),(%u),%u,%u%^FD(.*)%^FS")},
["B2"] = {"bc2of5i", 20, string.match(input,"%^B2(%u),(%d*),(%u),%u,%u%^FD(.*)%^FS")},
["BE"] = {"ean13", 10, string.match(input,"%^BE(%u),(%d*),(%u),%u%^FD(.*)%^FS")},
["B8"] = {"ean8", 10, string.match(input,"%^B8(%u),(%d*),(%u),%u%^FD(.*)%^FS")},
["B3"] = {"code39", 10, string.match(input,"%^B3(%u),%u,(%d*),(%u),%u%^FD(.*)%^FS")},
["BU"] = {"upc_a", -1, string.match(input,"%^BU(%u),(%d*),(%u),%u%,%u^FD(.*)%^FS")}
}
for k,v in pairs (bcTable) do
if bcID == k then
bcFields = v
bcType, qzone, bcR, bcH, bcHr, bcData = unpack(bcFields)
end
end
hPos = 0
vPos = 0
bcOutput = '<'..bcType..' qzone=\"'..qzone..'\" hbb=\"0\" vbb=\"0\" bbwidth=\"1\" hpos=\"'..hPos..'\" vpos=\"'..vPos..'\" rotation = \"'..rot(bcR)..'\" bgcolor=\"0\" barcolor=\"255\" textcolor=\"255\" barwidth=\"1\" height=\"8\">'..bcData..'</'..bcType..'>'
return bcOutput
end
print(barCode("^BY3^BCN,102,N,N^FDCHF05000042^FS"))
print(barCode("^B2B,110,N,N,N^FD45681382^FS"))
print(barCode("^BUN,183,N,N,N^FD61414199999^FS"))
print(barCode("^B8I,146,N,N^FD212345645121^FS"))
print(barCode("^BEB,183,N,N^FD211234567891^FS"))
I'm not sure what is wrong with your code, if anything, but rot can be written more simply as
local rotTable = {["R"] = "cw", ["I"] = "180", ["B"] = "ccw"}
local function rot(input)
return rotTable[input] or ""
end
In general, you shouldn't need to search Lua tables. For instance, the loop for k,v in pairs (bcTable) do can be replace by indexing as in the code above.