1.WorkSpace
實(shí)現(xiàn)cocos2dx項(xiàng)目和unity項(xiàng)目互相調(diào)用的方法就是依賴WorkSpace。
當(dāng)一個(gè) target 被多個(gè)不同的項(xiàng)目依賴,或者 project 之間互相引用,那么我們就需要把這些 projects 放到相同的層級上來。管理相同層級 projects 的容器就是 Workspace。
workspace 是純粹的容器,不參與任何編譯鏈接過程,它主要管理:
1.Xcode 中的 projects,記錄它們在 Finder 中的引用位置。
2.一些用戶界面的自定義信息(窗口的位置,順序,偏好等等)。

這樣cocos的工程和unity的工程耦合度非常低。
unity工程中導(dǎo)出為ios項(xiàng)目時(shí)需要選擇導(dǎo)出為framework庫,方便我們的工程引用。
需要把data資源也指向UnityFramework,這樣所有內(nèi)容都封裝到單個(gè)框架中了。
最后需要把UnityFramework改為public的模式,這樣就可以調(diào)用unity中的代碼了。
(http://m.itdecent.cn/p/830cb9455c3b)可以參考這個(gè)文章,講的很詳細(xì)。
2.窗口
-
unity和cocos2dx界面的相互切換實(shí)際上就是window在互相切換。
-
從cocos2dx返回到unity使用的方法是
先暫停cocos項(xiàng)目
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();
這個(gè)cocos的方法會發(fā)送一個(gè)事件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)用另一個(gè)窗口時(shí),需要保持原窗口中的內(nèi)存占用量最小。

常態(tài)下打開大廳的內(nèi)存一般是181mb。
卸載內(nèi)存的兩個(gè)思路:第一個(gè)是卸載資源,第二個(gè)是卸載lua代碼。
卸載lua內(nèi)存依靠的是lua的自動回收機(jī)制,就是在mian函數(shù)里的那三行代碼
--執(zhí)行一個(gè)完整的垃圾收集循環(huán)
collectgarbage("collect")
--setpause為100代表,垃圾收集不會停止
collectgarbage("setpause", 100)
--setsetpmul為5000代表是內(nèi)存分配速度的50倍
collectgarbage("setstepmul", 5000)
既然已經(jīng)有了自動回收機(jī)制我們能做的事情也就不多了,至于去刪除全局變量的東西,一是沒必要,二是很危險(xiǎn)。
卸載資源的方法是
display.removeUnusedSpriteFrames()
這個(gè)方法中,cocos幫我們卸載了不用的資源占用。
function display.removeUnusedSpriteFrames()
spriteFrameCache:removeUnusedSpriteFrames()
textureCache:removeUnusedTextures()
end
所以,解決方案就是:在需要喚起unity時(shí)先跳轉(zhuǎn)到一個(gè)新的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,然后就能獲得一個(gè)比較理想的內(nèi)存:

但是到這里就結(jié)束了嗎?
我測試了游戲界面直接進(jìn)入到預(yù)跳轉(zhuǎn)界面的內(nèi)存是43mb左右,還有10mb的內(nèi)存不對勁!
那么這部分是什么呢?
我在網(wǎng)上搜尋了一些答案,發(fā)現(xiàn)有可能是spine動畫的泄露問題造成的!
于是一路查找spine動畫的問題,發(fā)現(xiàn)我們項(xiàng)目中的spine動畫都是使用
sp.SkeletonAnimation:create方法生成一個(gè)新的節(jié)點(diǎn)。
而這個(gè)方法的底層中會給每一個(gè)動畫都創(chuàng)建一個(gè)新的SkeletonRenderer類,這個(gè)類會去讀取一份新的動畫紋理資源,并持有,這就導(dǎo)致了同一個(gè)動畫占用資源越來越多的情況!
我在cocos社區(qū)搜尋了一下答案,提出問題的很多,但是都沒有解決方案。
直到看到了這個(gè)帖子:
https://www.cnblogs.com/chevin/p/5667768.html
原理也很簡單,就是創(chuàng)建一個(gè)map,在創(chuàng)建紋理時(shí)存儲到該map中,然后再新建改動畫時(shí),直接使用此紋理,就不用創(chuàng)建新紋理了。
正當(dāng)我準(zhǔn)備把這些代碼接入到我們工程中時(shí),我發(fā)現(xiàn)已經(jīng)有前人接入過了,當(dāng)然也有可能是官方加的……
新的方法就是
sp.SkeletonAnimation:createWithFileAndAddCache("spine name", "json file", "atlas file", scale)
當(dāng)然,他還提供了一系列其他的方法:
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)存,我會在返回大廳時(shí)刪除所有的紋理緩存,避免游戲中忘記管理自己的內(nèi)存。
ok。回到正題,那么這最后10mb內(nèi)存是由于spine導(dǎo)致的嗎?
答案是,no。
使用create方法創(chuàng)建的動畫雖然會每次都占一點(diǎn)內(nèi)存,但是他們在節(jié)點(diǎn)刪除時(shí)也會自動卸載內(nèi)存,所以方向并不對。
內(nèi)存包含所有Malloc的部分,比如聲音資源,全局產(chǎn)量,數(shù)組等,如果把這些都想卸載干凈,下一次在回到大廳的時(shí)候可能會出現(xiàn)問題,而且需要重新加載,所以綜合考慮下就忽略了。
那么,到這里就結(jié)束了嗎?
最終測試的時(shí)候還遇到了一個(gè)問題:
unity返回到cocos窗口中時(shí),調(diào)用的applicationWillEnterForeground方法,分發(fā)event_will_enter_foreground事件無效!
在lua代碼中監(jiān)聽此事件會返回到大廳里,否則就一直停留在過渡場景了。
那么這個(gè)問題是什么呢……
最后問題又回到了cocos的場景切換流程。
在切換場景時(shí),會關(guān)閉所有的事件分發(fā),直到場景加載完成了,才會重新啟用事件分發(fā)。
so……
我決定不跳轉(zhuǎn)場景了,直接刪除該場景下所有的節(jié)點(diǎn),刪除無用資源,加載跳轉(zhuǎn)界面。
完結(jié)……撒花……