Performance looping over table with Sprites

Hi all,
I am building a game where I keep a reference to my sprites in a table.
Then I loop over this table to draw all the sprites.
This goes fine, until I reach about 15 sprites, then performance starts to seriously drop.

My question: is there a better way to draw large numbers of sprites? These sprites are transparent PNG’s with retina versions. I am testing on iPad 3. My Code:


function draw()
   for i=#self.allSprites,1,-1 do 
      self.allSprites[i]:draw()
   end
end

Try to use mesh instead of sprite. Lots of sprites will kill performace. Here is an example i found on mesh vs sprites.


-- Use this function to perform your initial setup
function setup()
   --gameState is the current state of play
   -- 1 is sprites
   -- 2 is meshes
   iparameter("gameState",1,2,1)
   iparameter("size",25,100,40)
   g=mesh()
   g.texture="Planet Cute:Brown Block"
   xpos=0 --used for the movement of the background
   ground={}
   roof={}
   for i=1,200 do
       ground[i]=math.random(20)
       roof[i]=math.random(20)
       end
   FPS = 0
   watch("FPS")
   timeInterval = 0
   frameCount = 0
end

-- This function gets called once every frame
function draw()
--FPS print out from Minesweeper by @Reefwing

  frameCount = frameCount + 1
  timeInterval = timeInterval + DeltaTime

  if timeInterval > 1 then

      FPS = math.floor((frameCount / timeInterval)+0.5)
      timeInterval = 0
      frameCount = 0
  end
elementcount=0
background(0)
if gameState==1 then
   for i = 1,(1.5*WIDTH/size) do
       for h=1,ground[i] do
          sprite("Planet Cute:Ramp South",xpos+size*i-size,-50+(size/2)*h,size,size)
       elementcount = elementcount + 1
       end
       for h=1,roof[i] do
           sprite("Planet Cute:Ramp South",xpos+size*i-size,HEIGHT+50-(size/2)*h,size,size)
        elementcount = elementcount + 1
       end
   end
elseif gameState==2 then
g:clear()
  for i = 1,(1.5*WIDTH/size) do
       for h=1,ground[i] do
           local idx = g:addRect(xpos+size*i-size,-50+(size/2)*h,size,size)
            g:setRectTex(idx, 0, 0, 1, 1)
           elementcount = elementcount + 1
       end

       for h=1,roof[i] do
           local idx = g:addRect(xpos+size*i-size,HEIGHT+50-(size/2)*h,size,size)
           g:setRectTex(idx, 0, 0, 1, 1)
           elementcount = elementcount + 1
       end
   end
g:draw()
end

   xpos=xpos-1
      if xpos<-1*size then
       xpos=0
       end
       temp=ground[1]
       tempr=roof[1]
       for c=1,#ground-1 do
           ground[c]=ground[c+1]
           roof[c]=roof[c+1]
       end

       ground[#ground]=temp
       roof[#roof]=tempr
       text("Elements drawn: "..elementcount,WIDTH/2,HEIGHT/2)
end

You should try using mesh(), you can draw lots of things and it’s super fast.

Here is just a quick demo that I’m running on iPad 1

http://www.youtube.com/watch?v=8k8sSa2xs-g


--# Main
-- SpriteMesh

-- Use this function to perform your initial setup
function setup()
    meshes = {}
    extras = {}
    width = 101
    height = 171
    noSmooth()

    for i=1, 50 do
        meshes[i] = mesh()
        meshes[i].texture = readImage("Planet Cute:Character Pink Girl")
        extras[i] = {}
        extras[i].pos = vec2(math.random()*WIDTH, math.random()*HEIGHT)
        extras[i].vel = vec2(math.random()*2-1, math.random()*2-1)
    end
end

-- This function gets called once every frame
function draw()
    -- This sets a dark background color
    background(40, 40, 50)

    for i=1, #meshes do
        pushMatrix()
        extras[i].pos.x = extras[i].pos.x + extras[i].vel.x
        extras[i].pos.y = extras[i].pos.y + extras[i].vel.y
        if extras[i].pos.x < 0 or extras[i].pos.x > WIDTH then
            extras[i].vel.x = -extras[i].vel.x end
        if extras[i].pos.y < 0 or extras[i].pos.y > HEIGHT then
            extras[i].vel.y = -extras[i].vel.y end

        translate(extras[i].pos.x, extras[i].pos.y)
        meshes[i]:clear()
        meshes[i]:addRect(0,0,width,height)
        meshes[i]:draw()
        popMatrix()
    end

end


Verschiedene Bilder über Mesh laden. Siehe Video.

http://www.youtube.com/watch?v=vsSH9G5-d34&feature=youtube_gdata_player

--MeshBilder

--# Main
-- SpriteMesh

-- Use this function to perform your initial setup
function setup()
    meshes = {}
    extras = {}
    width = 120
    height = 140
    --noSmooth()

    for i=75, 95 do
        meshes[i] = mesh()
        meshes[i].texture = readImage("Dropbox:B"..i)
        extras[i] = {}
        extras[i].pos = vec2(math.random()*WIDTH, math.random()*HEIGHT)
        extras[i].vel = vec2(math.random()*2-1, math.random()*2-1)
    end
end

-- This function gets called once every frame
function draw()
    -- This sets a dark background color
    background(40, 40, 50)
    smooth()
    for i=75,95 do               --#meshes do
        pushMatrix()
        extras[i].pos.x = extras[i].pos.x + extras[i].vel.x
        extras[i].pos.y = extras[i].pos.y + extras[i].vel.y
        if extras[i].pos.x < 0 or extras[i].pos.x > WIDTH then
            extras[i].vel.x = -extras[i].vel.x end
        if extras[i].pos.y < 0 or extras[i].pos.y > HEIGHT then
            extras[i].vel.y = -extras[i].vel.y end

        translate(extras[i].pos.x, extras[i].pos.y)
        meshes[i]:clear()
        meshes[i]:addRect(0,0,width,height)
        meshes[i]:draw()
        popMatrix()
    end
end

http://youtu.be/vsSH9G5-d34

These are some great examples, thanks!
What I still fail to grasp is the basic principle of a mesh. In this code I’m trying to display a 200x200 part of a larger image, but it’s not working. Any idea why not?

function setup()
  mymesh = mesh()
  mymesh.texture = readImage("Dropbox:texture")
end

function draw()
  local idx=mymesh.addRect(0,0,200,200)
  mymesh.setRectTex(idx,0,0,200,200)
  mymesh.draw()
end

Hate to toot my own horn (no I don’t) but:
http://twolivesleft.com/Codea/Talk/discussion/1862/

You can grab SpriteBatch.lua here:
https://github.com/apendley/TPCodeaExamples/blob/master/TPCodeaTabExample.codea/SpriteBatch.lua

SpriteBatch doesn’t only work on sprite sheets…you can render (lots) of regular sprites as well and still benefit from the speed increase. Here’s a small example:


function setup()
    batch = SpriteBatch("Small World:Church")
end

function draw()
    background(0)
    
    -- draw sprites like normal, only use the batch object
    -- instead:
    batch:sprite("Church", WIDTH/2, HEIGHT/2)
    
    -- you can even use Codea's style and transform functions:
    pushMatrix()
    translate(200, 200)
    rotate(45)
    tint(255, 255, 255, 128)
    batch:sprite("Church")
    popMatrix()
    
    -- now draw the batch
    batch:draw()  
end


Thanks again, lots of cool examples to work with!