[Cocos2d-x相关教程来源于红孩儿的游戏编程之路CSDN博客地址:http://blog.csdn.net/honghaier]
红孩儿Cocos2d-X学习园地QQ群:249941957 加群写:Cocos2d-x
本章为我的Cocos2d-x教程一书初稿。望各位看官多提建议!
纹理优化实例:拼图优化与实测
[注:本版使用Cocos2d-x 2.02版本]
Cocos2d-x优化方案一文发布一周了,这篇文章我介绍了一些纹理优化方面的方法原理。未来我将会逐步的将这些优化方案的具体制做实例讲解教授给大家。本节我先带领大家来进行拼图优化的具体制做与实测。
我们本节使用的小图为:
这一堆小图共有34张,都是PNG格式的RGBA8888。我们知道OpenGL ES在生成纹理时大小会扩展成2的幂次方。下面我们来统计一下它们生成纹理所占用的内存。
有15张图(grossini~grossini_dance_14.png)大小为85x121,实际纹理占用128x128, 一张128x128大小的RGBA8888格式纹理需要64K内存来存储像素数据。15x64K = 960K。
一张(grossinis_sister1.png) 大小为52x139, 实际纹理占用64x256,64K
一张(grossinis_sister1-testalpha.png)是52x200, ,实际纹理占用64x256,64K
一张是(grossinis_sister2.png) 大小为56x138, 实际纹理占用64x256,64K
六张(btn-about-normal.png~btn-play-selected.png)大小为125x42, 实际纹理占用128x64,6x32K = 192K
两张(b1.png~ f1.png)大小为79x46, 实际纹理占用128x64,2x32K = 64K
两张(b2.png~ f2.png)大小为79x47, 实际纹理占用128x64,2x32K = 64K
三张 (SpinningPeas.png ,SpookyPeas.png, Pea.png ) 大小为37x37。实际纹理占用64x64,3x16K=48K
一张(r1.png)大小为49x47, 实际纹理占用64x64,16k
一张(r2.png)大小为49x48, 实际纹理占用64x64,16k
一张(ball.png)大小为16x16, 实际纹理占用16x16,1K
所有图片在创建为纹理后总内存为1553K
我们来把这些小图加入到场景中。
在场景所在CCLayer中加入一个CCSprite指针容器用来存放生成的精灵.
private:
vector<CCSprite*> m_pSpriteVec;
然后在场景的init函数中加入:
int nRepeat = 100;
//建立一个bool变量做为是否使用批次处理的开关
bool nUseSprteBatchNod = false;
//读取零散文件
if(false == nUseSprteBatchNod)
{
for(int i = 0 ; i < nRepeat ; i++)
{
CCSprite* tpSprite1 = CCSprite::create("grossini.png");
m_pSpriteVec.push_back(tpSprite1);
CCSprite* tpSprite2 = CCSprite::create("grossini_dance_01.png");
m_pSpriteVec.push_back(tpSprite2);
CCSprite* tpSprite3 = CCSprite::create("grossini_dance_02.png");
m_pSpriteVec.push_back(tpSprite3);
CCSprite* tpSprite4 = CCSprite::create("grossini_dance_03.png");
m_pSpriteVec.push_back(tpSprite4);
CCSprite* tpSprite5 = CCSprite::create("grossini_dance_04.png");
m_pSpriteVec.push_back(tpSprite5);
CCSprite* tpSprite6 = CCSprite::create("grossini_dance_05.png");
m_pSpriteVec.push_back(tpSprite6);
CCSprite* tpSprite7 = CCSprite::create("grossini_dance_06.png");
m_pSpriteVec.push_back(tpSprite7);
CCSprite* tpSprite8 = CCSprite::create("grossini_dance_07.png");
m_pSpriteVec.push_back(tpSprite8);
CCSprite* tpSprite9 = CCSprite::create("grossini_dance_08.png");
m_pSpriteVec.push_back(tpSprite9);
CCSprite* tpSprite10 = CCSprite::create("grossini_dance_09.png");
m_pSpriteVec.push_back(tpSprite10);
CCSprite* tpSprite11 = CCSprite::create("grossini_dance_10.png");
m_pSpriteVec.push_back(tpSprite11);
CCSprite* tpSprite12 = CCSprite::create("grossini_dance_11.png");
m_pSpriteVec.push_back(tpSprite12);
CCSprite* tpSprite13 = CCSprite::create("grossini_dance_12.png");
m_pSpriteVec.push_back(tpSprite13);
CCSprite* tpSprite14 = CCSprite::create("grossini_dance_13.png");
m_pSpriteVec.push_back(tpSprite14);
CCSprite* tpSprite15 = CCSprite::create("grossini_dance_14.png");
m_pSpriteVec.push_back(tpSprite15);
CCSprite* tpSprite16 = CCSprite::create("grossinis_sister1.png");
m_pSpriteVec.push_back(tpSprite16);
CCSprite* tpSprite17 = CCSprite::create("grossinis_sister2.png");
m_pSpriteVec.push_back(tpSprite17);
CCSprite* tpSprite18 = CCSprite::create("grossinis_sister1-testalpha.png");
m_pSpriteVec.push_back(tpSprite18);
CCSprite* tpSprite19 = CCSprite::create("r1.png");
m_pSpriteVec.push_back(tpSprite19);
CCSprite* tpSprite20 = CCSprite::create("r2.png");
m_pSpriteVec.push_back(tpSprite20);
CCSprite* tpSprite21 = CCSprite::create("btn-about-normal.png");
m_pSpriteVec.push_back(tpSprite21);
CCSprite* tpSprite22 = CCSprite::create("btn-play-normal.png");
m_pSpriteVec.push_back(tpSprite22);
CCSprite* tpSprite23 = CCSprite::create("btn-play-selected.png");
m_pSpriteVec.push_back(tpSprite23);
CCSprite* tpSprite24 = CCSprite::create("btn-about-selected.png");
m_pSpriteVec.push_back(tpSprite24);
CCSprite* tpSprite25 = CCSprite::create("btn-highscores-selected.png");
m_pSpriteVec.push_back(tpSprite25);
CCSprite* tpSprite26 = CCSprite::create("btn-highscores-normal.png");
m_pSpriteVec.push_back(tpSprite26);
CCSprite* tpSprite27 = CCSprite::create("b1.png");
m_pSpriteVec.push_back(tpSprite27);
CCSprite* tpSprite28 = CCSprite::create("f1.png");
m_pSpriteVec.push_back(tpSprite28);
CCSprite* tpSprite29 = CCSprite::create("b2.png");
m_pSpriteVec.push_back(tpSprite29);
CCSprite* tpSprite30 = CCSprite::create("f2.png");
m_pSpriteVec.push_back(tpSprite30);
CCSprite* tpSprite31 = CCSprite::create("SpinningPeas.png");
m_pSpriteVec.push_back(tpSprite31);
CCSprite* tpSprite32 = CCSprite::create("SpookyPeas.png");
m_pSpriteVec.push_back(tpSprite32);
CCSprite* tpSprite33 = CCSprite::create("Pea.png");
m_pSpriteVec.push_back(tpSprite33);
CCSprite* tpSprite34 = CCSprite::create("ball.png");
m_pSpriteVec.push_back(tpSprite34);
}
//对容器进行一个随机排序
random_shuffle(m_pSpriteVec.begin(),m_pSpriteVec.end());
//初始化一下随机种子,后面用于产生随机位置
srand(GetTickCount());
//产生精灵
for(int i = 0 ; i < nRepeat; i++)
{
for(int j = 0 ; j < 34; j++)
{
int nIndex = i * 34 + j ;
int nPosX = rand()%960;
int nPosY = rand()%640;
m_pSpriteVec[nIndex]->setPosition(ccp(origin.x + nPosX, origin.y + nPosY));
this->addChild(m_pSpriteVec[nIndex], 1);
}
}
}
运行程序后的画面在我的机器【五年前的笔记本,2G内存2GHzCPU】上大约在17帧左右.
左下角显示共有3400个批次,每帧平均耗时0.058秒,每秒大约17.4帧。
下面我们来进行拼图优化,我们使用“红孩儿纹理打包器1.1版本”来进行拼图制做,它具有使用方便,拼图率高的特点。
下载地址:http://download.csdn.net/detail/honghaier/4671677
使用方法:先将要使用的图片放在Test目录,建立一个Export目录用来存放导出图片与信息,启动红孩儿纹理打包器1.1.exe,然后点击 “浏览源图片目录” 选择Test目录,点击“浏览目标目录”选择Export目录。之后打包器会自动进行记算,在Export目录中生成1张512的图以及几种格式的导出文件。
一张512x512的图的大小就是1024K,可以看出,在咱们这个测试例程中,在使用拼合图比散图省了约500K左右,另外也节省了IO的次数和时间呢!
blo格式为二进制文件。
//二进制文件头信息
struct SPackFileHeader
{
int m_Version; //版本
char m_szBigTexName[64]; //大图名称
int m_nImageSize; //大图的大小
int m_nBlockNum; //图块数量
};
//二进制文件数据块
struct SPackNode
{
RECT m_Rect; //对应图块矩形
POINT m_OffsetPt; //中心点偏移
bool m_bRotated; //是否顺时针旋转90度
};
读取时先读入二进制文件头信息,然后根据m_nBlockNum遍历读入数据块信息
ini格式为Windows信息文件。
INI格式:
[BigTexture]
Name=大图名称
BlockSize=拼合到此大图的图块数量
[Block]
Block0=图块矩形left,图块矩形top,图块矩形宽right,图块矩形bottom,是否是否顺时针旋转90度,中心点偏移x,中心点偏移y
plist格式为XML信息文件。
我们可以根据自已的实际需要来使用相应的信息格式文件来进行读取。
本节我以二进制信息文件来进行讲解:
if(false == nUseSprteBatchNod) { } else { //创建拼图批次,里面共有34个图块 CCSpriteBatchNode* pNewBatchNode = CCSpriteBatchNode::create("Big_0.png", 34 ); //读取二进制文件,将数据读取到容器中 this->addChild(pNewBatchNode,1); //读取二进制文件 string szResPath(CCFileUtils::sharedFileUtils()->getResourceDirectory()); szResPath += "Big_0.blo"; FILE* hFile = fopen(szResPath.c_str(),"rb"); if(hFile) { //先读取文件头 SPackFileHeader tFileHeader; fread(&tFileHeader,sizeof(SPackFileHeader),1,hFile); //遍历读取每个图块 SPackNode tPackNode; for(int b = 0 ; b < tFileHeader.m_nBlockNum ; ++b) { fread(&tPackNode,sizeof(SPackNode),1,hFile); for(int i = 0 ; i < nRepeat ; i++) { //创建精灵 CCRect tRect; if(tPackNode.m_bRotated) { tRect = CCRect(tPackNode.m_Rect.left,tPackNode.m_Rect.top,tPackNode.m_Rect.bottom-tPackNode.m_Rect.top,tPackNode.m_Rect.right-tPackNode.m_Rect.left); } else { tRect = CCRect(tPackNode.m_Rect.left,tPackNode.m_Rect.top,tPackNode.m_Rect.right-tPackNode.m_Rect.left,tPackNode.m_Rect.bottom-tPackNode.m_Rect.top); } CCSize tSize(tPackNode.m_Rect.right-tPackNode.m_Rect.left+tPackNode.m_OffsetPt.x,tPackNode.m_Rect.bottom-tPackNode.m_Rect.top+tPackNode.m_OffsetPt.y); //创建精灵并从拼合图中初始化纹理贴图。 CCSprite* tpSprite1 = CCSprite::create(); tpSprite1->initWithTexture(pNewBatchNode->getTexture(), tRect,tPackNode.m_bRotated); //将精灵放入容器 m_pSpriteVec.push_back(tpSprite1); } } fclose(hFile); random_shuffle(m_pSpriteVec.begin(),m_pSpriteVec.end()); srand(GetTickCount()); for(int i = 0 ; i < 2 * nRepeat; i++) { for(int j = 0 ; j < 17 ; j++) { int nIndex = i * 17 + j ; int nPosX = rand()%960; int nPosY = rand()%640; m_pSpriteVec[nIndex]->setPosition(ccp(origin.x + nPosX, origin.y + nPosY)); pNewBatchNode->addChild(m_pSpriteVec[nIndex]); } } } }
把nUseSprteBatchNod设为true,运行程序后的FPS大约在22帧左右.可以清楚的看到批次数为1。
最后做一下总结:
在进行2D游戏的开发时,图片绝对是重中之重,有图片才有画面,有图片才有动画,所以好的图片使用效率是一个成功游戏的重要技术保证,希望大家多研究一下。下课~