對於內存的優化,網上有不少例子和教程。整體來講,就那麼幾種解決方案,在最後我會簡單提下,這裏先說下在quick中,對於圖片的處理。html
對於quick框架的瞭解,咱們能夠參考\docs\文件夾裏面的文件,有相關api。學會學習的第一步,就是學會看api。好了,廢話很少說,下面是和內存相關的地方。android
可是在這裏我不說具體再項目中怎麼使用了,相信各位大神們一看就明白,有錯誤的地方,更好的,請大神們分享一下。ios
在項目的config.lua中有些調試信息的設置,這裏簡單說下。api
在初始化框架以前,能夠定義如下常量:數組
DEBUG: 設置框架的調試輸出級別緩存
DEBUG = 0 -- 不輸出任何調試信息(默認值) DEBUG = 1 -- 輸出基本的調試信息 DEBUG = 2 -- 輸出詳細的調試信息
DEBUG_FPS: 設置是否在畫面中顯示渲染幀率等信息框架
DEBUG_FPS = false -- 不顯示(默認值) DEBUG_FPS = true -- 顯示
DEBUG_MEM: 設置是否輸出內存佔用信息異步
DEBUG_MEM = false -- 不輸出(默認值) DEBUG_MEM = true -- 每 10 秒輸出一次
LOAD_DEPRECATED_API: 是否載入過期的 API 定義,默認爲 falseasync
DISABLE_DEPRECATED_WARNING: 使用過期的 API 時是否顯示警告信息,默認爲 true學習
USE_DEPRECATED_EVENT_ARGUMENTS: 是否使用過期的 Node 事件參數格式,默認爲 false
上面標紅的就是咱們要用的,能夠在調試信息中看到內存的使用狀況。
Scene的自動清理更能,實現原理是exit的時候,遍歷autoCleanupImages_數組,而後調用
display.removeSpriteFrameByImageName(imageName)進行釋放,咱們能夠把須要在場景切換時釋放掉的圖片,經過
Scene:markAutoCleanupImage放到scene的autoCleanupImages_中。詳細代碼以下:
local c = cc local Scene = c.Scene function Scene:setAutoCleanupEnabled() self:addNodeEventListener(c.NODE_EVENT, function(event) if event.name == "exit" then if self.autoCleanupImages_ then for imageName, v in pairs(self.autoCleanupImages_) do display.removeSpriteFrameByImageName(imageName) end self.autoCleanupImages_ = nil end end end) end function Scene:markAutoCleanupImage(imageName) if not self.autoCleanupImages_ then self.autoCleanupImages_ = {} end self.autoCleanupImages_[imageName] = true return self end
對於圖片的批處理、批量加載、合成大圖加載、下降圖片的質量等,在display中,有方法的封裝和介紹。
-- start -- -------------------------------- -- 將指定的 Sprite Sheets 材質文件及其數據文件載入圖像幀緩存。 -- @function [parent=#display] addSpriteFrames -- @param string plistFilename 數據文件名 -- @param string image 材質文件名 -- @see Sprite Sheets --[[-- 將指定的 Sprite Sheets 材質文件及其數據文件載入圖像幀緩存。 格式: display.addSpriteFrames(數據文件名, 材質文件名) ~~~ lua -- 同步加載紋理 display.addSpriteFrames("Sprites.plist", "Sprites.png") -- 異步加載紋理 local cb = function(plist, image) -- do something end display.addSpriteFrames("Sprites.plist", "Sprites.png", cb) ~~~ Sprite Sheets 通俗一點解釋就是包含多張圖片的集合。Sprite Sheets 材質文件由多張圖片組成,而數據文件則記錄了圖片在材質文件中的位置等信息。 ]] -- end -- function display.addSpriteFrames(plistFilename, image, handler) local async = type(handler) == "function" local asyncHandler = nil if async then asyncHandler = function() local texture = sharedTextureCache:getTextureForKey(image) assert(texture, string.format("The texture %s, %s is unavailable.", plistFilename, image)) sharedSpriteFrameCache:addSpriteFrames(plistFilename, texture) handler(plistFilename, image) end end if display.TEXTURES_PIXEL_FORMAT[image] then cc.Texture2D:setDefaultAlphaPixelFormat(display.TEXTURES_PIXEL_FORMAT[image]) if async then sharedTextureCache:addImageAsync(image, asyncHandler) else sharedSpriteFrameCache:addSpriteFrames(plistFilename, image) end cc.Texture2D:setDefaultAlphaPixelFormat(cc.TEXTURE2D_PIXEL_FORMAT_RGBA8888) else if async then sharedTextureCache:addImageAsync(image, asyncHandler) else sharedSpriteFrameCache:addSpriteFrames(plistFilename, image) end end end -- start -- -------------------------------- -- 從內存中卸載 Sprite Sheets 材質和數據文件 -- @function [parent=#display] removeSpriteFramesWithFile -- @param string plistFilename 數據文件名 -- @param string image 材質文件名 -- end -- function display.removeSpriteFramesWithFile(plistFilename, imageName) sharedSpriteFrameCache:removeSpriteFramesFromFile(plistFilename) if imageName then display.removeSpriteFrameByImageName(imageName) end end -- start -- -------------------------------- -- 設置材質格式。 -- @function [parent=#display] setTexturePixelFormat -- @param string filename 材質文件名 -- @param integer format 材質格式 -- @see Texture Pixel Format --[[-- 設置材質格式。 爲了節約內存,咱們會使用一些顏色品質較低的材質格式,例如針對背景圖使用 cc.TEXTURE2D_PIXEL_FORMAT_RGB565 格式。 display.setTexturePixelFormat() 能夠指定材質文件的材質格式,這樣在加載材質文件時就會使用指定的格式。 ]] -- end -- function display.setTexturePixelFormat(filename, format) display.TEXTURES_PIXEL_FORMAT[filename] = format end -- start -- -------------------------------- -- 從圖像幀緩存中刪除一個圖像。 -- @function [parent=#display] removeSpriteFrameByImageName -- @param string imageName 圖像文件名 --[[-- 從圖像幀緩存中刪除一個圖像。 有時候,某些圖像僅在特定場景中使用,例如背景圖。那麼在場景退出時,就能夠用 display.removeSpriteFrameByImageName() 從緩存裏刪除再也不使用的圖像數據。 此外,Scene 提供了 markAutoCleanupImage() 接口,能夠指定場景退出時須要自動清理的圖像,推薦使用。 ]] -- end -- function display.removeSpriteFrameByImageName(imageName) sharedSpriteFrameCache:removeSpriteFrameByName(imageName) cc.Director:getInstance():getTextureCache():removeTextureForKey(imageName) end -- start -- -------------------------------- -- 從指定的圖像文件建立並返回一個批量渲染對象。 -- @function [parent=#display] newBatchNode -- @param string image 圖像文件名 -- @param integer capacity -- @return SpriteBatchNode#SpriteBatchNode ret (return value: cc.SpriteBatchNode) -- @see Batch Node --[[-- 從指定的圖像文件建立並返回一個批量渲染對象。 ~~~ lua local imageName = "Sprites.png" display.addSpriteFrames("Sprites.plist", imageName) -- 載入圖像到幀緩存 -- 下面的代碼繪製 100 個圖像只用了 1 次 OpenGL draw call local batch = display.newBatchNode(imageName) for i = 1, 100 do local sprite = display.newSprite("#Sprite0001.png") batch:addChild(sprite) end -- 下面的代碼繪製 100 個圖像則要使用 100 次 OpenGL draw call local group = display.newNode() for i = 1, 100 do local sprite = display.newSprite("#Sprite0001.png") group:addChild(sprite) end ~~~ ]] -- end -- function display.newBatchNode(image, capacity) return cc.SpriteBatchNode:create(image, capacity or 100) end -- start -- -------------------------------- -- 建立並返回一個圖像幀對象。 -- @function [parent=#display] newSpriteFrame -- @param string 圖像幀名稱 -- @return SpriteFrameCache#SpriteFrameCache ret (return value: cc.SpriteFrameCache) --[[-- 建立並返回一個圖像幀對象。 ~~~ lua display.addSpriteFrames("Sprites.plist", "Sprites.png") -- 建立一個 Sprite local sprite = display.newSprite("#Yes.png") -- 建立一個圖像幀 local frameNo = display.newSpriteFrame("No.png") -- 在須要時,修改 Sprite 的顯示內容 sprite:setSpriteFrame(frameNo) ~~~ ]] -- end -- function display.newSpriteFrame(frameName) local frame = sharedSpriteFrameCache:getSpriteFrame(frameName) if not frame then printError("display.newSpriteFrame() - invalid frameName %s", tostring(frameName)) end return frame end -- start -- -------------------------------- -- 以特定模式建立一個包含多個圖像幀對象的數組。 -- @function [parent=#display] newFrames -- @param string pattern 模式字符串 -- @param integer begin 起始索引 -- @param integer length 長度 -- @param boolean isReversed 是不是遞減索引 -- @return table#table ret (return value: table) 圖像幀數組 --[[-- 以特定模式建立一個包含多個圖像幀對象的數組。 ~~~ lua -- 建立一個數組,包含 Walk0001.png 到 Walk0008.png 的 8 個圖像幀對象 local frames = display.newFrames("Walk%04d.png", 1, 8) -- 建立一個數組,包含 Walk0008.png 到 Walk0001.png 的 8 個圖像幀對象 local frames = display.newFrames("Walk%04d.png", 1, 8, true) ~~~ ]] -- end -- function display.newFrames(pattern, begin, length, isReversed) local frames = {} local step = 1 local last = begin + length - 1 if isReversed then last, begin = begin, last step = -1 end for index = begin, last, step do local frameName = string.format(pattern, index) local frame = sharedSpriteFrameCache:getSpriteFrame(frameName) if not frame then printError("display.newFrames() - invalid frame, name %s", tostring(frameName)) return end frames[#frames + 1] = frame end return frames end -- start -- -------------------------------- -- 以包含圖像幀的數組建立一個動畫對象。 -- @function [parent=#display] newAnimation -- @param table frames 圖像幀的數組 -- @param number time 每一楨動畫之間的間隔時間 -- @return Animation#Animation ret (return value: cc.Animation) Animation對象 --[[-- 以包含圖像幀的數組建立一個動畫對象。 ~~~ lua local frames = display.newFrames("Walk%04d.png", 1, 8) local animation = display.newAnimation(frames, 0.5 / 8) -- 0.5 秒播放 8 楨 sprite:playAnimationOnce(animation) -- 播放一次動畫 ~~~ ]] -- end -- function display.newAnimation(frames, time) local count = #frames -- local array = Array:create() -- for i = 1, count do -- array:addObject(frames[i]) -- end time = time or 1.0 / count return cc.Animation:createWithSpriteFrames(frames, time) end -- start -- -------------------------------- -- 以指定名字緩存建立好的動畫對象,以便後續反覆使用。 -- @function [parent=#display] setAnimationCache -- @param string name 名字 -- @param Animation animation 動畫對象 --[[-- 以指定名字緩存建立好的動畫對象,以便後續反覆使用。 ~~~ lua local frames = display.newFrames("Walk%04d.png", 1, 8) local animation = display.newAnimation(frames, 0.5 / 8) -- 0.5 秒播放 8 楨 display.setAnimationCache("Walk", animation) -- 在須要使用 Walk 動畫的地方 sprite:playAnimationOnce(display.getAnimationCache("Walk")) -- 播放一次動畫 ~~~ ]] -- end -- function display.setAnimationCache(name, animation) sharedAnimationCache:addAnimation(animation, name) end -- start -- -------------------------------- -- 取得以指定名字緩存的動畫對象,若是不存在則返回 nil。 -- @function [parent=#display] getAnimationCache -- @param string name -- @return Animation#Animation ret (return value: cc.Animation) -- end -- function display.getAnimationCache(name) return sharedAnimationCache:getAnimation(name) end -- start -- -------------------------------- -- 刪除指定名字緩存的動畫對象。 -- @function [parent=#display] removeAnimationCache -- @param string name -- end -- function display.removeAnimationCache(name) sharedAnimationCache:removeAnimation(name) end -- start -- -------------------------------- -- 從內存中卸載沒有使用 Sprite Sheets 材質 -- @function [parent=#display] removeUnusedSpriteFrames -- end -- function display.removeUnusedSpriteFrames() sharedSpriteFrameCache:removeUnusedSpriteFrames() sharedTextureCache:removeUnusedTextures() end -- start -- -------------------------------- -- 建立一個進度條的節點 -- @function [parent=#display] newProgressTimer -- @param mixed image -- @param number progressType --[[-- 建立一個進度條的節點 進度條類型有: - display.PROGRESS_TIMER_BAR - display.PROGRESS_TIMER_RADIAL 環形 ]] -- end --
對於整個項目的內存優化,咱們能夠在下面幾個方面。cocos2dx一直在更新和優化,因此,不要僅侷限於下面的解決方案,只是其中的一部分,
也許有的版本已經再也不適應,請根據你所使用的版本和項目的具體需求,作出優化方案。
(1)紋理優化
上面咱們講到了quick中的優化,下面還有其餘幾個解決辦法
爲了優化紋理內存的使用,咱們必須知道什麼因素影響了內存的使用狀況。
有三個因素影響了紋理的內存使用。紋理格式(壓縮的仍是非壓縮的),顏色,大小。
咱們可使用PVR格式的紋理來減小內存使用。最被建議的紋理格式是pvr.ccz,每色的bit值越高,畫面質量就約好。可是也會消費不少內存。
那麼咱們使用顏色深度是RGBA4444的紋理來代替RBGA8888,這將會消費一半內存。
咱們也會發現大紋理也會致使內存相關的問題。那麼你最好使用適度的大小。
推薦個圖片壓縮的網站:
https://tinypng.com/
(2)音頻
有三個因素影響文件內存使用。是音頻文件格式,比特率,和樣本率
咱們最但願音頻文件時mp3格式。由於它被android和ios都支持。而且它也被壓縮而且硬件加速了。
你應該保證你的背景音樂文件大小在800KB一下。最簡單的方式就是減小背景音樂播放時間而且重複調用。
你應該保持你的音頻文件樣本率在96-128kbps之間,而且比特率在44kHz就足夠了。
(3)字體和粒子系統優化
這裏咱們有兩個建議:當使用BM字體顯示遊戲分數,在你的圖片文件中選擇最小的數字字符,例如:
若是你想只顯示數字,你能夠移除全部的字符。
粒子系統中,咱們能夠減小粒子數量來減小內存使用。
(4)語言代碼
無內存泄露的代碼。
lua 注意全局變量的使用 ,局部變量不要忘記 local
最後一些建議:
一、一幀幀的加載遊戲資源。
二、減小繪製調用
三、按照最大到最小的順序的加載紋理
四、避開內存使用高峯、
五、使用加載界面來預加載遊戲資源。
六、當不須要的時候釋放無用的資源
七、當有內存警告的時候釋放緩存的資源
八、使用texturePacker來優化紋理尺寸,格式,色彩深度值等等。
九、當心使用JPG文件
十、使用16位RBGA4444色彩深度的紋理(看具體的機型,Android和IOS有點卻別)
十一、使用NPOT紋理代替POT紋理
十二、避開加載大尺寸圖片
1三、使用1024*1024 NPOT pvr.ccz紋理圖集而不是原生圖片
有些的不對的地方和須要完善的地方,但願大神指教。