Performance fix part 2

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?

views:1695 update:2011/10/13 16:30:09
corona forums © 2003-2011