转自http://cocos2d.cocoachina.com/bbs/forum.php?mod=viewthread&tid=775&extra=page%3D1
(内容重点: CCSprite,CCSpriteFrameCache,CCSpriteBatchNode,TexturePacker)
很早就开始留意 cocos2d,但不太习惯用 Objective C,同时也考虑到跨平台的问题,所以一直没有正式用过,後来有了 cocos2d-x,就更为关注,老想有空时拿来玩玩写点东西.
之前下载了 cocos2d-x 看了一下附带的例子,被里边的 scene,layer,CCTextureCache,CCSpriteFrameCache 等等弄得很是混乱,再加上工作上的事情,就放下了,直到上周无意中下载了一本书的 PDF,决定再好好地学习一下,而我发现有关 cocos2d-x 的教程并不多,所以我就班门弄斧的和大家分享一下我的学习过程吧.
(附带一提,这本书叫Learn cocos2D Game Development with iOS 5,这麽好的书当然要买正版支持,我已经在 amazon 定购了,正在运送途中)
学习一种新技术,一般我会找一本较好的参考书(或资料),先了解一下它的基本概念,再通过实习应用来熟识.
我想不少童鞋可能是被cocos2d 超多的功能所吸引而选用它,但其实写个一般的小遊戏并不需要知道得太多,以下就是我想先了解的东西:
1) cocos2d 的基本架构 (像scene,layer 等的概念)
2) 怎样在萤幕画东西 (texture 处理,sprite 等)
3) 操控
4) 声效
5) 菜单处理
这里我略为说一下关於怎样在萤幕画东西吧. 首先如往常般的建立一个 cocos2d 项目在 VC2010 Express 里 (为方便起,我会在 Windows 平台上写和测试,有机会再移植到 iOS 和Android):
new_project.jpg
建立了这个叫 Demo 的项目後,我们再把一些图抄到 Resources 里:
res.jpg
把这些图像画到萤幕上,最简单就是把每个图放进一个个的 CCSprite然後加到当前的 Layer 上. 以下这段代码就是把 Background.png,Grass Block.png 和 p8.png 显示在画面:
CCSize size = CCDirector::sharedDirector()->getWinSize(); CCSprite* pSprite = CCSprite::spriteWithFile("Background.png"); CC_BREAK_IF(! pSprite); pSprite->setPosition(ccp(size.width/2,size.height/2)); this->addChild(pSprite,0); pSprite = CCSprite::spriteWithFile("Grass Block.png"); CC_BREAK_IF(! pSprite); pSprite->setPosition(ccp(size.width/2,0); pSprite = CCSprite::spriteWithFile("p8.png"); CC_BREAK_IF(! pSprite); CCSize dim = pSprite->getContentSize(); pSprite->setPosition(ccp(size.width/2,size.height/2+dim.height/2)); this->addChild(pSprite,0);
复制代码
程序跑起来就会看到下边这个画面:
hello.jpg
不过我们最终的目标是把遊戏放到 iOS 或 Android 机子上玩,所以我们不能不考虑一下有关OpenGL ES 优化的问题,两大关键问题就是内存(显存)运用和速度.
先看一下内存问题,OpenGL ES 纹理的宽和高都要是2的倍数,以刚才的例子来说,虽然 Background.png 本身是 480x320,但在载入内存後,它其实会被变成一张 512x512 的纹理,而Grass Block.png 则由 101x131 变成 128x256,我们可以看到如此这样会造成不少浪费.
再看看关於渲染速度方面,OpenGL ES 上来说我们应该尽量减少渲染时切换纹理和 glDrawArray 的呼叫,刚才的例子每画一个图像都会切换一次纹理并呼叫一次 glDrawArray,我们这里只画3样东西,所以不会看到有什麽问题,但如果我们要渲染几十个甚至几百个图像,速度上就会被拖慢. 很明显这并不是我们所想要的.
那我们应该怎麽解决这些问题呢? 答案就是利用纹理地图(texture atlas),比如下面这张纹理就是把我们想用的图像都合併在一起,而它的大小正好是 512x512:
images.png
如果人手去做这个合併工作就太痛苦了,这里要向大家推荐一个十分好用的工具: TexturePacker! (http://www.texturepacker.com/) 这个工具直接支持 cocos2d,实在是太方便了!
首先我们把想要用的图像都放到一个目录里,再用TexturePacker 的 “Add Folder” 功能把目录加进去,TexturePacker 的默认输出格式就是 cocos2d:
texturepacker1.jpg
为了节省位置,我们可以把Border padding 和Shape Padding 都设为1,而选了 Allow rotation 可以让 TexturePacker 更为有效率的摆放图像在纹理里:
texturepacker2.jpg
在键入了输出的档案名字後,我们就可以用 Publish 把纹理输出.
接下来,我们把输出的两个档案(我们这里的例子是images.plist 和 images.png) 放到 Resources 里,就可以在程序里用 CCSpriteFrameCache 把纹理和有关资料载入:
cache->addSpriteFramesWithFile("images.plist","images.png");
复制代码
但现在我们只有一张叫 “images.png” 的纹理,那麽怎样去调用比如是 Background.png 呢? 当然我们还是用 CCSprite 做渲染图像的工作,但在建立一个 CCSprite 时,我们换为用 spriteWithSpriteFrameName 而不是 spriteWithFile:
CCSprite* pSprite = CCSprite::spriteWithSpriteFrameName(" Background.png"); CCSpriteFrameCache *cache = CCSpriteFrameCache::sharedSpriteFrameCache(); cache->addSpriteFramesWithFile("images.plist","images.png"); // Get window size and place the label upper. CCSize size = CCDirector::sharedDirector()->getWinSize(); CCSprite* pSprite = CCSprite::spriteWithSpriteFrameName("Background.png"); CC_BREAK_IF(! pSprite); pSprite->setPosition(ccp(size.width/2,0); pSprite = CCSprite::spriteWithSpriteFrameName("Grass Block.png"); CC_BREAK_IF(! pSprite); pSprite->setPosition(ccp(size.width/2,0); pSprite = CCSprite::spriteWithSpriteFrameName("p8.png"); CC_BREAK_IF(! pSprite); CCSize dim = pSprite->getContentSize(); pSprite->setPosition(ccp(size.width/2,0);
复制代码
来到这里,我们已经逹到了节省内存和减少纹理切换,最後一个我们想做的优化是减少 glDrawArray 的次数,而我们所运用的技巧,就是批次渲染(Batch Rendering),cocos2d 提供了CCSpriteBatchNode 来方便大家做有关的处理,CCSpriteBatchNode 里的CCSprite 都是要用同一个纹理的,所以我们在建立一个 CCSpriteBatchNode 是要给它一个纹理,再把它加到 Layer 里 :
CCTexture2D *texture = CCTextureCache::sharedTextureCache()->textureForKey("images.png"); CCSpriteBatchNode *spriteBatch = CCSpriteBatchNode::batchNodeWithTexture(texture); addChild(spriteBatch);
复制代码
接下来我们如常的建立各个 CCSprite,但不同的地方是我们不把它们加在 Layer 里而是把它们直接加到 CCSpriteBatchNode 上:
CCSpriteFrameCache *cache = CCSpriteFrameCache::sharedSpriteFrameCache(); cache->addSpriteFramesWithFile("images.plist","images.png"); CCTexture2D *texture = CCTextureCache::sharedTextureCache()->textureForKey("images.png"); CCSpriteBatchNode *spriteBatch = CCSpriteBatchNode::batchNodeWithTexture(texture); addChild(spriteBatch); // Get window size and place the label upper. CCSize size = CCDirector::sharedDirector()->getWinSize(); CCSprite* pSprite = CCSprite::spriteWithSpriteFrameName("Background.png"); pSprite->setPosition(ccp(size.width/2,size.height/2)); spriteBatch->addChild(pSprite,0); pSprite = CCSprite::spriteWithSpriteFrameName("Grass Block.png"); pSprite->setPosition(ccp(size.width/2,0); pSprite = CCSprite::spriteWithSpriteFrameName("p8.png"); CCSize dim = pSprite->getContentSize(); pSprite->setPosition(ccp(size.width/2,size.height/2+dim.height/2)); spriteBatch->addChild(pSprite,0);
复制代码
大功告成!