原文地址:http://blog.csdn.net/musicvs/article/details/8105611
1. 会动的精灵
第一篇教程教我们的是创建一个精灵,但是它不会动,现在终于可以创建会动的精灵了,看代码先:
- /* 加载图片帧*/
- CCSpriteFrameCache* cache = CCSpriteFrameCache::sharedSpriteFrameCache();
- cache->addSpriteFramesWithFile("images.plist", "images.png");
- /* 创建一个精灵*/
- CCSprite* girlSprite = CCSprite::createWithSpriteFrameName("girl1.png");
- CCSize size = CCDirector::sharedDirector()->getWinSize();
- girlSprite->setPosition(ccp(size.width / 2, size.height / 2));
- /* 让精灵执行一个动画,
- animation是名称,animate是动词,
- 所以要动起来就要用CCAnimate创建一个会动的动画,我是这么记忆的,嘿嘿
- */
- girlSprite->runAction(CCAnimate::create(animation));
- this->addChild(girlSprite);
/* 加载图片帧*/
CCSpriteFrameCache* cache = CCSpriteFrameCache::sharedSpriteFrameCache();
cache->addSpriteFramesWithFile("images.plist", "images.png");
/* 创建一个精灵*/
CCSprite* girlSprite = CCSprite::createWithSpriteFrameName("girl1.png");
CCSize size = CCDirector::sharedDirector()->getWinSize();
girlSprite->setPosition(ccp(size.width / 2, size.height / 2));
/* 让精灵执行一个动画,
animation是名称,animate是动词,
所以要动起来就要用CCAnimate创建一个会动的动画,我是这么记忆的,嘿嘿
*/
girlSprite->runAction(CCAnimate::create(animation));
this->addChild(girlSprite);
首先加载图片(SpriteFrame),这是上一篇教程教过我们的。
然后我先省略了中间的重要步骤,直接创建了一个精灵,最后有一句话:
girlSprite->runAction(CCAnimate::create(animation));
这就是让精灵动起来的方法——runAction。
要让精灵动起来,我们需要一个动画对象,这个对象其实就是由若干个图片帧组合的:
- /* 数组对象,和Java的集合差不多?*/
- CCArray* framesArray = CCArray::create();
- /* 把动画所需的所有图片帧添加到数组里*/
- CCSpriteFrame* frame = cache->spriteFrameByName("girl1.png");
- framesArray->addObject(frame);
- frame = cache->spriteFrameByName("girl2.png");
- framesArray->addObject(frame);
- frame = cache->spriteFrameByName("girl3.png");
- framesArray->addObject(frame);
- frame = cache->spriteFrameByName("girl4.png");
- framesArray->addObject(frame);
- /* 使用图片帧生成动画对象,每隔500毫秒播放一帧*/
- CCAnimation* animation = CCAnimation::createWithSpriteFrames(framesArray, 0.5f);
- animation->setLoops(-1); // 设置循环播放
- animation->setRestoreOriginalFrame(true); // 设置播放完毕后恢复精灵原始帧
/* 数组对象,和Java的集合差不多?*/
CCArray* framesArray = CCArray::create();
/* 把动画所需的所有图片帧添加到数组里*/
CCSpriteFrame* frame = cache->spriteFrameByName("girl1.png");
framesArray->addObject(frame);
frame = cache->spriteFrameByName("girl2.png");
framesArray->addObject(frame);
frame = cache->spriteFrameByName("girl3.png");
framesArray->addObject(frame);
frame = cache->spriteFrameByName("girl4.png");
framesArray->addObject(frame);
/* 使用图片帧生成动画对象,每隔500毫秒播放一帧*/
CCAnimation* animation = CCAnimation::createWithSpriteFrames(framesArray, 0.5f);
animation->setLoops(-1); // 设置循环播放
animation->setRestoreOriginalFrame(true); // 设置播放完毕后恢复精灵原始帧
好像没有什么能解释的,CCAnimation对象需要一组图片帧(SpriteFrame)来创建,而
这组SpriteFrame对象用一个CCArray来存放。
CCArray应该就是和Java的List类似?但是coco2d-x的2.x版本里把CCArray的泛型去掉
了,有点不习惯(不知道是不是我弄错了?噗)。
对了,1.x版本和2.x版本创建CCAnimation是有点不一样的(我就不说了哈,我只研究2.x版本~)。
2. 工具类
每次要一个精灵动起来就要写这么多行代码,虽然简单,但是看着就很繁琐,所以教程里面就教我们写一个工具类。
把前面生成CCAnimation的步骤抽离出来,写成一个函数:
- cocos2d::CCAnimation* SpriteHelper::createAnimWithSingleFrameN( const char* name, int count, float delay )
- {
- CCSpriteFrameCache* cache = CCSpriteFrameCache::sharedSpriteFrameCache();
- CCArray* framesArray = CCArray::createWithCapacity(count);
- char str[80];
- for(int i = 1; i <= count; i++)
- {
- sprintf(str, "%s%d.png", name, i);
- framesArray->addObject(cache->spriteFrameByName(str));
- }
- CCAnimation* animation = CCAnimation::createWithSpriteFrames(framesArray);
- animation->setLoops(-1);
- animation->setRestoreOriginalFrame(true);
- animation->setDelayPerUnit(delay);
- return animation;
- }
cocos2d::CCAnimation* SpriteHelper::createAnimWithSingleFrameN( const char* name, int count, float delay )
{
CCSpriteFrameCache* cache = CCSpriteFrameCache::sharedSpriteFrameCache();
CCArray* framesArray = CCArray::createWithCapacity(count);
char str[80];
for(int i = 1; i <= count; i++)
{
sprintf(str, "%s%d.png", name, i);
framesArray->addObject(cache->spriteFrameByName(str));
}
CCAnimation* animation = CCAnimation::createWithSpriteFrames(framesArray);
animation->setLoops(-1);
animation->setRestoreOriginalFrame(true);
animation->setDelayPerUnit(delay);
return animation;
}
没有什么新的知识,噗,当然了,用了一个sprintf函数,用来连接字符串的。我到现在还不懂怎么用C++里面的String对象,Java里用String用习惯了,现在用char*很不习惯,嘻嘻。
3. 如果精灵图片是连在一起的
我们常常会看到这样连在一起的图片:
而刚刚写的那个工具类的函数是针对一张一张分开的图片的,于是,我们要修改一下:
- /************************************************************************/
- /* 根据图片列数,自动切割图片,生成CCAnimation对象 */
- /************************************************************************/
- cocos2d::CCAnimation* SpriteHelper::createAnimByLineFrameN( const char* name, int col, float delay )
- {
- CCSpriteFrameCache* cache = CCSpriteFrameCache::sharedSpriteFrameCache();
- CCSpriteFrame* frame = cache->spriteFrameByName(name);
- CCTexture2D* texture = frame->getTexture();
- CCSize frameSize = frame->getOriginalSizeInPixels();
- CCRect frameRect = frame->getRect();
- float w = frameSize.width / col;
- float h = frameSize.height;
- float x = frameRect.origin.x;
- float y = frameRect.origin.y;
- CCArray* framesArray = CCArray::createWithCapacity(col);
- for(int i = 0; i < col; i++)
- {
- framesArray->addObject(CCSpriteFrame::createWithTexture(texture, CCRectMake(x, y, w, h)));
- x += w;
- }
- CCAnimation* animation = CCAnimation::createWithSpriteFrames(framesArray);
- animation->setLoops(-1);
- animation->setRestoreOriginalFrame(true);
- animation->setDelayPerUnit(delay);
- return animation;
- }
/************************************************************************/
/* 根据图片列数,自动切割图片,生成CCAnimation对象 */
/************************************************************************/
cocos2d::CCAnimation* SpriteHelper::createAnimByLineFrameN( const char* name, int col, float delay )
{
CCSpriteFrameCache* cache = CCSpriteFrameCache::sharedSpriteFrameCache();
CCSpriteFrame* frame = cache->spriteFrameByName(name);
CCTexture2D* texture = frame->getTexture();
CCSize frameSize = frame->getOriginalSizeInPixels();
CCRect frameRect = frame->getRect();
float w = frameSize.width / col;
float h = frameSize.height;
float x = frameRect.origin.x;
float y = frameRect.origin.y;
CCArray* framesArray = CCArray::createWithCapacity(col);
for(int i = 0; i < col; i++)
{
framesArray->addObject(CCSpriteFrame::createWithTexture(texture, CCRectMake(x, y, w, h)));
x += w;
}
CCAnimation* animation = CCAnimation::createWithSpriteFrames(framesArray);
animation->setLoops(-1);
animation->setRestoreOriginalFrame(true);
animation->setDelayPerUnit(delay);
return animation;
}
因为怕大家看到代码太长会厌烦,所以我把注释都去掉了,大家先扫一眼,看懂了就好,如果还有疑问的话,请看下面这段加上详细注释之后的代码:
- /************************************************************************/
- /* 根据图片列数,自动切割图片,生成CCAnimation对象 */
- /************************************************************************/
- cocos2d::CCAnimation* SpriteHelper::createAnimByLineFrameN( const char* name, int col, float delay )
- {
- CCSpriteFrameCache* cache = CCSpriteFrameCache::sharedSpriteFrameCache();
- CCSpriteFrame* frame = cache->spriteFrameByName(name);
- /*
- SpriteFrame的纹理对象(CCTexture2D),
- 我的理解就是那张TexturePacker打包的大图
- 并且,同一张大图生成的SpriteFrame对象的getTexture的结果是一致的。
- 换句话说,SpriteFrame其实保存了一份图片纹理的引用。
- */
- CCTexture2D* texture = frame->getTexture();
- /*
- 我很好奇,getOriginalSize和getOriginalSizeInPixels倒底有什么区别?
- 稍微我研究一下,再和大家分享
- */
- CCSize frameSize = frame->getOriginalSizeInPixels();
- /*
- SpriteFrame是否没有坐标的概念的,
- 只有矩阵范围这个概念,所以要获得它的位置,
- 必须通过它的矩阵范围来取得
- */
- CCRect frameRect = frame->getRect();
- float w = frameSize.width / col; /* 每帧图片的宽*/
- float h = frameSize.height;
- float x = frameRect.origin.x;
- float y = frameRect.origin.y;
- CCArray* framesArray = CCArray::createWithCapacity(col);
- for(int i = 0; i < col; i++)
- {
- /*
- 我一直都很担心,createWithTexture是不是就生成了新的纹理对象?
- 不过,我再仔细想想,SpriteFrame本来就只是引用了纹理对象,而不是新建一份小纹理。
- 我的理解是,SpriteFrame其实只是其记录数据的作用,记录了某个图片帧在整张大图片的位置,以及截取范围等。
- 所以,createWithTexture只不过是把SpriteFrame的范围改变了一下而已。
- */
- framesArray->addObject(CCSpriteFrame::createWithTexture(texture, CCRectMake(x, y, w, h)));
- x += w;
- }
- CCAnimation* animation = CCAnimation::createWithSpriteFrames(framesArray);
- animation->setLoops(-1);
- animation->setRestoreOriginalFrame(true);
- animation->setDelayPerUnit(delay);
- return animation;
- }
/************************************************************************/
/* 根据图片列数,自动切割图片,生成CCAnimation对象 */
/************************************************************************/
cocos2d::CCAnimation* SpriteHelper::createAnimByLineFrameN( const char* name, int col, float delay )
{
CCSpriteFrameCache* cache = CCSpriteFrameCache::sharedSpriteFrameCache();
CCSpriteFrame* frame = cache->spriteFrameByName(name);
/*
SpriteFrame的纹理对象(CCTexture2D),
我的理解就是那张TexturePacker打包的大图
并且,同一张大图生成的SpriteFrame对象的getTexture的结果是一致的。
换句话说,SpriteFrame其实保存了一份图片纹理的引用。
*/
CCTexture2D* texture = frame->getTexture();
/*
我很好奇,getOriginalSize和getOriginalSizeInPixels倒底有什么区别?
稍微我研究一下,再和大家分享
*/
CCSize frameSize = frame->getOriginalSizeInPixels();
/*
SpriteFrame是否没有坐标的概念的,
只有矩阵范围这个概念,所以要获得它的位置,
必须通过它的矩阵范围来取得
*/
CCRect frameRect = frame->getRect();
float w = frameSize.width / col; /* 每帧图片的宽*/
float h = frameSize.height;
float x = frameRect.origin.x;
float y = frameRect.origin.y;
CCArray* framesArray = CCArray::createWithCapacity(col);
for(int i = 0; i < col; i++)
{
/*
我一直都很担心,createWithTexture是不是就生成了新的纹理对象?
不过,我再仔细想想,SpriteFrame本来就只是引用了纹理对象,而不是新建一份小纹理。
我的理解是,SpriteFrame其实只是其记录数据的作用,记录了某个图片帧在整张大图片的位置,以及截取范围等。
所以,createWithTexture只不过是把SpriteFrame的范围改变了一下而已。
*/
framesArray->addObject(CCSpriteFrame::createWithTexture(texture, CCRectMake(x, y, w, h)));
x += w;
}
CCAnimation* animation = CCAnimation::createWithSpriteFrames(framesArray);
animation->setLoops(-1);
animation->setRestoreOriginalFrame(true);
animation->setDelayPerUnit(delay);
return animation;
}
4. 如果精灵图片是堆在一起并且被旋转了
哇,这种图片要我们来切的话,还是要花点心思的,教程里作者已经给出了算法了,有兴趣的朋友可以研究一下。
我在这里只说两点:
1. CCSpriteFrame记录的是图片帧在整张大图片里的位置信息,而不管图片帧在大图片里是否旋转过,它所记录的图片宽高是不会变的。图片帧被旋转之后,CCSpriteFrame会使用isRotate来记录。
2. 仔细研究教程作者的算法,我们应该会发现,截取被旋转后的图片帧的方式有点奇怪,并且,当使用CCSpriteFrame的setRotate方法后,CCSpriteFrame对象的纹理对象会以CCSpriteFrame所记录的位置为基准去旋转。有点难逻辑,要自己仔细看看算法才能体会到呢