Closure Help Needed

I’m attempting to use Lua’s closures in the following bit of code:

[lua]

for k,v in pairs(tiles) do
            for key, value in pairs(v) do
                position.xPos = value[1]
                position.yPos = value[2]
               
                if self.inventoryData[itemId].Type == “Seed” then
                    item = SeedClass.new(itemData, self._game, position) –
                    self.items[item.id] = item
                   
                    TaskManager:addTask({id=item.id, desc="Plant “…item.name, duration=15000, repeated=false,
                    callback = function()
                        print(”* x: “…item.mainImage.x) --always referring to last item created in the for loop
                        print(”* Y: "…item.mainImage.y) --always referring to last item created in the for loop
                        return item:action({x=item.mainImage.x, y=item.mainImage.y}) --always referring to last item created in the for loop
                    end})
                end
            end
        end

[/lua]

Many thanks…

The idea being that a task is added to create the object, once the task’s duration has passed the object is displayed on screen in the item:action() call via a call to the task.callback() method.

Although this code works fine it comes across a problem when I’m trying to spawn multiple objects; the callback function only refers to the last item created up above - so the call to item:action is always being made on the last created item object. I’m pretty sure there’s a way to do this via the corretc use of closures, but can’t seem to get it right yet…

Any 

OK… so no sooner do I post then I find a solution…

[lua]

item = SeedClass.new(itemData, self._game, position) –
                    self.items[item.id] = item
                   
                    local function createTask(item)
                        return TaskManager:addTask({id=item.id, desc="Plant “…item.name, duration=15000, repeated=false,
                        callback = function()
                            print(”* x: “…item.mainImage.x)
                            print(”* Y: "…item.mainImage.y)
                            item:action({x=item.mainImage.x, y=item.mainImage.y})
                        end})
                    end
                   
                    createTask(item)

[/lua]

I’ve changed the code to this, it gets around the problem of ensuring the correct instance data is passed - however it’s far from an elegant solution, so be interested to know whether anybody has any better alternatives.

Many thanks…

You did not post where you call the callback function and where is the callback variable declared it looks like it’s global thus will change for ever new instance.

Hi Primoz…

The callback function is called in the TaskManager class.  Essentially there is a method in that class that calls the callback when the task duration is finished:

[lua]

function TaskManager:isTaskReady(id)
    if self.mTasks[id].taskDuration <= 0 then
        self.mTasks[id].callback()
        if self.mTasks[id].repeated == false then
            self:removeTask(id)
        end
        return true
    end
end

[/lua]

The callback variable is part of a table that is passed through in the code above to TaskManager:addTask(), as so:

[lua]

TaskManager:addTask({id=item.id, desc="Plant "…item.name, duration=15000, repeated=false,

callback = function()

  print("* x: “…item.mainImage.x) --always referring to last item created in the for loop
  print(”* Y: "…item.mainImage.y) --always referring to last item created in the for loop
  return item:action({x=item.mainImage.x, y=item.mainImage.y}) --always referring to last item created in the for loop

end})

[/lua]

The technique in my second post works, I just thought there might be a more elegant solution then encapsulating the call within a function.

Thanks for checking over it for me…

Ah sorry I missed that it was a parameter.

What I like to do in a case like this is to add just the reference to the function that needs to be called and a table of parameters that it needs like so:

params = { 1, “something” }

task = {call = func1, params = params }

TaskManager.addTask(task)

and when task manager calls it just do this:

task.call(unpack(task.params))

OK… so no sooner do I post then I find a solution…

[lua]

item = SeedClass.new(itemData, self._game, position) –
                    self.items[item.id] = item
                   
                    local function createTask(item)
                        return TaskManager:addTask({id=item.id, desc="Plant “…item.name, duration=15000, repeated=false,
                        callback = function()
                            print(”* x: “…item.mainImage.x)
                            print(”* Y: "…item.mainImage.y)
                            item:action({x=item.mainImage.x, y=item.mainImage.y})
                        end})
                    end
                   
                    createTask(item)

[/lua]

I’ve changed the code to this, it gets around the problem of ensuring the correct instance data is passed - however it’s far from an elegant solution, so be interested to know whether anybody has any better alternatives.

Many thanks…

You did not post where you call the callback function and where is the callback variable declared it looks like it’s global thus will change for ever new instance.

Hi Primoz…

The callback function is called in the TaskManager class.  Essentially there is a method in that class that calls the callback when the task duration is finished:

[lua]

function TaskManager:isTaskReady(id)
    if self.mTasks[id].taskDuration <= 0 then
        self.mTasks[id].callback()
        if self.mTasks[id].repeated == false then
            self:removeTask(id)
        end
        return true
    end
end

[/lua]

The callback variable is part of a table that is passed through in the code above to TaskManager:addTask(), as so:

[lua]

TaskManager:addTask({id=item.id, desc="Plant "…item.name, duration=15000, repeated=false,

callback = function()

  print("* x: “…item.mainImage.x) --always referring to last item created in the for loop
  print(”* Y: "…item.mainImage.y) --always referring to last item created in the for loop
  return item:action({x=item.mainImage.x, y=item.mainImage.y}) --always referring to last item created in the for loop

end})

[/lua]

The technique in my second post works, I just thought there might be a more elegant solution then encapsulating the call within a function.

Thanks for checking over it for me…

Ah sorry I missed that it was a parameter.

What I like to do in a case like this is to add just the reference to the function that needs to be called and a table of parameters that it needs like so:

params = { 1, “something” }

task = {call = func1, params = params }

TaskManager.addTask(task)

and when task manager calls it just do this:

task.call(unpack(task.params))