calling sprite animations in a class cannot destroy them

I have a sprite animation in a class that will get called over and over in different places like so:

sprite=config.gui_t.ball_match_decoration_sprite

sprite:setSequence(‘once’)

sprite:play()

sprite:addEventListener( “sprite”, listener )

And then I destroy it when it is finished like so:

function listener(event)

 if (event.phase==“ended”)then

   sprite:removeSelf( )

   sprite=nil 

 end

end

My problem is if a sprite gets called to quickly before the previous one has destroyed itself, than that previous one will not remove itself and that image is still on the screen.  Can someone please explain a way that I can have all sprites that are called destroy themselves when finished?

Hi @russm,

We may need some more code to assist you on this. Does the sprite-creating class return the sprite object to the module which called it? If so, you may need to put the “destroy” function in the calling module and apply the listener to the returned object.

Brent

This is the sprite creating class where we create, please assist me where to put inside this code.  Thank you so much i think this is exactly what is wrong!

[lua]

function gui.ball_match_decoration_sprite(x, y)

local options={

width=152,

height=152,

numFrames=18,

sheetContentWidth=914,

sheetContentHeight=458

}

local image_sheet=graphics.newImageSheet(config.title…"/card/spot-hit-animation.png", system.DocumentsDirectory, options)

local sprite=display.newSprite(image_sheet, { name=“once”, start=1, count=18, time=1000, loopCount=1 })

sprite.x=x

sprite.y=y

return sprite

end

[/lua]

Hi @russm305,

This looks basically OK, except that you shouldn’t be creating a new image sheet within each instance of this call. Instead, you should create the image sheet(s) in the calling module and pass the reference to that image sheet to the sprite creator, so it can utilize it, but you’re not creating a new one each time.

Now on the calling module side, after you call this function and get the sprite returned, what code do you put after?

Brent

@Brent

Thank you for reply.  Okay that is the sprite in the gui class.  Whenever that sprite needs to be called I just lay it and it works fine.

[lua]

pepper_decoration_sprite=config.gui_t.ball_match_decoration_sprite(spot_hit_pepper.x, spot_hit_pepper.y)

self.view.parent:insert(pepper_decoration_sprite)

pepper_decoration_sprite:setSequence(‘once’)

pepper_decoration_sprite:play()

pepper_decoration_sprite:addEventListener( “sprite”, listener )

[/lua]

the listener function I was trying to destroy them with but it wasn’t working because another sprite would be created before i destroyed the previous one:

function listener(event)

if (event.phase==“ended”)then

pepper_decoration_sprite:removeSelf( )

pepper_decoration_sprite=nil

end

end

Hi @russm305,

Am I seeing correctly that you’re using a global reference of “pepper_decoration_sprite”? That may be the problem right there… if you create one sprite, then you create another with the same reference, the previous sprite will no longer be known by that variable reference, so you will have no easy way to remove it.

Brent

@Brent

Thank you but I don’t think that is the case.  I apologize I didnt mention I forward declare “pepper_decoration_sprite”.  So at the top of module that calls sprite I have:

local pepper_decoration_sprite

–then later on I have

[lua]

  1. pepper_decoration_sprite=config.gui_t.ball_match_decoration_sprite(spot_hit_pepper.x, spot_hit_pepper.y)
  2. self.view.parent:insert(pepper_decoration_sprite)
  3.  
  4. pepper_decoration_sprite:setSequence(‘once’)
  5. pepper_decoration_sprite:play()

[/lua]

Then in a separate module I have the sprite code which makes the sprite which is the area you felt I should be destroying the sprite because thats where its created.  Do you still think I can do something in here?

[lua]function gui.ball_match_decoration_sprite(x, y)

local options={

width=152,

height=152,

numFrames=18,

sheetContentWidth=914,

sheetContentHeight=458

}

local image_sheet=graphics.newImageSheet(config.title…"/card/spot-hit-animation.png", system.DocumentsDirectory, options)

local sprite=display.newSprite(image_sheet, { name=“once”, start=1, count=18, time=1000, loopCount=1 })

sprite.x=x

sprite.y=y

return sprite

end[/lua]

Hi @russm305,

All of that looks OK. You said something about the sprite not being destroyed? In your sprite “event” listener, did you try to reference the sprite like this instead?

[lua]

function listener( event )

   if ( event.phase==“ended” ) then

      event.target:removeSelf( )

      event.target = nil 

   end

end

[/lua]

Brent, yes the sprite is not being destroyed,  This is a Keno game so when a ball lands on a matching spot a little animation plays on that spot.  The problem is the balls shoot out so fast that if one animation does not stop before the next one is called the first one will not destroy itself and your left with the last image from image sheet.

[lua]pepper_decoration_sprite=config.gui_t.ball_match_decoration_sprite(spot_hit_pepper.x, spot_hit_pepper.y)

self.view.parent:insert(pepper_decoration_sprite)

pepper_decoration_sprite:setSequence(‘once’)

pepper_decoration_sprite:play()

pepper_decoration_sprite:addEventListener( “sprite”, listener )

function listener(event)

if (event.phase==“ended”)then

pepper_decoration_sprite:removeSelf( )

pepper_decoration_sprite=nil

print(“test”)

end

end

[/lua]

​I tried your new way and no luck, it still didn’t destroy if called to quick

  1. function listener( event )
  2.    if ( event.phase==“ended” ) then
  3.       event.target:removeSelf( )
  4.       event.target = nil 
  5.    end
  6. end

Any other ideas?  Maybe set it up in a differently?

Hi @russm305,

OK, this changes the approach slightly. I would avoid using the same reference for the balls, and instead create a new local variable for each sprite (ball) that gets shot out. Then, instead of creating the one local variable, create a holding table and insert each (local) ball into that table when it shoots out. In the sprite event listener, use my suggested approach of “event.target” to manage the end of the animation, but also when that occurs (the animation completes), remove the ball’s internal ID from the holding table. I find that a simple looping function like this works well:

[lua]

local function removeID( tableID, objID )

    for i=1,#tableID do

        local v = tableID[i] ; if ( v and v == objID ) then table.remove( tableID,i ) ; break end ; v = nil

    end ; return false

end

[/lua]

Note that “tableID” should be the reference to the holding table that you created.

Hope this helps,

Brent

Hi @russm,

We may need some more code to assist you on this. Does the sprite-creating class return the sprite object to the module which called it? If so, you may need to put the “destroy” function in the calling module and apply the listener to the returned object.

Brent

This is the sprite creating class where we create, please assist me where to put inside this code.  Thank you so much i think this is exactly what is wrong!

[lua]

function gui.ball_match_decoration_sprite(x, y)

local options={

width=152,

height=152,

numFrames=18,

sheetContentWidth=914,

sheetContentHeight=458

}

local image_sheet=graphics.newImageSheet(config.title…"/card/spot-hit-animation.png", system.DocumentsDirectory, options)

local sprite=display.newSprite(image_sheet, { name=“once”, start=1, count=18, time=1000, loopCount=1 })

sprite.x=x

sprite.y=y

return sprite

end

[/lua]

Hi @russm305,

This looks basically OK, except that you shouldn’t be creating a new image sheet within each instance of this call. Instead, you should create the image sheet(s) in the calling module and pass the reference to that image sheet to the sprite creator, so it can utilize it, but you’re not creating a new one each time.

Now on the calling module side, after you call this function and get the sprite returned, what code do you put after?

Brent

@Brent

Thank you for reply.  Okay that is the sprite in the gui class.  Whenever that sprite needs to be called I just lay it and it works fine.

[lua]

pepper_decoration_sprite=config.gui_t.ball_match_decoration_sprite(spot_hit_pepper.x, spot_hit_pepper.y)

self.view.parent:insert(pepper_decoration_sprite)

pepper_decoration_sprite:setSequence(‘once’)

pepper_decoration_sprite:play()

pepper_decoration_sprite:addEventListener( “sprite”, listener )

[/lua]

the listener function I was trying to destroy them with but it wasn’t working because another sprite would be created before i destroyed the previous one:

function listener(event)

if (event.phase==“ended”)then

pepper_decoration_sprite:removeSelf( )

pepper_decoration_sprite=nil

end

end

Hi @russm305,

Am I seeing correctly that you’re using a global reference of “pepper_decoration_sprite”? That may be the problem right there… if you create one sprite, then you create another with the same reference, the previous sprite will no longer be known by that variable reference, so you will have no easy way to remove it.

Brent

@Brent

Thank you but I don’t think that is the case.  I apologize I didnt mention I forward declare “pepper_decoration_sprite”.  So at the top of module that calls sprite I have:

local pepper_decoration_sprite

–then later on I have

[lua]

  1. pepper_decoration_sprite=config.gui_t.ball_match_decoration_sprite(spot_hit_pepper.x, spot_hit_pepper.y)
  2. self.view.parent:insert(pepper_decoration_sprite)
  3.  
  4. pepper_decoration_sprite:setSequence(‘once’)
  5. pepper_decoration_sprite:play()

[/lua]

Then in a separate module I have the sprite code which makes the sprite which is the area you felt I should be destroying the sprite because thats where its created.  Do you still think I can do something in here?

[lua]function gui.ball_match_decoration_sprite(x, y)

local options={

width=152,

height=152,

numFrames=18,

sheetContentWidth=914,

sheetContentHeight=458

}

local image_sheet=graphics.newImageSheet(config.title…"/card/spot-hit-animation.png", system.DocumentsDirectory, options)

local sprite=display.newSprite(image_sheet, { name=“once”, start=1, count=18, time=1000, loopCount=1 })

sprite.x=x

sprite.y=y

return sprite

end[/lua]

Hi @russm305,

All of that looks OK. You said something about the sprite not being destroyed? In your sprite “event” listener, did you try to reference the sprite like this instead?

[lua]

function listener( event )

   if ( event.phase==“ended” ) then

      event.target:removeSelf( )

      event.target = nil 

   end

end

[/lua]

Brent, yes the sprite is not being destroyed,  This is a Keno game so when a ball lands on a matching spot a little animation plays on that spot.  The problem is the balls shoot out so fast that if one animation does not stop before the next one is called the first one will not destroy itself and your left with the last image from image sheet.

[lua]pepper_decoration_sprite=config.gui_t.ball_match_decoration_sprite(spot_hit_pepper.x, spot_hit_pepper.y)

self.view.parent:insert(pepper_decoration_sprite)

pepper_decoration_sprite:setSequence(‘once’)

pepper_decoration_sprite:play()

pepper_decoration_sprite:addEventListener( “sprite”, listener )

function listener(event)

if (event.phase==“ended”)then

pepper_decoration_sprite:removeSelf( )

pepper_decoration_sprite=nil

print(“test”)

end

end

[/lua]

​I tried your new way and no luck, it still didn’t destroy if called to quick

  1. function listener( event )
  2.    if ( event.phase==“ended” ) then
  3.       event.target:removeSelf( )
  4.       event.target = nil 
  5.    end
  6. end

Any other ideas?  Maybe set it up in a differently?

Hi @russm305,

OK, this changes the approach slightly. I would avoid using the same reference for the balls, and instead create a new local variable for each sprite (ball) that gets shot out. Then, instead of creating the one local variable, create a holding table and insert each (local) ball into that table when it shoots out. In the sprite event listener, use my suggested approach of “event.target” to manage the end of the animation, but also when that occurs (the animation completes), remove the ball’s internal ID from the holding table. I find that a simple looping function like this works well:

[lua]

local function removeID( tableID, objID )

    for i=1,#tableID do

        local v = tableID[i] ; if ( v and v == objID ) then table.remove( tableID,i ) ; break end ; v = nil

    end ; return false

end

[/lua]

Note that “tableID” should be the reference to the holding table that you created.

Hope this helps,

Brent