【玩转cocos2d-x之二十三】多线程和同步03-图片异步加载

转载 2015年07月30日 16:17:18

转自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, size.height/2));
        this->addChild(pSprite, 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, size.height/2));
        this->addChild(pSprite, 0);

                pSprite = CCSprite::spriteWithSpriteFrameName("Grass Block.png");
        CC_BREAK_IF(! pSprite);

        pSprite->setPosition(ccp(size.width/2, size.height/2));
        this->addChild(pSprite, 0);

                pSprite = CCSprite::spriteWithSpriteFrameName("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);

复制代码

来到这里, 我们已经逹到了节省内存和减少纹理切换, 最後一个我们想做的优化是减少 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, size.height/2));
        spriteBatch->addChild(pSprite, 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);

复制代码

大功告成!

多线程和同步03-图片异步加载

转自:http://blog.csdn.net/yang3wei/article/details/23510987
  • azhou_hui
  • azhou_hui
  • 2014-07-19 22:39:36
  • 791

cocos2d-x多线程和同步

  • 2013年11月05日 10:10
  • 182KB
  • 下载

【玩转cocos2d-x之二十二】多线程和同步02-售票

pthread有很多不同应用,官网都有相应的API解释和Sample,这里不再重复,本文主要介绍cocos2d-x中一个多线程和同步示例。 1.卖票 孙鑫老师的C++和Java多线程卖票一直让我念...
  • jackyvincefu
  • jackyvincefu
  • 2013-11-05 10:15:34
  • 5981

第二十三节 蓝牙协议栈之主机通讯

第二十三节  蓝牙协议栈之主机通讯  随着蓝牙4.0模块的大量使用,为了很多从未接触过蓝牙的工程师也能快速便捷地开发蓝牙项目或者使用蓝牙,主从一体、远控IO等等特性也成为蓝牙模块必备的条件。其实,联...
  • snyanglq
  • snyanglq
  • 2015-11-28 10:34:43
  • 867

Citrix 服务器虚拟化之二十三 桌面虚拟化之Provisioning Services与XenDesktop结合

Citrix 服务器虚拟化之二十三  桌面虚拟化之Provisioning Services与XenDesktop结合  Citrix Provisioning Services采用流技术通过网络将单...
  • kkfloat
  • kkfloat
  • 2013-08-14 11:22:41
  • 7197

【安卓】使用多线程实现ListView中图片的异步加载

ListView中图片的加载问题十分突出,因为程序读取图片资源十分耗时,而如果把设置图片放到主线程中的话,在下滑时会感觉到明显的卡顿,用户体验非常之差。 于是我想到设置图片这一步肯定不能不再主线程中进...
  • jiaomenglei
  • jiaomenglei
  • 2016-08-24 17:36:32
  • 1421

C++11多线程

  • 2014年05月12日 20:24
  • 15.06MB
  • 下载

多线程异步加载图片async_pictures

异步加载图片 目标:在表格中异步加载网络图片 目的:模拟 SDWebImage 基本功能实现 理解 SDWebImage 的底层实现机制 SDWebImage 是非常著名的网络图片处理框架,目前国内超...
  • jiahao8915
  • jiahao8915
  • 2015-08-15 22:12:30
  • 779

【玩转cocos2d-x之二十一】多线程和同步01-pthread库

和其他框架一样,cocos2d-x允许我们使用多线程进行编程。cocos2d-x使用的是pthread库,是一套用户级线程库,被广泛地使用在跨平台应用上。 1.配置 在使用pthread之前,需要...
  • jackyvincefu
  • jackyvincefu
  • 2013-11-04 10:41:50
  • 11007

JS实现图片的异步加载

1.概述异步加载:也称为图片的预加载。利用js代码提前加载图片,用户需要时可以直接从本地缓存获取,但是会增加服务器前端的压力。这样做可以提高用户的体验,因为同步加载大图片的时候,图片会一层一层的显示处...
  • Szu_AKer
  • Szu_AKer
  • 2016-10-21 18:00:34
  • 6363
收藏助手
不良信息举报
您举报文章:【玩转cocos2d-x之二十三】多线程和同步03-图片异步加载
举报原因:
原因补充:

(最多只允许输入30个字)