在开发应用程序时,应始终考虑设计选择如何影响性能。尽管核心功能不断改进,但移动设备在处理能力、内存使用和电池续航时间方面仍然面临着根本性的限制。因此,性能和优化对于实现更快的响应时间、最大限度地减少内存使用和最大限度地延长电池续航时间至关重要。
内存是移动设备上的关键资源。如果应用程序消耗过多内存,某些设备甚至可能会终止它。
通常**不建议**使用全局变量,但如果为了方便必须使用它们,请确保在不再需要它们时将它们从内存中移除
**保持资源文件尽可能小** — 应用程序使用的资源文件通常驻留在磁盘上。它们必须加载到内存中才能使用。图像和音频文件应尽可能小。还应尽可能重复使用相同的图像资源。例如,如果您设计一个
**延迟加载资源** — 避免在实际需要之前加载资源文件。尽管
**从显示层次结构中移除对象** — 创建显示对象时,它会隐式添加到显示层次结构中。当不再需要显示对象时,应将其从显示层次结构中移除,并将其引用设置为 `nil`。这会使对象有资格进行垃圾回收。但是,这并不能保证对象将从内存中移除。如果内存中的其他变量引用了该显示对象,Lua 将**不会**将其视为垃圾。有关移除对象的更多信息,请参阅显示对象指南。
由于外形尺寸小,移动设备的电池续航时间 inherently 有限。您可以通过遵循以下做法来提高电池续航时间。
**网络访问**会消耗大量电量。您可以通过遵循以下准则来最大限度地减少网络流量的影响
**GPS** 和**加速度计**硬件也会消耗电量。如果您通过 GPS 访问位置数据,请在获得所需数据后停止收集。如果您使用加速度计,请尝试将其限制在场景中,在这些场景中,加速度计对于所需功能至关重要。
**磁盘访问**(将文件读写到设备的本地磁盘)应与网络访问类似地处理。最好向磁盘传输/从磁盘传输较大的数据包,而不是将其分散到多个较小的交易中。
如果您需要将多个显示对象的特定属性设置为相同的值或对其进行过渡/补间(例如,将整个叠加菜单淡化
如果您使用的是精灵动画,一个常见的疏忽是允许屏幕外或不可见的精灵继续动画。虽然用户可能看不到这些精灵,但它们在动画过程中会继续使用处理器能力。我们建议您暂停所有移出屏幕或变得不活动的动画。
纹理内存经常被忽略,直到它达到“临界质量”,此时
作为一般实践,请记住以下关于管理纹理内存的技巧
当不再需要纹理时,始终卸载它们(将它们从显示层次结构中移除)。
如果您使用的是图像表,请考虑使用 TexturePacker 之类的工具将图像打包成尽可能小的配置。
设备支持的最大纹理大小有限制。如果超过此限制,纹理会自动缩小以适应最大值。您可以使用`system.getInfo( "maxTextureSize" )`
在设备上,当您能够最大限度地减少状态更改时,OpenGL 的性能最佳。这是因为如果连续显示对象之间不需要状态更改,则可以将多个对象批处理到单个绘制调用中。
Solar2D 的渲染引擎会尝试识别可以在单个绘制调用中提交多个显示对象的情况。只要有可能,您就应该尝试排列显示对象层次结构,以便可以将**连续**显示对象(即它们的渲染顺序)批处理到单个绘制调用中。
在某些情况下可能会发生这种情况。一般规则是,可以使用相同纹理的连续显示对象可以进行批处理。这包括使用来自同一图像表的不同帧的显示对象,因为底层纹理是相同的。在这些情况下,您可以更改每个对象的位置、色调和 alpha,而不会破坏批处理,但请记住,某些操作可能会阻止批处理,例如向对象添加着色器效果。
在代码级别,您应尽可能多地遵循 Lua 优化。下面的大多数性能技巧主要适用于
与应尽可能避免的全局变量相比,访问**局部**变量和函数的速度更快,尤其是在
-- Local (recommended) local CCX = display.contentCenterX -- Local variable for i = 1,100 do local image = display.newImage( "myImage.png" ) image.x = CCX end
-- Non-local (discouraged) CCX = display.contentCenterX -- Global variable for i = 1,100 do local image = display.newImage( "myImage.png" ) image.x = CCX end
这也适用于核心 Lua 库,例如 `math` 库。在
-- Local (recommended) local sin = math.sin -- Local reference to "math.sin" local function foo(x) for i = 1,100 do x = x + sin(i) end return x end
-- Non-local (discouraged) local function foo( x ) for i = 1,100 do x = x + math.sin(i) end return x end
最后,请记住,应尽可能地本地化函数。当然,这需要适当的作用域!
-- Local (recommended) local function func2( y ) -- "func2()" properly scoped above "func1()" print( y ) end local function func1() func2( "myValue" ) end func1()
-- Non-local (discouraged) function func1() func2( "myValue" ) end function func2( y ) print( y ) end func1()
让我们比较四种都能实现相同目标的方法:将值插入表中的常见操作。在这四种方法中,Lua `table.insert()` 函数的性能平平,应避免使用。
-- Loop index method (recommended) local a = {} for i = 1,100 do a[i] = i end
-- Counter method (recommended) local a = {} local index = 1 for i = 1,100 do a[index] = i index = index+1 end
-- Table size method (acceptable) local a = {} for i = 1,100 do a[#a+1] = i end
-- "table.insert()" (discouraged) local a = {} for i = 1,100 do table.insert( a, i ) end
Lua `unpack()` 函数的性能不是很好。幸运的是,可以编写一个简单的循环来完成同样的事情
-- Loop method (recommended) local a = { 100, 200, 300, 400 } for i = 1,100 do print( a[1],a[2],a[3],a[4] ) end
-- "unpack()" (discouraged) local a = { 100, 200, 300, 400 } for i = 1,100 do print( unpack(a) ) end
需要注意的是,您必须知道表的长度才能在循环方法中检索其所有值。因此,`unpack()` 仍然有用(例如,在长度未知的表中),
迭代表时,Lua `ipairs()` 函数的开销并不能证明其使用是合理的,尤其是在可以使用 Lua 构造完成同样的事情时。
-- Lua construct (recommended) local t1 = {} local t2 = {} local t3 = {} local t4 = {} local a = { t1, t2, t3, t4 } for i = 1,#a do print( a[i] ) end
-- "ipairs()" (discouraged) local t1 = {} local t2 = {} local t3 = {} local t4 = {} local a = { t1, t2, t3, t4 } for i,v in ipairs( a ) do print( i,v ) end
某些数学函数和过程比其他函数和过程更快。例如,乘法比除法快,您通常应该乘以小数而不是除法。
-- Multiplication by decimal (recommended) x * 0.5 y * 0.125
-- Division (acceptable) x / 2 y / 8
乘法也比求幂快
-- Multiplication (recommended) x * x * x
-- Exponentiation (acceptable) x^3
最后,对于正数,避免使用 `math.fmod()`,而应使用模运算符
-- Modulus operator (recommended) for i = 1,100 do if ( ( i%30 ) < 1 ) then local x = 1 end end
-- "math.fmod()" (discouraged) local fmod = math.fmod for i = 1,100 do if ( fmod( i,30 ) < 1 ) then local x = 1 end end
使用音频时,在大多数情况下,您应该将声音压缩/采样到可接受的最小质量。此外,使用简单的、
应用程序的音效几乎应始终预加载在
如果需要,可以将音效组织在表中,如下所示,以便于参考和最终处理。
local soundTable = { mySound1 = audio.loadSound( "a.wav" ), mySound2 = audio.loadSound( "b.wav" ), mySound3 = audio.loadSound( "c.wav" ), mySound4 = audio.loadSound( "d.wav" ), mySound5 = audio.loadSound( "e.wav" ), mySound6 = audio.loadSound( "f.wav" ), mySound7 = audio.loadSound( "g.wav" ), mySound8 = audio.loadSound( "h.wav" ), }
使用这种结构,播放就像
local mySound = audio.play( soundTable["mySound1"] )
一样简单,请记住,当不再需要音频文件时,您**必须**处理它们**并**清除对它们的任何引用。假设使用上述表结构来组织和预加载声音,则以下循环将处理音频句柄
for s = #soundTable,1,-1 do audio.dispose( soundTable[s] ) ; soundTable[s] = nil end