【Cocos2dx开发】精灵

写在前面——有不对的地方,烦请大家批评指正,我也会继续努力提高自己。如果转载请注明出处,谢谢大家支持——Forward。

                                                                   我的微博——秦京一梦

在上一篇博客中,Forward对场景以及各种场景切换特效进行了一个入门级的学习分享,但是只有一个场景是不能完成游戏开发的。用画家画画来做比喻,有了场景就好像画家拿到了一页画纸,而要完成一幅壮丽的图画,还需要在这页画纸上通过合理的分配,填充上不同的元素。这里所说的元素,类比到游戏开发中,就是场景中的精灵。

今天我们就来学习一下Cocos2dx中的精灵这一概念。按照习惯,我们首先来看看CCSprite这个类相关的类图。

图 1

如图1中所示,CCSprite继承自CCTextureProtocol和CCNodeRGBA类,而CCNodeRGBA有继承自CCNode和CCRGBAProtocol类。将上面的关系翻译成自然语言,我们就很好理解CCSprite这个类了——所谓CCSprite精灵,就是用来描述纹理和颜色信息的节点。为了我们能够更深入的了解CCSprite精灵类,还需要进一步深入的学习。

查看CCSprite类源码,可以看到,里面提供了很多API,常用的比如创建、设置纹理信息、设缩放、旋转、坐标位置、锚点信息、隐藏与显示的设置、子节点的添加与移除等等,具体的Forward在这里不再赘述,相信大家都能看懂。

接下来我们通过程序来进行进一步的学习。这里使用Cocos2dx例子中的资源“grossini”。

图 2

好的,我们先在已经创建好的Cocos2dxDemo工程中添加一个精灵上去。

图 3

那么这个精灵在加载的过程中,我们都做了哪些工作呢?

首先,我们加载动画的代码清单如下:

CCSprite* pSprite = CCSprite::create("grossini.png");
pSprite->setPosition(ccp(240,160));
this->addChild(pSprite);

进入精灵类的create接口,代码清单如下:

CCSprite* CCSprite::create(constchar*pszFileName)
{CCSprite *pobSprite = new CCSprite();if(pobSprite && pobSprite->initWithFile(pszFileName)){pobSprite->autorelease();returnpobSprite;}CC_SAFE_DELETE(pobSprite);returnNULL;
}

可以看出在CCSprite的create接口中,我们首先创建了一个精灵类对象,然后通过initWithFile接口来对这个精灵做初始化,就Forward前面说的一样,所谓精灵,就是用来描述纹理和颜色信息的节点,创建CCSprite对象的过程我们就得到了这个节点,初始化就相当于给这个节点填充上对应的纹理和其它必需的信息的过程。继续跟进~~

boolCCSprite::initWithFile(constchar *pszFilename)
{CCAssert(pszFilename != NULL, "Invalid filename for sprite");CCTexture2D *pTexture =CCTextureCache::sharedTextureCache()->addImage(pszFilename);
if(pTexture){CCRect rect = CCRectZero;rect.size =pTexture->getContentSize();
return initWithTexture(pTexture, rect);}
// don't releasehere.
// when loadtexture failed, it's better to get a "transparent" sprite then acrashed program
//this->release();
returnfalse;
}

这里我们可以看出,通过CCTextureCache的addImage,我们会得到一张纹理信息,并且最终用来加载到CCSprite上的就是张纹理。

继续进入到addImage接口中——

CCTexture2D *CCTextureCache::addImage(constchar * path)
{CCAssert(path != NULL, "TextureCache: filep_w_picpath MUST not be NULL");CCTexture2D * texture = NULL;CCImage* pImage = NULL;
// Split updirectory and filename
// MUTEX:
// Needed sinceaddImageAsync calls this method from a different thread
//pthread_mutex_lock(m_pDictLock);std::string pathKey = path;pathKey =CCFileUtils::sharedFileUtils()->fullPathForFilename(pathKey.c_str());if(pathKey.size() == 0){returnNULL;}texture =(CCTexture2D*)m_pTextures->objectForKey(pathKey.c_str());std::string fullpath = pathKey; //(CCFileUtils::sharedFileUtils()->fullPathFromRelativePath(path));if (!texture){std::string lowerCase(pathKey);for (unsignedint i = 0; i< lowerCase.length(); ++i){lowerCase[i] =tolower(lowerCase[i]);}
// all p_w_picpathsare handled by UIImage except PVR extension that is handled by our own handlerdo{if(std::string::npos != lowerCase.find(".pvr")){texture = this->addPVRImage(fullpath.c_str());}elseif (std::string::npos != lowerCase.find(".pkm")){// ETC1 file format, only supportted onAndroidtexture = this->addETCImage(fullpath.c_str());}else{CCImage::EImageFormateImageFormat = CCImage::kFmtUnKnown;if(std::string::npos != lowerCase.find(".png")){eImageFormat =CCImage::kFmtPng;}else if (std::string::npos != lowerCase.find(".jpg") || std::string::npos !=lowerCase.find(".jpeg")){eImageFormat =CCImage::kFmtJpg;}else if (std::string::npos != lowerCase.find(".tif") || std::string::npos !=lowerCase.find(".tiff")){eImageFormat =CCImage::kFmtTiff;}else if (std::string::npos != lowerCase.find(".webp")){eImageFormat =CCImage::kFmtWebp;}pImage = newCCImage();CC_BREAK_IF(NULL == pImage);boolbRet = pImage->initWithImageFile(fullpath.c_str(), eImageFormat);CC_BREAK_IF(!bRet);texture = new CCTexture2D();
if(texture &&texture->initWithImage(pImage) ){
#ifCC_ENABLE_CACHE_TEXTURE_DATA//cache the texture file nameVolatileTexture::addImageTexture(texture, fullpath.c_str(),eImageFormat);
#endifm_pTextures->setObject(texture, pathKey.c_str());texture->release();}else{CCLOG("cocos2d: Couldn't create texture for file:%s inCCTextureCache", path);}}} while(0);}CC_SAFE_RELEASE(pImage);//pthread_mutex_unlock(m_pDictLock);returntexture;
}

可能这个接口的实现略微显得有点长,但并不影响我们的代码阅读与理解。这段代码主要实现了在一张Hash表中去查找对应的纹理信息是否已经加载,如果找到就将对应纹理返回

pathKey =CCFileUtils::sharedFileUtils()->fullPathForFilename(pathKey.c_str());
if(pathKey.size() == 0)
{returnNULL;
}
texture = (CCTexture2D*)m_pTextures->objectForKey(pathKey.c_str());

否则的话就要先创建一个Image然后通过这个Image来完成一张新的纹理的创建,并将这张新创建的纹理加入到Hash表中。

pImage = newCCImage();
CC_BREAK_IF(NULL == pImage);
boolbRet = pImage->initWithImageFile(fullpath.c_str(), eImageFormat);CC_BREAK_IF(!bRet);
texture = new CCTexture2D();
if(texture &&texture->initWithImage(pImage) )
{
#ifCC_ENABLE_CACHE_TEXTURE_DATA//cache the texture file nameVolatileTexture::addImageTexture(texture, fullpath.c_str(),eImageFormat);
#endifm_pTextures->setObject(texture, pathKey.c_str());texture->release();
}

接着,我们进入到CCImage类的initWithImageFile接口中。

boolCCImage::initWithImageFile(constchar * strPath, EImageFormat eImgFmt/* = eFmtPng*/)
{bool bRet =false;
#ifdefEMSCRIPTEN
// Emscriptenincludes a re-implementation of SDL that uses HTML5 canvas
// operationsunderneath. Consequently, loading p_w_picpaths via IMG_Load (an SDL
// API) will be alot faster than running libpng et al as compiled with
// Emscripten.SDL_Surface *iSurf = IMG_Load(strPath);int size =4 * (iSurf->w * iSurf->h);bRet = _initWithRawData((void*)iSurf->pixels, size, iSurf->w,iSurf->h, 8, true);unsignedint *tmp = (unsignedint *)m_pData;intnrPixels = iSurf->w * iSurf->h;for(int i = 0; i < nrPixels; i++){unsignedchar *p = m_pData + i * 4;tmp[i] = CC_RGB_PREMULTIPLY_ALPHA(p[0], p[1], p[2], p[3] );}SDL_FreeSurface(iSurf);
#elseunsignedlong nSize = 0;std::string fullPath =CCFileUtils::sharedFileUtils()->fullPathForFilename(strPath);unsignedchar* pBuffer =CCFileUtils::sharedFileUtils()->getFileData(fullPath.c_str(), "rb", &nSize);if (pBuffer!= NULL && nSize > 0){bRet = initWithImageData(pBuffer,nSize, eImgFmt);}CC_SAFE_DELETE_ARRAY(pBuffer);
#endif// EMSCRIPTENreturnbRet;
}

在#else到#endif一段,我们应该很清楚地看到,在每次调用initWithImageFile接口来初始化Image对象的时候都会去通过“rb”方式读取文件。至此,我们可以说对一个精灵的创建到纹理加载全过程已经有了比较深入的学习了。

批处理图片的学习

Forward对上面的Demo程序作了一些修改——

mIndex = 1;
mElapseTime = 0.0f;
pSprite = NULL;
pSprite = CCSprite::create("grossini.png");
pSprite->setPosition(ccp(240,160));
this->addChild(pSprite);
this->schedule(schedule_selector(HelloWorld::Tick));

首先我们在场景上添加了一个精灵对象,然后通过schedule接口注册了一个回调,具体的回调代码清单如下。

voidHelloWorld::Tick(float dt)
{mElapseTime += dt;if (mElapseTime <= 0.3f){return;}mElapseTime = 0.0f;int tIndex = mIndex % 14 + 1;mIndex++;char strImageName[64] = {0};sprintf(strImageName,"grossini_dance_%02d.png",tIndex);pSprite->initWithFile(strImageName);
}

翻译为自然语言就是,每隔0.3秒,我们对之前创建的grossini_dance对象做一次纹理修改,编译运行——grossini_dance果然动起来了。

但当我们断点调试的时候,会发现在每次使用的纹理图片在第一次使用的时候都会执行到这里。

bool bRet =pImage->initWithImageFile(fullpath.c_str(), eImageFormat);
CC_BREAK_IF(!bRet);

上面我们提到“调用initWithImageFile接口来初始化Image对象的时候都会去通过“rb”方式读取文件”而文件操作一般都是比较耗时的,在小的Demo程序中可能看不到这一点对效率造成的影响,但是当我们在做的是一个真正的游戏项目时,这里的时间消耗就不能忽略甚至与不能容忍了。

这就引出了我们接下来要说的另外一问题。当我们在第一次使用图2中“grossini_dance_01.png”到“grossini_dance_14.png”这一套序列图片的纹理信息的时候,我们需要对一张图片进行一次文件读取。

解决这个问题的思路是,我们通过将一张张碎图拼到一张大图上去,所有纹理在第一次加载图片的时候就已经读进内存,以后直接使用,通过降低文件读取次数来提高效率。这里Forward使用TexturePacker工具(不了TP打包工具的同学可以去网上查找一下相关资料,网上资源很丰富哦~^_^),将十四张碎图拼接成一张大图并导出。

图 4

图 5

如图5所示,我们在导出后,得到了一张拼接好的大图和一个plist文件。打开这个plist文件我们会发现,其实它就是用来描述每一张碎图在这张大图的上的位置,像素宽高等等信息的。

下面我们就用这套资源来重新完成上面的那个简易动画。代码清单如下。

mIndex = 1;
mElapseTime = 0.0f;
pSprite = NULL;
CCSpriteFrameCache::sharedSpriteFrameCache()->addSpriteFramesWithFile("grossini.plist");
pSprite = CCSprite::create("grossini.png");
pSprite->setPosition(ccp(240,160));
this->addChild(pSprite);
this->schedule(schedule_selector(HelloWorld::Tick));

与之前的不同之处在于,这里使用CCSpriteFrameCache加载了我们导出的plist文件,其实这里就是将plist文件描述的“grossiniDemo.png”大图加载到内存并通过plist文件描述对其划分为一个个小的CCSpriteFrame。

voidHelloWorld::Tick(float dt)
{mElapseTime += dt;if (mElapseTime <= 0.3f){return;}mElapseTime = 0.0f;int tIndex = mIndex % 14 + 1;mIndex++;char strImageName[64] = {0};sprintf(strImageName,"grossini_dance_%02d.png",tIndex);pSprite->initWithSpriteFrame(CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName(strImageName));
}

然后在使用的时候,我们直接通过每个碎图名来获取对应纹理信息即可。

OK!继续断点调试——我们会发现,Image的initWithImageFile接口只调用到了一次,而这一次其实就是“grossini.png”大图加载的时候调用的。

好的,今天的学习就先到这里吧@_@

转载于:https://blog.51cto.com/forward/1231434

【Cocos2dx开发】精灵相关推荐

  1. Cocos2dx开发之锚点

    Cocos2dx开发之锚点实例讲解 [ http://blog.sina.com.cn/s/blog_ad1675150101ffre.html ] 锚点概念 由于我们在使用Cocos2dx进行开发时 ...

  2. Cocos2d-X开发教程-捕鱼达人-欧阳坚-专题视频课程

    Cocos2d-X开发教程-捕鱼达人-24521人已学习 课程介绍         本次视频全程以捕鱼达人为项目案例讲述如何使用Cocos2d-X来开发这样一款游戏.本次视频使用了主流的C++开发语言 ...

  3. cocos2d-x开发中wstring和string的转换

    cocos2d-x开发中汉字操作技术是必须要面对的,其中可能要用到wstring和string的相互转换.我在网络上搜索到的能够应用于android 和WIN32(其他的平台没有试)环境下的汉字的可选 ...

  4. 用Cocos2dx开发棋牌游戏的观点解析

    众所周知,目前棋牌游戏特别的火.很多游戏公司都想在这一块赚钱,可是却不知用什么软件比较好的去开发棋牌游戏,对此,我列出了两款比较靠谱的软件去开发棋牌游戏,希望对大家有帮助! 第一款软件是cocos2d ...

  5. cocos2dx创造精灵的五种方法

     cocos2dx创建精灵的五种方法(包括使用图片名获取图片) // 创建精灵的五种方法 //方法一:直接创建精灵//适合于要显示的是这张图片的全部区域,CCSprite * sprite = C ...

  6. [cocos2dx开发技巧4]工具CocosBuilder的使用--复杂动画

    转发,请保持地址:http://blog.csdn.net/stalendp/article/details/8760957 一个网友推荐过一款制作动画的工具,叫做 Spriter: 其中有个Demo ...

  7. cocos2d-x 改变精灵图片的2种方法

    cocos2d-x 改变精灵图片的2种方法. 1. // 首先载入贴图集 CCSpriteBatchNode *spriteBatch=CCSpriteBatchNode::batchNodeWith ...

  8. 火云开发课堂 - 《使用Cocos2d-x 开发3D游戏》系列 第九节:卡通渲染

    <使用Cocos2d-x 开发3D游戏>系列在线课程 第九节:卡通渲染 视频地址:http://edu.csdn.net/course/detail/1330/20809?auto_sta ...

  9. 火云开发课堂 - 《使用Cocos2d-x 开发3D游戏》系列 第十九节:雾

    <使用Cocos2d-x 开发3D游戏>系列在线课程 第十九节:雾 视频地址:http://edu.csdn.net/course/detail/1330/20819?auto_start ...

最新文章

  1. 干掉Navicat:这个IDEA的兄弟真香!
  2. linux qt 生成word,Qt 生成word、pdf文檔
  3. 清华开源Jittor:首个国内高校自研深度学习框架,一键转换PyTorch
  4. Android Support Library 24.2.0 正式发布
  5. Battery Historian 使用常用命令
  6. 如何在PowerDesigner将PDM导出生成WORD文档或者html文件
  7. linux中gradle编译慢,【Linux】解决linux下android studio用gradle构建从jcenter或maven下载依赖太慢...
  8. Ubuntu下安装配置JDK
  9. phpstduy8 redisClient 2.0 点不了_关于以太坊 2.0,你想知道的都在这里
  10. synchronized 修饰static方法
  11. SQL SERVER性能优化综述(转载)
  12. MySQL5.5安装版安装教程
  13. php自定义通讯协议,PHP自定义协议攻击 by L0st
  14. HDLM for AIX安装
  15. 13. JavaScript 字符串(String)对象
  16. SelectionKey 说 明
  17. python能开发android吗_用Python开发Android程序到底
  18. 5328笔记 Advanced ML Chapter3-Hypothesis Complexity and Generalisation
  19. C-021.字符类型char 以及ASCII对照表
  20. 刘韧:煮知识 论英雄

热门文章

  1. Linux 下 svn 的使用
  2. C#.NET跨线程控件的相关操作
  3. java web 自定义标签_如何在JavaWeb程序中使用自定义标签
  4. vmware 8 精简 安装版_被困免安装版下载
  5. Linux 操作系统原理 — cgroups 进程资源配额与管理
  6. Kubenetes — YAML 基本语法
  7. VMware 虚拟化编程(15) — VMware 虚拟机的恢复方案设计
  8. Python Module_oslo.vmware_连接 vCenter
  9. 分享一个现代的,免费的,简单而有效的编辑器Vis
  10. React Native填坑之旅 -- 回归小插曲