菜鳥也能學cocos2dx3.0 淺析刀塔傳奇(下)
首先我們講點話外的東西,非同步載入:眾所周知,loading裡面一般都是載入資料的,那麼是怎麼載入的呢?
Director::getInstance()->getTextureCache()->addImageAsync(const std::string &path, const std::function<void(Texture2D*)>& callback)//引數1,檔案路徑,引數2,回撥函式(一般都是進度條)
那麼如果我們要載入幀動畫呢?
auto frameache=SpriteFrameCache::getInstance(); frameache->addSpriteFramesWithFile(" xxxxxx ");//引數,plist檔案路徑
可是這樣並不是非同步載入,那麼應該怎麼辦?其實還是用上面這兩個:
Director::getInstance()->getTextureCache()->addImageAsync(const std::string &path, const std::function<void(Texture2D*)>& callback);//我們首先非同步載入了紋理
auto My_Texture2D=Director::getInstance()->getTextureCache()->addImage(" ");
//如果我們成功非同步載入圖片之後,我們可以從紋理快取裡面,即std::unordered_map<std::string,Texture2D*> _textures 返回對應key的紋理。而key在引擎原始碼中則是檔案的完整路徑,因為在texture2d裡面會做一步std::string fullpath =FileUtils::getInstance()->fullPathForFilename(path);而我們外部用的話,只需要平時resources的路徑即可。
或者直接auto cache=SpriteFrameCache::getInstance(); cache->addSpriteFramesWithFile("plist路徑","png路徑");
auto frameache=SpriteFrameCache::getInstance();
frameache->addSpriteFramesWithFile(" xxxxxx ",My_Texture2D);//引數1,plist檔案路徑,引數2,紋理 這樣我們就能完成非同步載入幀動畫了~
進入正題 刀塔傳奇:
首先我們解壓dota的包,會發現有
一張背景為黑色的jpg圖和一張灰度圖,對不是帶alpha的png圖,而是無alpha的jpg,那麼刀塔傳奇為什麼要這麼做呢?
以下為我個人的理解:
jpg圖是將影象畫素進行了壓縮,而另外一張灰度圖實際上則是附帶了透明通道的8位的png,那麼我們只要將灰度圖的alpha複製過去,就能實現jpg的背景鏤空。
這樣的話就能縮小圖片的資源大小,畢竟你jpg是經過壓縮的,同等鏤空的png果斷是要大很多的,這裡可以確定jpg+8位灰度<png。
那麼這樣做的優勢在哪裡呢,沒錯,就是在圖片資源很多的情況下,我們可以清楚得感受到使用這種圖片格式的好處:一、可以使你的應用程式更小,因為圖片是壓縮過了的。二、你的遊戲能夠啟動地更快。
原理上和pvr.ccz有點類似,但是pvr.ccz有著它獨特的優勢,就是pvr格式可以直接被ios的顯示卡所認可,比png更加安全,避免大量圖片載入的記憶體問題。
然後呢,我們應該怎麼用jpg+灰度圖實現紋理鏤空的效果呢?
看引擎程式碼!
Texture2D * TextureCache::addImage(const std::string &path)
{
Texture2D * texture = nullptr;
Image* image = nullptr;
// Split up directory and filename
// MUTEX:
// Needed since addImageAsync calls this method from a different thread
std::string fullpath = FileUtils::getInstance()->fullPathForFilename(path);//獲取完整路徑
if (fullpath.size() == 0)
{
return nullptr;
}
auto it = _textures.find(fullpath);//在快取中查詢是否是已經載入過的紋理圖片
if( it != _textures.end() )
texture = it->second;
if (! texture)//如果是未被加過的紋理
{
// all images are handled by UIImage except PVR extension that is handled by our own handler
do
{
image = new Image();//建立一個image物件,imgae物件中封裝了libjpeg,即jpg的解壓/壓縮庫,另外利用了FileUtils::getInstance()->getDataFromFile(_filePath) FileUtils預設會以rb模式讀取二進位制的資料資訊
CC_BREAK_IF(nullptr == image);
bool bRet = image->initWithImageFile(fullpath);//將FileUtiles讀取的資料用jpeg進行解壓,期間有個圖片格式的判斷
CC_BREAK_IF(!bRet);
texture = new Texture2D();
if( texture && texture->initWithImage(image) )//載入紋理將rgb888的jpg轉為rgba8888
{
#if CC_ENABLE_CACHE_TEXTURE_DATA
// cache the texture file name
VolatileTextureMgr::addImageTexture(texture, fullpath);
#endif
// texture already retained, no need to re-retain it
_textures.insert( std::make_pair(fullpath, texture) );
}
else
{
CCLOG("cocos2d: Couldn't create texture for file:%s in TextureCache", path.c_str());
}
} while (0);
}
CC_SAFE_RELEASE(image);
return texture;
}
/*..
省略
*/
*outDataLen = dataLen/3*4;//將rgb的length增長到rgba的長度
*outData = new unsigned char[*outDataLen];//申請一塊長度為rgba長度的記憶體
auto TempData=outData;
/*..
省略
*/
for (ssize_t i = 0, j=0,l = dataLen - 2; i < l; i += 3,++j)
{
* outData ++ = data[i]; //R
* outData ++ = data[i + 1]; //G
* outData ++ = data[i + 2]; //B
* outData ++ =png_data[j]; //A 根據灰度圖的畫素資訊0和255設定alpha
}
auto new_Texture2d=new Texture2D();
new_Texture2d->initWithData(TempData,datalen,pixelFormat, imageWidth, imageHeight, imageSize);
//這樣我們就能實現刀塔傳奇的主介面效果。 至於主介面中的比如泉水發光的效果,則是通過shader實現的
至於dota的骨骼部分據說是他們自己的flash引擎做的,而我則用spine做了骨骼,因為cocostuio的骨骼功能不完善,spine則是專門針對骨骼的編輯器,並且全平臺支援,u3d,libgdx,as3等等。最讚的應該就是spine的ffd,蒙皮了。
目前正在研究binary,即spine的二進位制檔案匯出,直接讀取binary建立骨骼的話,將會比json讀取的更快,記憶體佔用更小。
ps:cocos2dx 3.1rc0出了,支援prite3d和video,目前感覺video應該是比較實用的,3d的話,個人感覺還是u3d吧。。。cocos的3d支援還需要發展。