日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

cocos2dx 大地图分块加载的研究(初)

發布時間:2025/7/14 编程问答 41 豆豆
生活随笔 收集整理的這篇文章主要介紹了 cocos2dx 大地图分块加载的研究(初) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

本文轉自http://blog.csdn.net/dinko321/article/details/46739563

?

項目里面需要加載一個很大的地圖,目測最少是4096x4096的分辨率。

?

? ? ? ? 先不考慮什么引擎最大支持多大的圖啊,大圖加載效率啊等等這些問題,光是4k x 4k的分辨率,ARGB8888,加載進去,就是64M的內存,這還只是一個背景。再來點其他七七八八的東西,輕松超過120,這個內存在某些設備上就已經很危險了。

?

? ? ? ? 為了實現這個目標,處理的方式大概有2種:

? ? ? ? 一、資源重用。也就是類似于tiled這種方法,把地圖上面的一些圖塊,反復使用,通過有限的紋理,來拼接出地圖。但是這種方法的確定是地圖會比較死板

? ? ? ? 二、分塊加載。就是把一個大圖切成若干小塊,每次只加載需要顯示的圖塊。看上去很美,現在主要說這個。

?

? ? ? ??先說切圖,肯定是切小方塊,按照二的冪的原則,一般有這么幾個備選:32、64、128、256、512。

? ? ? ? 用切的圖塊來鋪滿一個屏幕,不一定能恰好填滿,可能會多出一部分(少一部分就會留黑,肯定不行),從這個上面上來說,肯定是切的越小越好,因為這樣就算有浪費,最多也就浪費一個圖塊的寬度。但是圖切的太碎,會對渲染效率產生影響。從調試信息可以看到有個GLVerts,verts越多,顯示效率就越低。

? ? ? ? 但是如果切太大,內存又會有影響,比如我切個512的方塊,假設屏幕是960X640,那么極限情況下,最多會同時顯示4塊(請自行想象在田字格的中間放一個方框,方框就是屏幕),這樣就達不到節約內存的目的。

? ? ? ? 一般128或者256應該就差不多了。

? ? ? ? 然后把大圖切小,分別命名。這圖怎么切,當然是叫美工用PS切啊,命名,手動啊。。。當然這是開玩笑,你要真這么弄,美工不把你砍死。。。作為程序員,就是要會偷懶嘛,寫個程序就切了,python的。雖然我也不是很會python,只會基本語法。但是python庫多啊

?

[python]?view plaincopy
  • import?Image????
  • import?sys????
  • import?os.path????
  • from??datetime??import??*??????
  • import?random????
  • import?time????
  • ????
  • ??
  • IMAGE_PATH?=?"map.png"??
  • ??
  • xIndex?=?0??
  • yIndex?=?0??
  • cropSize?=?256??
  • xNum?=?0??
  • yNum?=?0??
  • ????
  • im?=?Image.open(IMAGE_PATH)?#打開圖片句柄????
  • pSize?=?im.size??
  • ??
  • xNum?=?pSize[0]/cropSize??
  • yNum?=?pSize[1]/cropSize??
  • ??
  • print?"size??"?,xNum,'??',yNum??
  • ??
  • for?yIndex?in?range(yNum):??
  • ????for?xIndex?in?range(xNum):??
  • ????????print?"pic?:?"?,?xIndex?,?"_"?,?yIndex??
  • ????????box?=?(xIndex*cropSize,yIndex*cropSize,(xIndex+1)*cropSize,(yIndex+1)*cropSize)?#設定裁剪區域????
  • ????????region?=?im.crop(box)??#裁剪圖片,并獲取句柄region???
  • ????????name?=?"/Users/apple/Desktop/result/map%s_%s.png"?%?(xIndex,yNum-1-yIndex)??
  • ????????region.save(name)?#保存圖片????
  • ????????#?xIndex?=?xIndex+1??
  • ????????#?yIndex?=?yIndex+1??
  • ????????print?int(time.time());????
  • ????
  • ? ? ? ? 這樣圖就切好了,名字也起好了,而且還不用和美工撕逼或者裝孫子。。。

    ?

    ?

    ? ? ? ??再說鋪磚,也就是把圖塊放在地圖上的方法。

    ? ? ? ? 這里會有2個需要區別的東西,一個是坐標,就是offset的那個坐標。一個是index,就是圖塊的index。因為按照上面的圖塊命名,坐標是map0_0 ,map0_1這樣的規則。

    ? ? ? ? 大概思路應該是這樣的,首先,獲取winSize,計算橫豎2個方向需要多少圖塊才能鋪滿,向上取整。

    ?

    [cpp]?view plaincopy
  • xTileNum?=?winSize.width/tileSize;??
  • yTileNum?=?winSize.height/tileSize;??
  • xTileNum+=1;??
  • yTileNum+=1;??
  • ? ? ? ? 然后,根據scrollView的offset,取得左下角那個圖塊的index,知道這一點應該用那一塊來鋪。

    ?

    ?

    [cpp]?view plaincopy
  • Vec2?offset?=?view->getContentOffset();??
  • ??
  • offset.x?=?fabsf(offset.x);??
  • offset.y?=?fabsf(offset.y);??
  • ??
  • int?xStartIdx?=?offset.x/tileSize;??
  • int?yStartIdx?=?offset.y/tileSize;??
  • ? ? ? ? 然后,在橫方向,和豎方向上,鋪滿

    ?

    ?

    [cpp]?view plaincopy
  • for?(int?i?=?0?;?i<xTileNum?;?i++)??
  • {??
  • ????for?(int?j=0;?j<yTileNum;?j++)??
  • ????{??
  • ????????int?xIdx?=?xStartIdx+i;??
  • ????????int?yIdx?=?yStartIdx+j;??
  • ??????????
  • ????????char?name[128];??
  • ????????sprintf(name,?"result/map%d_%d.png",xIdx,yIdx);??
  • ????????Sprite*?tile?=?Sprite::create(name);??
  • ????????tile->ignoreAnchorPointForPosition(true);??
  • ????????int?posX?=?tileSize*xIdx;??
  • ????????int?posY?=?tileSize*yIdx;??
  • ????????tile->setPosition(posX,posY);??
  • ????????contentLayer->addChild(tile);??
  • ????}??
  • }??
  • ? ? ? ? 這樣,就鋪滿一屏幕了。

    ?

    ? ? ? ? 然后來說說滾動的時候的處理方法。

    上圖很清楚,要補充的是綠色區域,要去掉的是左上的那2條,中間的是不用動的。這個地方能想出一萬種算法來做。我用的是一種簡單直接的,但是絕對不是效率最高的。因為最后你會發現這點效率完全沒有什么卵用。。。。 首先需要這么幾個東西 [cpp]?view plaincopy
  • #include?<algorithm>??
  • #include?<unordered_map>??
  • #include?<set>??
  • ??
  • using?namespace?std;??
  • ??????
  • unordered_map<string,?Sprite*>?curTiles;??
  • set<string>?curKeys;??
  • set<string>?newKeys;??
  • ? ? ? ? 其實想法和簡單,就是把每次加入的圖塊sprite,放入一個map,然后把移動過后,需要加入的圖塊,是全部的,不是新增的那一部分,放入一個map,然后對2個map取差集,就可以得出需要添加的部分,和需要刪除的部分。 但是為毛我只寫了一個map確用了2個set。。。因為我沒試過map能不能用stl的set_difference這個函數。。。大概就是這樣 [cpp]?view plaincopy
  • //?統計需要顯示的圖塊??
  • newKeys.clear();??
  • for?(int?i?=?0?;?i<xTileNum?;?i++)??
  • {??
  • ????for?(int?j=0;?j<yTileNum;?j++)??
  • ????{??
  • ????????int?xIdx?=?xStartIdx+i;??
  • ????????int?yIdx?=?yStartIdx+j;??
  • ??????????
  • ????????if?(xIdx>15?||?yIdx>15)??
  • ????????{??
  • ????????????continue;??
  • ????????}??
  • ??????????
  • ????????char?name[128];??
  • ????????sprintf(name,?"result/map%d_%d.png",xIdx,yIdx);??
  • ??????????
  • ????????string?key?=?string(name);??
  • ????????newKeys.insert(key);??
  • ????}??
  • }??
  • ??
  • set<string>?results;??
  • //在舊集合中去掉新的集合的元素,得到該移除的部分??
  • set_difference(curKeys.begin(),?curKeys.end(),?newKeys.begin(),?newKeys.end(),?inserter(results,?results.begin()));??
  • for?(auto?it=results.begin();?it!=results.end();?it++)??
  • {??
  • ????curTiles[*it]->removeFromParent();??
  • ????curTiles.erase(*it);??
  • }??
  • ??
  • //在新集合中去掉舊的集合的元素,得到該添加的部分??
  • results.clear();??
  • set_difference(newKeys.begin(),?newKeys.end(),?curKeys.begin(),?curKeys.end(),?inserter(results,?results.begin()));??
  • for?(auto?it=results.begin();?it!=results.end();?it++)??
  • {??
  • ????Sprite*?tile?=?Sprite::create((*it).c_str());??
  • ????tile->ignoreAnchorPointForPosition(true);??
  • ??????
  • ????int?xIdx?=?0;??
  • ????int?yIdx?=?0;??
  • ??????
  • ????sscanf((*it).c_str(),?"result/map%d_%d.png",&xIdx,&yIdx);??
  • ??????
  • ????int?posX?=?tileSize*xIdx;??
  • ????int?posY?=?tileSize*yIdx;??
  • ??
  • ????tile->setPosition(posX,posY);??
  • ????contentLayer->addChild(tile);??
  • ??
  • ????curTiles[*it]?=?tile;??
  • }??
  • ??
  • curKeys?=?newKeys;??
  • newKeys.clear();??
  • ? ? ? ? 然后還有最重要的一步 [cpp]?view plaincopy
  • Director::getInstance()->getTextureCache()->removeUnusedTextures();??


  • ? ? ? ??再說效果
    。代碼這就寫完了,然后上甄姬測試吧。 然后跑起來之后,你就會發現,媽蛋,怎么還是這么卡的不要不要的。還不如全部加載了流暢。 看幾個性能參數,內存,效果很明顯,GLVerts,效果也很明顯。那為毛還這么卡,一定是我的替換算法寫的太渣了。然后在算法的起始和結尾輸出時間。發現基本可以說是秒過,完全沒有性能瓶頸。 控制變量法,其他不變,把圖塊sprite的紋理全部換成一個圖,再一跑,發現流暢無比,各項參數都是很牛B的。 綜上可知,應該是可推測。如果是繪制的問題,那么每次都需要繪制新的圖庫,都應該一樣的卡才對。 在2次測試中,唯一的區別,就是把每次都去讀取新的圖片紋理,換成了用緩存中已經存在的紋理。那么推測應該就是在用新紋理創建新的sprite這個地方卡住了。 [cpp]?view plaincopy
  • bool?Sprite::initWithFile(const?std::string&?filename)??
  • {??
  • ????...?...??
  • ????Texture2D?*texture?=?Director::getInstance()->getTextureCache()->addImage(filename);??
  • ????...?...??
  • }??
  • [cpp]?view plaincopy
  • Texture2D?*?TextureCache::addImage(const?std::string?&path)??
  • {??
  • ????...?...??
  • ????image?=?new?(std::nothrow)?Image();??
  • ????...?...?????????
  • }??
  • ? ? ? ? 從這里看,在創建新紋理的時候,用到了fileUtil,也就是說讀了文件,也就是說這個地方有IO,然而IO是很容易卡的。 下面是輸出時間:
    [cpp]?view plaincopy
  • -------1--------??
  • -------?time?stamp?:?1386769052??
  • -------2--------??
  • -------?time?stamp?:?1386769053??
  • -------a--------??
  • -------?time?stamp?:?1386769053??
  • -------b--------??
  • -------?time?stamp?:?1386769084??
  • -------a--------??
  • -------?time?stamp?:?1386769084??
  • -------b--------??
  • -------?time?stamp?:?1386769115??
  • -------a--------??
  • -------?time?stamp?:?1386769115??
  • -------b--------??
  • -------?time?stamp?:?1386769147??
  • -------a--------??
  • -------?time?stamp?:?1386769147??
  • -------b--------??
  • -------?time?stamp?:?1386769182??
  • cocos2d:?TextureCache:?removing?unused?texture:?/private/var/mobile/Containers/Bundle/Application/C85C52CF-396B-4630-A4AE-11A412D8C060/Hello?iOS.app/result/map10_3.png??
  • cocos2d:?TextureCache:?removing?unused?texture:?/private/var/mobile/Containers/Bundle/Application/C85C52CF-396B-4630-A4AE-11A412D8C060/Hello?iOS.app/result/map10_2.png??
  • cocos2d:?TextureCache:?removing?unused?texture:?/private/var/mobile/Containers/Bundle/Application/C85C52CF-396B-4630-A4AE-11A412D8C060/Hello?iOS.app/result/map10_0.png??
  • cocos2d:?TextureCache:?removing?unused?texture:?/private/var/mobile/Containers/Bundle/Application/C85C52CF-396B-4630-A4AE-11A412D8C060/Hello?iOS.app/result/map10_1.png??
  • -------3--------??
  • -------?time?stamp?:?1386769184??
  • ? ? ? ? 1是開始計算新圖塊的地方,2是開始添加新圖庫的地方。3是整個完成。a是創建圖庫sprite前,b是創建圖庫sprite之后。 整個過程耗時132ms,也就是說此時FPS只有10不到。其中最慢的幾個地方,都是在ab之間,差不多30ms左右。再次說明和之前那個效率不算太高的替換算法關系不大。。。

    本文標題之所以跟了一個“初”,是我覺得這種方式如果優化一下是可行的,雖然我不知道怎么弄,有知道的麻煩通知我:dinko@126.com

    ?

    ? ? ? ? 所以我覺得這種方式暫時是不靠譜的,還是乖乖用tiled去拼,或者分層來做吧。

    ?

    ?

    PS:

    ? ? ? ? 其實還有一種做法,類似google map和百度地圖那樣,在滾動的時候不加載,滾動停了之后再加載。但是人家那是APP啊,游戲這樣做你看效果如何。分分鐘刪游戲

    轉載于:https://www.cnblogs.com/liuqing0328/p/4900903.html

    總結

    以上是生活随笔為你收集整理的cocos2dx 大地图分块加载的研究(初)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。