1
votes

Is it possible to put a listener function inside of an object module?

I have a Cloud class that implements a Display Object. Right now, the class only creates the image. I'd like it to also be in charge of its own listener event, so that I do not need to keep calling the addEventListener per object I spawn.

I've tried several variations, and I always end up with the listener function as nil. I've also tried splitting it out so that the addEventListener function is called in the main.

I'm starting to get the feeling that listener functions in objects aren't supported. Maybe I'm taking the wrong approach to this? Is what I'm asking feasible? And if yes, what am I doing wrong?

--
-- Cloud.lua
--

local Cloud = {}
local mtaCloud = { __index = cloud } -- metatable

local display = require "display" 

-- DOESN'T WORK
function Cloud.scroll( event )
  img.y = img.y - img.scrollSpeed
  if (img.y + img.contentWidth) < 0 then
    img.x = 0
    img:translate( math.random(1,_W), 1800  )
  end
end

function Cloud.New( strImage, intHeight, intWidth, intScrollSpeed)

  local image = display.newImageRect( strImage, intWidth, intHeight )
  image.anchorX = 0; image.anchorY = 0.5;
  image.x = _W/2; image.y = _H/2;
  image.scrollSpeed = 10
  image.enterFrame = scroll
  Runtime:addEventListener("enterFrame", scroll)

  local newCloud = {

    img = image

  }

return setmetatable( newCloud, mtaCloud )

end

return Cloud

-- main.lua (simplified)
local cloud = require "object.cloud"

function scene:create( event )
  local group = self.view
  cloud = cloud.New("img/cloud.png", 230, 140)
  group:insert(cloud.img)
end
1

1 Answers

1
votes

Yes, it is possible to set listeners on objects, if you mean objects in the usual object-oriented programming sense discussed in Chapter 16 of Programming in Lua. It's a matter of getting the self references right. I've included an implementation that works in this answer.

I'm not sure if you can do this on a DisplayObject, but that isn't what you were doing in your example either (the DisplayObject is your image not what is returned by your constructor Cloud.New(), which is table newCloud).

Here is one way to do it that works for sure. Your Cloud.lua should have something like this (I've used ShapeObjects in this scratch implementation for testing; you would make changes to use your image assets):

-- Cloud.lua

local Cloud = {}

function Cloud:scroll()

    self.img.x = self.img.x + self.scrollSpeed
    if self.img.x >= display.contentWidth + self.img.contentWidth then
        self.img.x = - self.img.contentWidth
    end

end

function Cloud:enterFrame( event )
    self:scroll()
end

function Cloud:new( intHeight, intWidth, intScrollSpeed )

    local aCloud = {}

    local img = display.newRect( 0, 0, intWidth, intHeight )
    img:setFillColor( 1 )

    aCloud.img = img
    aCloud.scrollSpeed = intScrollSpeed
    aCloud.id = id

    setmetatable( aCloud, self )
    self.__index = self

    Runtime:addEventListener( "enterFrame", aCloud )

    return aCloud

end

return Cloud

Notice the setmetatable and self.__index = self in the Cloud:new() function. This is key.

I've used the "colon notation" in the function definitions (eg. Cloud:new(...) ) which is syntactic sugar for the "dot notation" you used in which the first argument to the function is self (eg. Cloud.new( self, ...) ).

Also, I am using an object (table) listener (not a function); on each enterFrame event, the Runtime will call the enterFrame() function if one exists for the object (i.e. is found in the specified table).

Assuming a file named "Cloud.lua" is found in the project subdirectory "objects", in your scene, you would have something like:

local cloud = require("objects.Cloud")

local maxScrollRate = 4
local cloud1 = cloud:new( 100, 50, 1 + math.random( maxScrollRate )  )
cloud1.img.x = 200
cloud1.img.y = 200
sceneGroup:insert(cloud1.img)

local cloud2 = cloud:new( 100, 50, 1 + math.random( maxScrollRate ) )
cloud2.img.x = 100
cloud2.img.y = 400
sceneGroup:insert(cloud2.img)

Hope that helps.