1.WorkSpace
實現(xiàn)cocos2dx項目和unity項目互相調(diào)用的方法就是依賴WorkSpace。
當一個 target 被多個不同的項目依賴班套,或者 project 之間互相引用晰骑,那么我們就需要把這些 projects 放到相同的層級上來泣洞。管理相同層級 projects 的容器就是 Workspace席舍。
workspace 是純粹的容器勒魔,不參與任何編譯鏈接過程践图,它主要管理:
1.Xcode 中的 projects掺冠,記錄它們在 Finder 中的引用位置。
2.一些用戶界面的自定義信息(窗口的位置码党,順序德崭,偏好等等)。
這樣cocos的工程和unity的工程耦合度非常低接癌。
unity工程中導出為ios項目時需要選擇導出為framework庫,方便我們的工程引用扣讼。
需要把data資源也指向UnityFramework缺猛,這樣所有內(nèi)容都封裝到單個框架中了。
最后需要把UnityFramework改為public的模式椭符,這樣就可以調(diào)用unity中的代碼了荔燎。
(http://www.reibang.com/p/830cb9455c3b)可以參考這個文章,講的很詳細销钝。
2.窗口
-
unity和cocos2dx界面的相互切換實際上就是window在互相切換有咨。
-
從cocos2dx返回到unity使用的方法是
先暫停cocos項目
cocos2d::Application::getInstance()->applicationDidEnterBackground();
初始化unity的窗口
[[self ufw] runEmbeddedWithArgc: gArgc argv: gArgv appLaunchOpts: appLaunchOpts];
調(diào)用此方法就可以展示unityWindow界面
[[self ufw] showUnityWindow];
使用此方法可以傳遞參數(shù)到unity中(這里我們用來傳遞登錄參數(shù))。
[self sendMsgToUnityWithName:"CocosLauncher" functionName:"RecvCocosData" message:[data UTF8String]];
-
從unity返回到cocos2dx使用的方法是
卸載unity
unloadApplication
使主窗口顯示到屏幕的最前端蒸健。
[self.window makeKeyAndVisible];
然后調(diào)用
cocos2d::Application::getInstance()->applicationWillEnterForeground();
這個cocos的方法會發(fā)送一個事件event_will_enter_foreground
void AppDelegate::applicationWillEnterForeground() {
Director::getInstance()->startAnimation();
SimpleAudioEngine::getInstance()->setBackgroundMusicVolume(s_bgmVolume);
SimpleAudioEngine::getInstance()->setEffectsVolume(s_effectsVolume);
Director::getInstance()->getEventDispatcher()->dispatchCustomEvent("event_will_enter_foreground");
}
只需要在lua代碼中監(jiān)聽此事件座享,跳轉(zhuǎn)到LobbyScene.start()就回到了我們的大廳界面中婉商。
3.內(nèi)存優(yōu)化
使用了窗口切換的方式,我們在調(diào)用另一個窗口時渣叛,需要保持原窗口中的內(nèi)存占用量最小丈秩。
常態(tài)下打開大廳的內(nèi)存一般是181mb。
卸載內(nèi)存的兩個思路:第一個是卸載資源淳衙,第二個是卸載lua代碼蘑秽。
卸載lua內(nèi)存依靠的是lua的自動回收機制,就是在mian函數(shù)里的那三行代碼
--執(zhí)行一個完整的垃圾收集循環(huán)
collectgarbage("collect")
--setpause為100代表箫攀,垃圾收集不會停止
collectgarbage("setpause", 100)
--setsetpmul為5000代表是內(nèi)存分配速度的50倍
collectgarbage("setstepmul", 5000)
既然已經(jīng)有了自動回收機制我們能做的事情也就不多了肠牲,至于去刪除全局變量的東西,一是沒必要靴跛,二是很危險缀雳。
卸載資源的方法是
display.removeUnusedSpriteFrames()
這個方法中,cocos幫我們卸載了不用的資源占用梢睛。
function display.removeUnusedSpriteFrames()
spriteFrameCache:removeUnusedSpriteFrames()
textureCache:removeUnusedTextures()
end
所以肥印,解決方案就是:在需要喚起unity時先跳轉(zhuǎn)到一個新的scene上,卸載掉原先的資源內(nèi)存扬绪。于是代碼就順理成章的寫為:
//加載新的scene
local JumpScene = {}
local scene = display.newScene("JumpScene")
local node = BUI:loadCSB("Lobby/LobbyScene/CSB/JumpScene.csb", JumpScene)
scene:addChild(node)
display.runScene(scene, "FADE")
//卸載圖片資源
display.removeUnusedSpriteFrames()
由于runScene也就是replaceScene在跳轉(zhuǎn)到新的場景后會自動從內(nèi)存中刪除,再調(diào)用刪除無用的資源代碼裤唠,是不是就能獲得比較好的內(nèi)存結(jié)果了挤牛?
答案是no!
那么為什么呢种蘸?
這里得提到replaceScene切換場景墓赴,scene的生命周期
假設(shè)scene A是活動場景,現(xiàn)在我們用scene B來replaceScene替換A航瞭,A和B的生命周期是這樣的:
B ---- init();
A ---- onExit();
A ---- 析構(gòu)函數(shù)被調(diào)用
B ---- onEnter();
B ---- onEnterTransitionDidFinish();
答案就顯而易見了诫硕,A場景并不會立馬從內(nèi)存中刪除,所以調(diào)用removeUnusedSpriteFrames的效果并不好刊侯。
于是簡單的修改一下代碼章办,只需要加入
display.getRunningScene():removeAllChildren()
在去B場景前刪除A場景中的所有Node,然后就能獲得一個比較理想的內(nèi)存:
但是到這里就結(jié)束了嗎滨彻?
我測試了游戲界面直接進入到預(yù)跳轉(zhuǎn)界面的內(nèi)存是43mb左右藕届,還有10mb的內(nèi)存不對勁!
那么這部分是什么呢亭饵?
我在網(wǎng)上搜尋了一些答案休偶,發(fā)現(xiàn)有可能是spine動畫的泄露問題造成的!
于是一路查找spine動畫的問題辜羊,發(fā)現(xiàn)我們項目中的spine動畫都是使用
sp.SkeletonAnimation:create方法生成一個新的節(jié)點踏兜。
而這個方法的底層中會給每一個動畫都創(chuàng)建一個新的SkeletonRenderer類词顾,這個類會去讀取一份新的動畫紋理資源,并持有碱妆,這就導致了同一個動畫占用資源越來越多的情況肉盹!
我在cocos社區(qū)搜尋了一下答案,提出問題的很多山橄,但是都沒有解決方案垮媒。
直到看到了這個帖子:
https://www.cnblogs.com/chevin/p/5667768.html
原理也很簡單航棱,就是創(chuàng)建一個map,在創(chuàng)建紋理時存儲到該map中饮醇,然后再新建改動畫時,直接使用此紋理朴艰,就不用創(chuàng)建新紋理了观蓄。
正當我準備把這些代碼接入到我們工程中時,我發(fā)現(xiàn)已經(jīng)有前人接入過了侮穿,當然也有可能是官方加的……
新的方法就是
sp.SkeletonAnimation:createWithFileAndAddCache("spine name", "json file", "atlas file", scale)
當然毁嗦,他還提供了一系列其他的方法:
tolua_function(tolua_S,"setStartListener",lua_cocos2dx_spine_SkeletonAnimation_setStartListener);
tolua_function(tolua_S,"setTrackEventListener",lua_cocos2dx_spine_SkeletonAnimation_setTrackEventListener);
tolua_function(tolua_S,"setTrackCompleteListener",lua_cocos2dx_spine_SkeletonAnimation_setTrackCompleteListener);
tolua_function(tolua_S,"getSkeletonData",lua_cocos2dx_spine_SkeletonAnimation_getSkeletonData);
tolua_function(tolua_S,"setTrackStartListener",lua_cocos2dx_spine_SkeletonAnimation_setTrackStartListener);
tolua_function(tolua_S,"findAnimation",lua_cocos2dx_spine_SkeletonAnimation_findAnimation);
tolua_function(tolua_S,"setCompleteListener",lua_cocos2dx_spine_SkeletonAnimation_setCompleteListener);
tolua_function(tolua_S,"setTrackEndListener",lua_cocos2dx_spine_SkeletonAnimation_setTrackEndListener);
tolua_function(tolua_S,"setEventListener",lua_cocos2dx_spine_SkeletonAnimation_setEventListener);
tolua_function(tolua_S,"setMix",lua_cocos2dx_spine_SkeletonAnimation_setMix);
tolua_function(tolua_S,"setEndListener",lua_cocos2dx_spine_SkeletonAnimation_setEndListener);
tolua_function(tolua_S,"clearTracks",lua_cocos2dx_spine_SkeletonAnimation_clearTracks);
tolua_function(tolua_S,"clearTrack",lua_cocos2dx_spine_SkeletonAnimation_clearTrack);
tolua_function(tolua_S,"createWithBinaryFile", lua_cocos2dx_spine_SkeletonAnimation_createWithBinaryFile);
tolua_function(tolua_S,"readSkeletonDataToCache", lua_cocos2dx_spine_SkeletonAnimation_readSkeletonDataToCache);
tolua_function(tolua_S,"removeAllSkeletonData", lua_cocos2dx_spine_SkeletonAnimation_removeAllSkeletonData);
tolua_function(tolua_S,"create", lua_cocos2dx_spine_SkeletonAnimation_create);
tolua_function(tolua_S,"isExistSkeletonDataInCache", lua_cocos2dx_spine_SkeletonAnimation_isExistSkeletonDataInCache);
tolua_function(tolua_S,"createFromCache", lua_cocos2dx_spine_SkeletonAnimation_createFromCache);
tolua_function(tolua_S,"getSkeletonDataFromCache", lua_cocos2dx_spine_SkeletonAnimation_getSkeletonDataFromCache);
tolua_function(tolua_S,"createWithJsonFile", lua_cocos2dx_spine_SkeletonAnimation_createWithJsonFile);
tolua_function(tolua_S,"removeSkeletonData", lua_cocos2dx_spine_SkeletonAnimation_removeSkeletonData);
這里不展開贅述亲茅,唯一需要講的就是此方法需要自己管理內(nèi)存,我會在返回大廳時刪除所有的紋理緩存狗准,避免游戲中忘記管理自己的內(nèi)存克锣。
ok腔长∠睿回到正題捞附,那么這最后10mb內(nèi)存是由于spine導致的嗎?
答案是想鹰,no药版。
使用create方法創(chuàng)建的動畫雖然會每次都占一點內(nèi)存,但是他們在節(jié)點刪除時也會自動卸載內(nèi)存槽片,所以方向并不對肢础。
內(nèi)存包含所有Malloc的部分碌廓,比如聲音資源,全局產(chǎn)量慨蛙,數(shù)組等纪挎,如果把這些都想卸載干凈,下一次在回到大廳的時候可能會出現(xiàn)問題异袄,而且需要重新加載,所以綜合考慮下就忽略了封孙。
那么讽营,到這里就結(jié)束了嗎?
最終測試的時候還遇到了一個問題:
unity返回到cocos窗口中時橱鹏,調(diào)用的applicationWillEnterForeground方法,分發(fā)event_will_enter_foreground事件無效蚀瘸!
在lua代碼中監(jiān)聽此事件會返回到大廳里庶橱,否則就一直停留在過渡場景了贮勃。
那么這個問題是什么呢……
最后問題又回到了cocos的場景切換流程苏章。
在切換場景時,會關(guān)閉所有的事件分發(fā)枫绅,直到場景加載完成了,才會重新啟用事件分發(fā)寓搬。
so……
我決定不跳轉(zhuǎn)場景了县耽,直接刪除該場景下所有的節(jié)點镣典,刪除無用資源唾琼,加載跳轉(zhuǎn)界面。
完結(jié)……撒花……