quick cocos2dx lua 內存釋放


前言

  對於內存的優化,網上有很多例子和教程。總體來說,就那么幾種解決方案,在最后我會簡單提下,這里先說下在quick中,對於圖片的處理。

1.查看內存調試信息

  對於quick框架的了解,我們可以參考\docs\文件夾里面的文件,有相關api。學會學習的第一步,就是學會看api。好了,廢話不多說,下面是和內存相關的地方。

但是在這里我不說具體再項目中怎么使用了,相信各位大神們一看就明白,有錯誤的地方,更好的,請大神們分享一下。

在項目的config.lua中有些調試信息的設置,這里簡單說下。

在初始化框架之前,可以定義以下常量:

  • 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 定義,默認為 false

  • DISABLE_DEPRECATED_WARNING: 使用過時的 API 時是否顯示警告信息,默認為 true

  • USE_DEPRECATED_EVENT_ARGUMENTS: 是否使用過時的 Node 事件參數格式,默認為 false

上面標紅的就是我們要用的,可以在調試信息中看到內存的使用情況。

2.SceneEx.lua

  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

3.display.lua

  對於圖片的批處理、批量加載、合成大圖加載、降低圖片的質量等,在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 --

3.項目內存優化

  對於整個項目的內存優化,我們可以在下面幾個方面。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

最后一些建議:

1、一幀幀的加載游戲資源。

2、減少繪制調用

3、按照最大到最小的順序的加載紋理

4、避開內存使用高峰、

5、使用加載界面來預加載游戲資源。

6、當不需要的時候釋放無用的資源

7、當有內存警告的時候釋放緩存的資源

8、使用texturePacker來優化紋理尺寸,格式,色彩深度值等等。

9、小心使用JPG文件

10、使用16位RBGA4444色彩深度的紋理(看具體的機型,Android和IOS有點卻別)

11、使用NPOT紋理代替POT紋理

12、避開加載大尺寸圖片

13、使用1024*1024 NPOT pvr.ccz紋理圖集而不是原生圖片

有些的不對的地方和需要完善的地方,希望大神指教。

本文地址:http://www.cnblogs.com/zhangfeitao/p/4562791.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM