First of all - sorry for my English :)
We all know a large number of elements on screen kills Corona performance and when You do it somewhere cute bunny dies. Lets do some benchmark ( http://www.concat.pl/temp/tile_test.zip ) and kill some of this little bastards :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 | display.setStatusBar( display.HiddenStatusBar ) --system.activate( "multitouch" ) local fps = require("fps") local performance = fps.PerformanceOutput.new() -- SET UP DISPLAY GROUP "TREE" -- "p2" is the parallax layer in the distant back. local p2_moveGroup = display.newGroup() -- "p1" is the parallax layer next closest, visually. local p1_moveGroup = display.newGroup() -- "p0" is the 'core' movement group. This will scroll at the exact mouse/touch movement speed. -- note that this group has 2 'child' display groups. You can have multiple child groups scrolling at the same speed. -- this technique (multiple child groups) can be applied to any parallax layer, if you need more z-index layering. -- remember the rule of thumb: layers declared LATER appear in FRONT of other layers visually. local p0_moveGroup = display.newGroup() local p0_subGroup1 = display.newGroup() ; p0_moveGroup:insert(p0_subGroup1) --local p0_subGroup2 = display.newGroup() ; p0_moveGroup:insert(p0_subGroup2) -- set local "pointers" to movement groups (used in moveCamera function below) local p2 = p2_moveGroup local p1 = p1_moveGroup local p0 = p0_moveGroup -- DECLARE WORLD LIMITS -- in general, XMax and YMax will be your 'world size'. -- for example, a world of 960 x 1200 will use these numbers as the XMax and YMax values. -- tweaking of these numbers will be necessary to suit your own game, background sizes, etc. local worldLimits = { XMin=0 , YMin=0 , XMax=10000 , YMax=10000 } local testTexture = {} local testTextureId = {} local n = 0 -- Arrange textures for i = 1, 100 do for j = 1, 100 do n = n + 1 testTexture[n] = display.newImageRect( p0_subGroup1, "texture.png", 100, 100, true ) testTexture[n].x = (j*100) - 50 testTexture[n].y = (i*100) - 50 testTexture[n].isVisible = true testTexture[n].alpha = 0.5 table.insert(testTextureId, #testTextureId+1, n) end end -- basic visible elementh table --[[local visibleElem = {} local m = 0 for k = 1, 15 do for l = 1, 15 do local increaseX = l + (m*100) table.insert(visibleElem, #visibleElem+1, increaseX) testTexture[increaseX].alpha = 0.5 testTexture[increaseX].isVisible = true end m = m + 1 end ]]-- -- these two lines adjust the world limits based on the device screen size local adjustedXMax = display.contentWidth-worldLimits.XMax local adjustedYMax = display.contentHeight-worldLimits.YMax -- 2 testing functions! This gives you a tap position in 'world' coordinates, not screen coordinates. local function clearTestDot(event) event:removeSelf() ; event = nil end local function worldTouchPoint(event) local adjustedX = event.x - p0.x local adjustedY = event.y - p0.y local touchpoint = display.newCircle( p0_moveGroup, adjustedX, adjustedY, 12 ) touchpoint:setFillColor(255,255,255,255) transition.to( touchpoint, { time=500, alpha=0.0, onComplete=clearTestDot } ) end -- "touchSensor" spans the entire 'world' size. Any multitouch/tap on this 'sensor' produces a reaction. -- note its alpha setting of "0"... you don't want to see it in the game! local touchSensor = display.newRect( p0_subGroup1, 0, 0, worldLimits.XMax, worldLimits.YMax ) touchSensor:setFillColor(255,255,255,0) performance.group.x, performance.group.y = display.contentWidth/2, 0; local function moveCamera( event ) -- when touch first begins... if ( event.phase == "began" ) then -- set core moveGroup to 'focused' display.getCurrentStage():setFocus( p0, event.id ) -- set touch location to relative position (relative to moveGroup) p0.x0 = event.x - p0.x p0.y0 = event.y - p0.y -- when touch moves... elseif ( event.phase == "moved" ) then -- set 'predicted' new X and Y positions for core moveGroup p0.newX = event.x - p0.x0 p0.newY = event.y - p0.y0 -- in all following calculations, adjust decimals for parallax scrolling rate -- a smaller decimal moves a layer less, so use smaller multipliers as you move back in visual space -- in this demo, "0.4" is used for the near parallax, "0.2" used for the distant back parallax -- if new X position is within world limits, move it there! if ( p0.newX <= worldLimits.XMin and p0.newX >= adjustedXMax ) then p0.x = p0.newX ; p1.x = p0.x*0.4 ; p2.x = p0.x*0.2 -- if not, lock to world limits to prevent further scrolling elseif ( p0.newX > worldLimits.XMin ) then p0.x = worldLimits.XMin ; p1.x = p0.x*0.4 ; p2.x = p0.x*0.2 elseif ( p0.newX < worldLimits.XMax ) then p0.x = adjustedXMax ; p1.x = p0.x*0.4 ; p2.x = p0.x*0.2 end -- if new Y position is within world limits, move it there! if ( p0.newY <= worldLimits.YMin and p0.newY >= adjustedYMax ) then p0.y = p0.newY ; p1.y = p0.y*0.4 ; p2.y = p0.y*0.2 -- if not, lock to world limits to prevent further scrolling elseif ( p0.newY > worldLimits.YMin ) then p0.y = worldLimits.YMin ; p1.y = p0.y*0.4 ; p2.y = p0.y*0.2 elseif ( p0.newY < worldLimits.YMax ) then p0.y = adjustedYMax ; p1.y = p0.y*0.4 ; p2.y = p0.y*0.2 end -- when touch ends... elseif ( event.phase == "ended" or event.phase == "cancelled" ) then -- un-focus core moveGroup display.getCurrentStage():setFocus( p0, nil ) end return true end p0:addEventListener( "touch", moveCamera ) p0:addEventListener( "tap", worldTouchPoint ) |
Yeah um, I downloaded your demo, and I read your post, and I am failing to see:
1) The relevance of the Isogenic Game Engine. It has nothing to do with corona
2) Your demo manages 30 fps reasonably consistantly but each tile is 100x100 pixels - ie it's showing a maximum of 20 of these tiles onscreen at once. What is so special?
I know we all want to do our scrolly map as quickly as possible, but I just don't understand how this post helps with that at all. What am I missing?
Barry
Hah silly me, why did I bother looking at framerates in the simulator? :)
Ok I changed your config file to 60fps, and then built it for my ipod touch.
errr.. when moving I'm getting anywhere from 20 to 5 fps?
OK this whole performance thing is really really confusing me.
What I'll do is create what I believe is a reasonable, standardised framework, and I'll provide a test map in Tiled format, and we can each see about getting the best framerates on it (is it OK if I use your frame-rate display code?).
I think despite us all wanting the same goal, we are all getting bogged down in the methods, without any actual real useful end result.
Barry
First of all - sorry but I'm little drunk today :)
You miss the point. Look at this code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 | display.setStatusBar( display.HiddenStatusBar ) --system.activate( "multitouch" ) local fps = require("fps") local performance = fps.PerformanceOutput.new() local Abs = math.abs local Floor = math.floor -- SET UP DISPLAY GROUP "TREE" -- "p2" is the parallax layer in the distant back. local p2_moveGroup = display.newGroup() -- "p1" is the parallax layer next closest, visually. local p1_moveGroup = display.newGroup() -- "p0" is the 'core' movement group. This will scroll at the exact mouse/touch movement speed. -- note that this group has 2 'child' display groups. You can have multiple child groups scrolling at the same speed. -- this technique (multiple child groups) can be applied to any parallax layer, if you need more z-index layering. -- remember the rule of thumb: layers declared LATER appear in FRONT of other layers visually. local p0_moveGroup = display.newGroup() local p0_subGroup1 = display.newGroup() ; p0_moveGroup:insert(p0_subGroup1) --local p0_subGroup2 = display.newGroup() ; p0_moveGroup:insert(p0_subGroup2) -- set local "pointers" to movement groups (used in moveCamera function below) local p2 = p2_moveGroup local p1 = p1_moveGroup local p0 = p0_moveGroup -- DECLARE WORLD LIMITS -- in general, XMax and YMax will be your 'world size'. -- for example, a world of 960 x 1200 will use these numbers as the XMax and YMax values. -- tweaking of these numbers will be necessary to suit your own game, background sizes, etc. local worldLimits = { XMin=0 , YMin=0 , XMax=10000 , YMax=10000 } ---------------------------------------------------------------- -- PRIVATE: TABLE UTIL ---------------------------------------------------------------- local function val_To_Str ( v ) if "string" == type( v ) then v = string.gsub( v, "\n", "\\n" ) if string.match( string.gsub(v,"[^'\"]",""), '^"+$' ) then return "'" .. v .. "'" end return '"' .. string.gsub(v,'"', '\\"' ) .. '"' else return "table" == type( v ) and table.tostring( v ) or tostring( v ) end end local function key_To_Str ( k ) if "string" == type( k ) and string.match( k, "^[_%a][_%a%d]*$" ) then return k else return "[" .. val_To_Str( k ) .. "]" end end local function table_To_Str( tbl ) local result, done = {}, {} for k, v in ipairs( tbl ) do table.insert( result, val_To_Str( v ) ) done[ k ] = true end for k, v in pairs( tbl ) do if not done[ k ] then table.insert( result, key_To_Str( k ) .. "=" .. val_To_Str( v ) ) end end return table.concat(result) --table.concat( result, "," ) end local testTexture = {} local testTextureId = {} local n = 0 -- Arrange textures for i = 1, 100 do for j = 1, 100 do n = n + 1 testTexture[n] = display.newImageRect( p0_subGroup1, "texture.png", 100, 100, true ) testTexture[n].x = (j*100) - 50 testTexture[n].y = (i*100) - 50 testTexture[n].isVisible = false table.insert(testTextureId, n, n) end end -- basic visible elementh table local visibleElem = {} local m = 0 for k = 1, 15 do for l = 1, 15 do local increaseX = l + (m*100) table.insert(visibleElem, increaseX) testTexture[increaseX].alpha = 0.5 testTexture[increaseX].isVisible = true end m = m + 1 end -- these two lines adjust the world limits based on the device screen size local adjustedXMax = display.contentWidth-worldLimits.XMax local adjustedYMax = display.contentHeight-worldLimits.YMax -- 2 testing functions! This gives you a tap position in 'world' coordinates, not screen coordinates. local function clearTestDot(event) event:removeSelf() ; event = nil end local function worldTouchPoint(event) local adjustedX = event.x - p0.x local adjustedY = event.y - p0.y local touchpoint = display.newCircle( p0_moveGroup, adjustedX, adjustedY, 12 ) touchpoint:setFillColor(255,255,255,255) transition.to( touchpoint, { time=500, alpha=0.0, onComplete=clearTestDot } ) end -- "touchSensor" spans the entire 'world' size. Any multitouch/tap on this 'sensor' produces a reaction. -- note its alpha setting of "0"... you don't want to see it in the game! local touchSensor = display.newRect( p0_subGroup1, 0, 0, worldLimits.XMax, worldLimits.YMax ) touchSensor:setFillColor(255,255,255,0) performance.group.x, performance.group.y = display.contentWidth/2, 0; ---------------------------------------------------------------- -- UGLY AND INEFFICENT function!!!! ---------------------------------------------------------------- local mathX local mathY local positionXY local tempYArray = {} local tempVisibleElem = {} local tempY local tempX local function clickPosition (x, y) local x = x local y = y local yToArray mathX = Abs(Floor(x*0.01)) + 1 mathY = Abs(Floor(y*0.01)) + 1 --[[if (mathY == 1) then positionXY = mathX else positionXY = (mathY*100) + mathX end ]]-- for i,v in ipairs(visibleElem) do testTexture[v].isVisible = false table.remove(visibleElem,i) end tempYArray = {} for k = -6, 7 do tempY = mathY + k if (tempY > 0) then table.insert(tempYArray, #tempYArray + 1 ,tempY) end end tempVisibleElem = {} for l = -6, 7 do tempX = mathX + (l*100) if (tempX > 0) then for i,v in ipairs(tempYArray) do positionXY = (mathY*100) + mathX testTexture[positionXY].alpha = 0.5 testTexture[positionXY].isVisible = true end end end --print (#visibleElem) --return positionXY end local function moveCamera( event ) -- when touch first begins... if ( event.phase == "began" ) then -- set core moveGroup to 'focused' display.getCurrentStage():setFocus( p0, event.id ) -- set touch location to relative position (relative to moveGroup) p0.x0 = event.x - p0.x p0.y0 = event.y - p0.y -- when touch moves... elseif ( event.phase == "moved" ) then -- set 'predicted' new X and Y positions for core moveGroup p0.newX = event.x - p0.x0 p0.newY = event.y - p0.y0 clickPosition (p0.x0, p0.y0) -- in all following calculations, adjust decimals for parallax scrolling rate -- a smaller decimal moves a layer less, so use smaller multipliers as you move back in visual space -- in this demo, "0.4" is used for the near parallax, "0.2" used for the distant back parallax -- if new X position is within world limits, move it there! if ( p0.newX <= worldLimits.XMin and p0.newX >= adjustedXMax ) then p0.x = p0.newX ; p1.x = p0.x*0.4 ; p2.x = p0.x*0.2 -- if not, lock to world limits to prevent further scrolling elseif ( p0.newX > worldLimits.XMin ) then p0.x = worldLimits.XMin ; p1.x = p0.x*0.4 ; p2.x = p0.x*0.2 elseif ( p0.newX < worldLimits.XMax ) then p0.x = adjustedXMax ; p1.x = p0.x*0.4 ; p2.x = p0.x*0.2 end -- if new Y position is within world limits, move it there! if ( p0.newY <= worldLimits.YMin and p0.newY >= adjustedYMax ) then p0.y = p0.newY ; p1.y = p0.y*0.4 ; p2.y = p0.y*0.2 -- if not, lock to world limits to prevent further scrolling elseif ( p0.newY > worldLimits.YMin ) then p0.y = worldLimits.YMin ; p1.y = p0.y*0.4 ; p2.y = p0.y*0.2 elseif ( p0.newY < worldLimits.YMax ) then p0.y = adjustedYMax ; p1.y = p0.y*0.4 ; p2.y = p0.y*0.2 end -- when touch ends... elseif ( event.phase == "ended" or event.phase == "cancelled" ) then -- un-focus core moveGroup display.getCurrentStage():setFocus( p0, nil ) end return true end p0:addEventListener( "touch", moveCamera ) p0:addEventListener( "tap", worldTouchPoint ) |
OK performance is better, but is it *best*?
What happens with your method in a map with tiles of say 48x48?