Help with constructor

General discussion about LÖVE, Lua, game development, puns, and unicorns.
Post Reply
jpott
Prole
Posts: 8
Joined: Fri May 05, 2017 7:36 pm

Help with constructor

Post by jpott »

Fairly new to lua & love2d.
Here's a simplified example of what i'm trying to do...
this should draw five 100x25 cubes 5 pixels apart from each other across the top of the screen (unless i'm screwing something else up as well)

Code: Select all

objectlist = {}
myCube = {
  x = nil,
  y = nil,
  width = nil,
  height = nil
}
function myCube:create(x,y,width,height)
  self.x = x
  self.y = y
  self.width = width
  self.height = height
  return self
end

function myCube:draw()
  love.graphics.rectangle("fill",self.x,self.y,self.width,self.height)
end

function love.load()
  
  width = 100
  height = 25
  spacing = 5
  for i = 1,5 do
    cube = myCube:create(i*spacing + i*width,5,width,height)
    table.insert(objectlist,i,cube)
  end
  
end
function love.draw()
  for k,v in ipairs(objectlist) do
    v:draw()
  end
end
this fills my objectlist with 5 values, but they all have the same attributes; they are all drawn at the same (last) location. I suppose i'm missing some sort of actual constructor in the myCube:create() function. But I cannot find a simple solution to this, and i'm a bit confused when I start looking up constructors. Examples are showing rather lengthy constructors with _init and metatables. I don't really understand it at a glance.
I would really think love2d, as a library, would have some sort of simple solution to instancing objects. perhaps i'm wrong.
any help/advice would be appreciated.
jpott
Prole
Posts: 8
Joined: Fri May 05, 2017 7:36 pm

Re: Help with constructor

Post by jpott »

ok so here's a solution i've found while waiting for a response. maybe i just need to change the way i approach lua mentally, but this feels less... clean.
Here's another simplified example:

Code: Select all

myCube = 
{
  cubeData = {},
  width = 100,
  height = 25
}
function myCube:draw()
  for k,v in ipairs(self.cubeData) do
    love.graphics.rectangle("fill",v.x,v.y,v.width,v.height)
  end
end

function myCube:create(xpos,ypos)
  table.insert(self.cubeData,
  {
    x = xpos,
    y = ypos,
    width = self.width,
    height = self.height
  })
end
  
function love.load()
  for i = 1,5 do
    cube = myCube:create((i*5 + i*myCube.width)-myCube.width,5)
  end
end

function love.draw()
  myCube:draw()
end
pretty close to the same concept, just returning the numbers I want, and doing the draw function afterwards, on the table of data. But I could see this being a problem when I want to add more complex functions... say i wanted each cube to check for collision for example. doing it this way, it seems there will be a lot more for loops, instead of writing all the functions (methods in my head) and calling them once in the draw function. maybe i need to think about it more.
but still if anyone has a cleaner, or more straightforward answer or advice I'd love to hear it.
thanks
User avatar
airstruck
Party member
Posts: 650
Joined: Thu Jun 04, 2015 7:11 pm
Location: Not being time thief.

Re: Help with constructor

Post by airstruck »

First of all, this:

Code: Select all

myCube = {
  x = nil,
  y = nil,
  width = nil,
  height = nil
}
is functionally equivalent to this:

Code: Select all

myCube = {}
...and all that does is create a table called myCube. It's not a class definition; Lua doesn't have any concept of classes. And that's probably a good thing, because you never really need classes when you have ad-hoc object creation.

The most straightforward thing to do, without getting into metatables and prototype-based inheritance, is to simply write a function that creates your cube objects.

Code: Select all

local function drawCube (self)
    love.graphics.rectangle("fill", self.x, self.y, self.width, self.height)
end

local CUBE_WIDTH, CUBE_HEIGHT = 100, 25

local function createCube (x, y)
    return {
        x = x,
  	y = y,
  	width = CUBE_WIDTH,
  	height = CUBE_HEIGHT,
  	draw = drawCube,
    }
end
If you're interested in emulating classes, google "lua classes" and have a look at the info in PIL and the Lua users wiki.
jpott
Prole
Posts: 8
Joined: Fri May 05, 2017 7:36 pm

Re: Help with constructor

Post by jpott »

personally i find it easy to define them, as you would in a class or struct, since i can then look up to the top and easily see how/what i've defined, and don't forget to assign any variables, but I get what you're saying that it isn't necessary.
you're solution is pretty much what I've ended up doing, returning the values I want as you did in the createCube function. I guess I was hoping for a simple o:object = new object sort of constructor, but it doesn't seem to work that way in lua.
I have indeed started reading more about 'OOP in Lua' and am trying to understand metatables better. it seems metatable and __index will do what I want, but I'm having trouble understanding exactly why it works. Just need to read more i guess.
I appreciate the feedback, and a good, simple solution that did indeed solve the problem I posted.
Thank you
User avatar
airstruck
Party member
Posts: 650
Joined: Thu Jun 04, 2015 7:11 pm
Location: Not being time thief.

Re: Help with constructor

Post by airstruck »

jpott wrote:personally i find it easy to define them, as you would in a class or struct, since i can then look up to the top and easily see how/what i've defined, and don't forget to assign any variables, but I get what you're saying that it isn't necessary.
I know what you mean, sometimes it's more convenient or more readable that way.
it seems metatable and __index will do what I want, but I'm having trouble understanding exactly why it works. Just need to read more i guess.
Yeah, you're on the right track. I'm guessing you come from a (pre-ES6) JS background, given that you mention constructors but not classes. The only reason you can have classless constructors in JS is the magical, non-lexical this, which is unique to JS as far as I know and is probably the biggest source of confusion and mistakes in that language. Without that, you'll need something to mimic classes; a constructor on its own is meaningless in most languages including Lua.

You can roll your own prototype-based inheritance, which gives you something very similar to what JS has with new and function prototypes. It goes something like this:

Code: Select all

local MyClass = {} -- the prototype object
function MyClass:init (x, y) self.x, self.y = x, y end
function MyClass:doStuff () end
At this point MyClass is just a regular table with two fields, a function named init and another named doStuff.

Code: Select all

setmetatable(MyClass, {
    __call = function (self, ...)
        local instance = {}
        setmetatable(instance, { __index = self })
        instance:init(...)
        return instance
    end
})
Now we've set a metatable for MyClass and given it a __call metamethod, so we can call it like a function to create an instance of MyClass. The __call metamethod takes the table that was called as the first argument; that is, MyClass. Since MyClass is the prototype for our instances, we create an object and give it a metatable with MyClass as the __index. Then we call the constructor, which is named "init" here but can be whatever you want (just be consistent).

Code: Select all

local instanceOfMyClass = MyClass(1, 2)
Of course you won't want to write all that stuff every time you define a class, so you'll want to move it into a function, maybe add inheritance. Inheritance is just a matter of giving the metatable for MyClass an __index pointing to the supertype.

Hopefully that helps explain some of example code you'll see as you read up on it.
jpott
Prole
Posts: 8
Joined: Fri May 05, 2017 7:36 pm

Re: Help with constructor

Post by jpott »

this is the simplest way I've found to implement what I'm looking for. I've been playing around with it and it seems to do what i need. the 'parent' can have default values, and functions, and the 'child' or new objects inherit the values and functions, but can also independently change the values within each instance created. I still have a lot to understand about metatables, but this seems to be the answer I needed.

Code: Select all

parent = {
	value = 1
	}

function parent:create()
  local child = setmetatable({},{__index = parent})
  return child
end

object = parent:create()

some good information you posted above, I appreciate the time spent. Now I'll need to spend some time wrapping my head around it a bit better

edit: use of parent and child maybe not the best for the example, but it made it simple for me to understand while I was working it out.
User avatar
raidho36
Party member
Posts: 2063
Joined: Mon Jun 17, 2013 12:00 pm

Re: Help with constructor

Post by raidho36 »

There's not much to understand in metatables, the principle is very simple: whenever you access a key that doesn't exist in the table itself, the request is redirected to its metatable, ad infinitum.

I'll point out that there are two table function call/definition notations: colon notation and dot notation. Dot notation produces ordinary function. Colon notation produces a function with the first argument implicitly declared as "self". Emphasis on "notation". There is no real difference between the two ways operation, it's identical. Colon notation simply provides basic syntax sugar for a commonly used expression.

Code: Select all

function table.foo ( a, b ) print ( a, b ) end
function table:bar ( c ) print ( self, c ) end 
Both functions unwind to the same thing. The difference is that the second function has the first argument implicitly declared as "self". When a function is called with colon notation, the table that was calling the function is automatically passed as first parameter, whatever it is.

Code: Select all

table.foo ( table, "foo" )
table.bar ( table, "foo" )

table:foo ( "bar" )
table:bar ( "bar" )
Because of that, both variants of function calling convention produce the same output, because they run the same code. The single difference is call notation, emphasis on "notation".
jpott
Prole
Posts: 8
Joined: Fri May 05, 2017 7:36 pm

Re: Help with constructor

Post by jpott »

i like this place
Post Reply

Who is online

Users browsing this forum: No registered users and 52 guests