前言
朋友们,欢迎回来!今天,我们将要征服Cocos2d里面的精灵。这个过程并不会像你想像中那么难,接下来的教程,我就会证明给你看。首先,我们有N种方法在屏幕上显示一张图片。。。其实,我们在《Cocos2d-x 菜单教程:第三部分》就已经知道一种显示图片的方式了。 那么,我们今天将学习哪些内容呢?我们将学习有关 “Sprite”, “SpriteSheets”, “SpriteFrame”,以及“Texture2d” 和 “TextureCache”的一切!在这篇教程的最后,我们将有一条龙在一个简单的背景地形上面飞,路径由用户的手指滑动touch决定。很酷吧? 这里有本教程的完整源代码 在这个教程中,我们还是学习一些基础知识--不过没有菜单啦,我们主要关心的是精灵(sprite)。先看一看整个教程最后的产品是什么样子吧!如下图所示:


#pragma once
#include "cocos2d.h"
#include "PlayLayer.h"
USING_NS_CC;
class SceneManager: public Object
{
public:
static void goPlay();
static void go(Layer* layer);
static Scene* wrap(Layer* layer);
};
SceneManager.cpp
#include "SceneManager.h"
void SceneManager::goPlay()
{
auto layer = PlayLayer::create();
SceneManager::go(layer);
}
void SceneManager::go(Layer* layer)
{
auto director = Director::getInstance();
auto newScene = SceneManager::wrap(layer);
if (director->getRunningScene())
{
director->replaceScene(newScene);
}
else
{
director->runWithScene(newScene);
}
}
Scene* SceneManager::wrap(Layer* layer)
{
auto newScene = Scene::create();
newScene->addChild(layer);
return newScene;
}
一定要记得,把delegate类里面的Director replaceScene调用,改成 “[SceneManager goPlay];”调用。
好,如果你们都看了菜单教程的话,那么看到这里,你们可能会觉得烦了。所以,来点新鲜的吧:
PlayLayer.h
#pragma once
#include "cocos2d.h"
#include "SceneManager.h"
USING_NS_CC;
class PlayLayer: public Layer
{
public:
Vector<Action*>* flyActionArray;
Sprite* dargon;
Action* flyAction;
Action* moveAction;
bool moving;
CREATE_FUNC(PlayLayer);
bool virtual init();
};
那么,这里做了些什么事呢?我们创建了三个实例变量:_dragon, _flyAction and _moveAction.从名字差不多也可以看出来它们到底是干什么用的,_dragon是我们的主角精灵,_flyAnimmation负责处理dragon的煽动翅膀的动画,而_moveAction负责处理从一点移动到另一个点。
因此,Sprite非常重要,但是,你可能会问你自己,我到底是应该继承CCSprite,还是包含一个Sprite实例呢?我这里不使用一个Dragon类来继承Sprite的原因是你可以从文件中加载一张图片来初始化它。这里有一个非常著名的问题:“Is-a”还是“has-a”?好吧,我的喜好是派生至Node,然后把所有的Sprite当作它的属性。因为,我相信这样做会给你最大的灵性性。我承认,如果从Sprite继承的话,刚开始会有许多方便之处,比如可以直接添加到BatchNode等。但是,我还是坚信,把CCSprite当作一个属性的话,你可以在以后的编程中获得巨大的好处。
PlayLayer.cpp
#include "PlayLayer.h"
enum {ktagSpriteSheet = 1,};
bool PlayLayer::init()
{
if (!Layer::init())
{
return false;
}
auto background = Sprite::create("Terrain.png");
background->setPosition(ccp(160,240));
this->addChild(background);
auto texture = TextureCache::getInstance()->addImage("dragon.png");
auto sheet = SpriteBatchNode::create("dragon.png",10);
this->addChild(sheet, 0, ktagSpriteSheet);
Size s = Director::getInstance()->getWinSize();
Vector<SpriteFrame*> animFrames;
for (int i = 0; i < 8; i++)
{
animFrames.clear();
for (int j = 0; j < 10; j++)
{
auto frame = SpriteFrame::createWithTexture(texture, Rect(j * 75, i * 70, 75, 70), false, Director::getInstance()->getVisibleOrigin(), Size(75, 70));
animFrames.pushBack(frame);
}
auto animation = Animation::createWithSpriteFrames(animFrames, 0.1f);
auto animate = Animate::create(animation);
auto seq = Sequence::create(animate, NULL);
this->flyAction = RepeatForever::create(seq);
flyActionArray->pushBack(this->flyAction);
}
auto frame1 = SpriteFrame::createWithTexture(texture, Rect(0, 0, 75, 70), false, Director::getInstance()->getVisibleOrigin(), Size(75, 70));
this->dargon = Sprite::createWithSpriteFrame(frame1);
dargon->setPosition(ccp(s.width/2-80,s.height/2));
sheet->addChild(dargon);
this->flyAction = flyActionArray->at(0);
dargon->runAction(flyAction);
return true;
}
ok,上面的代码看起来比较长,不过没关系,让我们分别解释下:
首先是加载背景图片
auto background = Sprite::create("Terrain.png");
background->setPosition(ccp(160,240));
this->addChild(background);
背景图片加载后放置在屏幕的中心(默认情况下,图片的anchorPoint是图片的中心,你可以使用anchorPoint属性来改变它--举个例子:把背景的anchorPoint从中心点
background->setAnchorPoint(cpp(0.5,0.5));
改成图片的左下角:
background->setAnchorPoint(cpp(0.0,0.0));
接下来:
auto texture = TextureCache::getInstance()->addImage("dragon.png");
auto sheet = SpriteBatchNode::create("dragon.png",10);
this->addChild(sheet, 0, ktagSpriteSheet);
TextureCache和Texture2D是n种方式中的一种,可以把一张图片加载到Sprite中去。我是认真的,真的有n种方法可以做这个事情。。。现在,我不想让你头晕,所以先不列举其它方法了。。。让我们先看看这段代码都做了些什么吧。首先,加载一张dragon.png图片到texture对象中。
因此,接下来,好多人都对SpriteBatchNode的使用感到迷惑不解。一个SpriteBatchNode是一种效率比较高的渲染精灵的方式。比如,你把Sprite加到CCLayer中,那么sprite的draw函数在每一帧调用时都会执行7个opengl 调用来完成sprite的渲染。一个精灵的时候当然没问题,但是,当你在屏幕上有200个精灵的时候,那么就会有200×7次opengl调用。。。而SpriteBatchNode可以“批处理”它的孩子精灵的draw调用。这意味着,当把200个精灵加到SpriteBatchNode中去的时候,只要使用7个opengl调用就可以完成200个孩子的渲染了。在本例中,我们看到dragon类也有许多精灵,所以我们要使用SpriteBatchNode。
更新:关于CCSpriteBatchNode的误解,请参看泰然论坛这个帖子。
1.通过使用纹理集(texture atlas),你可以加快游戏的加载时间,同时减少耗费的内存资源。
2.通过使用SpriteSheet(SpriteBatchNode),你可以提供渲染的性能,(具体查看Performance Tests)
3.通过使用TextureCache和SpriteSheet,那么你可以同时获得加载时间的性能和渲染的性能提升。
当然,现在只有一只龙,可能看不出明显的效果提升。但是,这是最佳实践,你最好一开始就按照这种方式去做,它会为你以后省去很多麻烦事。
Vector<SpriteFrame*> animFrames;
for (int i = 0; i < 8; i++)
{
animFrames.clear();
for (int j = 0; j < 10; j++)
{
auto frame = SpriteFrame::createWithTexture(texture, Rect(j * 75, i * 70, 75, 70), false, Director::getInstance()->getVisibleOrigin(), Size(75, 70));
animFrames.pushBack(frame);
}
auto animation = Animation::createWithSpriteFrames(animFrames, 0.1f);
auto animate = Animate::create(animation);
auto seq = Sequence::create(animate, NULL);
this->flyAction = RepeatForever::create(seq);
flyActionArray->pushBack(this->flyAction);
}
上面这个部分似乎做了很多事情--但是,实际上很简单。我们使用SpriteFrame来创建精灵动画帧,一个SpriteFrame就是从texture里面抠出来的一块小图片区域,需要指定矩形区域。它并不包含图片本身,而是更像一帧图像。每一次,动画运行的时候,就会运行一系列的SpriteFrames,而每个SpriteFrames指向texture里的一小块图片。
因为我们的dragon每个动画都有10张图片,总共有8个方向飞行的动画(对应东南西北8个方向的动画)。我们创建了80个帧,然后把每一个方向飞行的动画都存到Vector(SpriteFrame*)里面。然后,我们把所有的flyAction再添加到flyActionArray里面去。
auto frame1 = SpriteFrame::createWithTexture(texture, Rect(0, 0, 75, 70), false, Director::getInstance()->getVisibleOrigin(), Size(75, 70));
this->dargon = Sprite::createWithSpriteFrame(frame1);
dargon->setPosition(ccp(s.width/2-80,s.height/2));
sheet->addChild(dargon);
this->flyAction = flyActionArray->at(0);
dargon->runAction(flyAction);
因为dragon刚开始时需要有一张图片作为初始状态,所以,我们从animation里面取出第一帧(位置在0,0,宽度是75,高度是70)。我们把dragon精灵初始位置设在屏幕中间偏左一点,然后把它加到spritesheet中,目的是为了获得更好的性能提升。
然后,我们开始运行第一个动画。。。现在,我们可以飞啦!
下篇教程见!
后记:这里使用的方法,说实话,真的过时了。:)不过我们了解了也是有好处的。之前翻译的ray的教程里面,都是使用texturePacker生成pvr.ccz和plist文件来处理的。获得动画帧也很简单,直接spriteFrameByName就可以了。 原文链接地址:http://www.iphonegametutorials.com/2010/09/10/cocos2d-sprite-tutorial/ 上一篇教程中,我们留下了我们孤独的dragon在屏幕中间。。。然而,90%的动画并没有应用到,那可是我们花了很大力气才建立好的呀!太遗憾了!所以,我们这篇教程要弥补这个缺憾。我们将添加touch控制,以此来捕获用户的输入,并根据用户手指的方向来选择一个合适的动画给dragon播放。你就可以用手在屏幕上滑动来指挥dragon移动了。 这里有本教程的完整源代码。 这里介绍一下Cocos2d-x 3.0新的触摸机制: 首先我们需要让layer能接收touch事件。Cocos2d-x 3.0增加了新的事件分发机制,并且让setTouchEnabled为deprecated的方法。对某个方法和类标注deprecated的意思就是这个方法或类不再建议使用。所以我们继承虚函数onEnter,并重写:
void PlayLayer::onEnter()
{
Layer::onEnter();
auto listener = EventListenerTouchOneByOne::create();
listener->setSwallowTouches(true);
listener->onTouchBegan = CC_CALLBACK_2(PlayLayer::onTouchBegan, this);
listener->onTouchMoved = CC_CALLBACK_2(PlayLayer::onTouchMoved, this);
listener->onTouchEnded = CC_CALLBACK_2(PlayLayer::onTouchEnded, this);
auto dispatcher = Director::getInstance()->getEventDispatcher();
dispatcher->addEventListenerWithSceneGraphPriority(listener, this);
这里我们将要声明 “onTouchBegan” 和“onTouchEnded” --第一个方法当第一个touch事件开始的时候被调用,另外一个是touch结束的时候被调用。还有一个 “onTouchMoved”方法,它是你的手一直按在屏幕上面的时候被调用的,在Cocos2d-x 3.0 中,响应者三种事件的函数可以自己绑定自己写好的函数。代码如下,
listener->onTouchBegan = CC_CALLBACK_2(PlayLayer::onTouchBegan, this);
listener->onTouchMoved = CC_CALLBACK_2(PlayLayer::onTouchMoved, this);
listener->onTouchEnded = CC_CALLBACK_2(PlayLayer::onTouchEnded, this);
因此,现在我们有工具和函数了,为什么不编写一些代码呢?
Point moveVector = touchLocation - dragon->getPosition();
float distanceToMove = ccpLength(moveVector);
float moveAngle = ccpToAngle(moveVector);
float cocosAngle = CC_RADIANS_TO_DEGREES(-1 * moveAngle);
float dragonVelocity = 480.0 / 3.0;
float moveDuration = distanceToMove / dragonVelocity;
cocosAngle += 23;
if (cocosAngle < 0)
{
cocosAngle += 360;
}
int runAnim = (int)((cocosAngle)/45);
因此,现在我们需要计算出如何旋转dragon:
- 计算touch点到dragon当前位置的向量
- 计算这个向量的长度(这里是像素长度)
- 计算move向量的弧度
- 把弧度转换成角度(因为Cocos2d-x使用的是角度)
dragon->stopAction(flyAction);
this->flyAction = flyActionArray.at(runAnim);
dragon->runAction(flyAction);
this->moveAction = Sequence::create(MoveTo::create(moveDuration,touchLocation),NULL);
dragon->runAction(moveAction);
最后,我们停止先前的动画,然后基于前面计算的结果“int runAnim = (int)((cocosAngle)/45);”来选择一个新的动画让dragon来run。这样的话,当我们在屏幕上面点击的时候,就可以让dragon朝着正确的方向移动,并且播放正确的动画了。第2部分教程到这里就结束了,让我们第3部分教程见!
原文链接地址:http://www.iphonegametutorials.com/2010/09/10/cocos2d-sprite-tutorial-part-2/
我们在第2部分教程中已经介绍了如何让dragon沿着8个不同的方向移动,并且播放相应的动画,同时,移动过程可以由用户touch屏幕来控制。Cocos2d-x 很酷吧!好了,今天我们将多干点活,我们将创建一大批村民--实际上是N个村民。我们会使用我们已经学习过的技术,从spritesheet里面加载精灵,同时建立相应的精灵动画。
这里有本教程的完整源代码。
那么,我们到底要做成什么样子呢---看了下面的图就明白了:

#include "cocos2d.h"
USING_NS_CC;
class Adventurer: public Node {
Sprite* charSprite;
Action* walkAction;
Action* moveAction;
bool moving;
};
如果你愿意的话,你也可以从Sprite继承,然后我们可以调用initWithFile方法来初始化我们的Adventure 类。但是,我更喜欢从Node继承,然后包含一个Sprite的实例。
Adventurer.cpp
#include "Adventurer.h"
bool Adventurer::init()
{
if (!Node::init())
{
return false;
}
return true;
}
很简单的init函数,上面这段代码,我们再熟悉不过了。这里创建了一个非常简单的类,但是,也给我们一些提示,如何为游戏主角创建class。
现在,我们拥有角色了,让我们来使用之。。。先回到“PlayLayer.h” ,然后做下面一些变更:
#pragma once
#include "cocos2d.h"
#include "SceneManager.h"
USING_NS_CC;
class PlayLayer: public Layer
{
public:
Vector<Action*> flyActionArray;
Texture2D* texture;
SpriteBatchNode* spritesheet;
Vector<char*> charArray;
Sprite* dragon;
Action* flyAction;
Action* moveAction;
bool moving;
CREATE_FUNC(PlayLayer);
bool virtual init();
void virtual onEnter();
bool onTouchBegan(Touch *touch, Event *event);
void onTouchMoved(Touch *touch, Event *event);
void onTouchEnded(Touch* touch, Event* event);
};
我们先导入 “Adventurer.h”,然后定义了3个实例变量。第一个变量 “texture”用来加载adventurer 精灵表单。第二变量 “spritesheet”是把我们将要创建的精灵都进行“批处理”,使之提高效率。最后,我们想要追踪所有的adventurers,所以,我们定义了一个“charArray”.数组。同时我们为每一个实例变量都声明了属性,这样我们就可以在PlayLayer.m间接使用了。(另一种方法是定义tag,在init方法里面指定tag,然后在其它方法里面就可以用self getChildByTag:tag来获得想要的孩子了)
OK,现在我们有一堆类了。不过别担心,我们会在后面把它逐步分开讲解--首先,先让我们实现
PlayLayer.cpp:
#include "PlayLayer.h"
enum {ktagSpriteSheet = 1,};
bool PlayLayer::init()
{
if (!Layer::init())
{
return false;
}
auto background = Sprite::create("Terrain.png");
background->setPosition(ccp(160,240));
this->addChild(background);
auto texture = TextureCache::getInstance()->addImage("dragon.png");
auto sheet = SpriteBatchNode::create("dragon.png",10);
this->addChild(sheet, 0, ktagSpriteSheet);
Size s = Director::getInstance()->getWinSize();
Vector<SpriteFrame*> animFrames;
for (int i = 0; i < 8; i++)
{
animFrames.clear();
for (int j = 0; j < 10; j++)
{
auto frame = SpriteFrame::createWithTexture(texture, Rect(j * 75, i * 70, 75, 70), false, Director::getInstance()->getVisibleOrigin(), Size(75, 70));
animFrames.pushBack(frame);
}
auto animation = Animation::createWithSpriteFrames(animFrames, 0.1f);
auto animate = Animate::create(animation);
auto seq = Sequence::create(animate, NULL);
this->flyAction = RepeatForever::create(seq);
flyActionArray.pushBack(this->flyAction);
}
auto frame1 = SpriteFrame::createWithTexture(texture, Rect(0, 0, 75, 70), false, Director::getInstance()->getVisibleOrigin(), Size(75, 70));
this->dragon = Sprite::createWithSpriteFrame(frame1);
dragon->setPosition(ccp(s.width/2-80,s.height/2));
sheet->addChild(dragon);
this->flyAction = flyActionArray.at(0);
dragon->runAction(flyAction);
texture = TextureCache::getInstance()->addImage("adventurer.png");
spriteSheet = SpriteBatchNode::createWithTexture(texture,100);
this->addChild(spriteSheet,0,ktagSpriteSheet);
this->schedule(schedule_selector(PlayLayer::gameLogic), 1.0f);
return true;
}
void PlayLayer::addAdventurer()
{
Vector<SpriteFrame*>animFrames;
animFrames.clear();
for (int i = 0; i < 9; i++)
{
auto *frame = SpriteFrame::createWithTexture(this->texture, Rect(i * 16, 0, 16, 29));
animFrames.pushBack(frame);
}
auto adventurer = Adventurer::create();
if (adventurer != NULL) {
auto *frame1 = SpriteFrame::createWithTexture(this->texture, Rect(0, 0, 19, 29));
adventurer->charSprite = Sprite::createWithSpriteFrame(frame1);
Size s = Director::getInstance()->getWinSize();
int minY = adventurer->charSprite->getContentSize().width/2;
int maxY = s.height - adventurer->charSprite->getContentSize().height / 2;
int rangeY = maxY - minY;
int actualY = (CCRANDOM_0_1() * rangeY) + minY;
int minX = -300;
int maxX = 0;
int rangeX = maxX - minX;
int actualX = (CCRANDOM_0_1() * rangeX) + minX;
adventurer->charSprite->setPosition(ccp(actualX, actualY));
auto *animation = Animation::createWithSpriteFrames(animFrames,0.2);
auto *animate = Animate::create(animation);
auto *seq = Sequence::create(animate,NULL);
adventurer->walkAction = RepeatForever::create(seq);
auto actionMove = MoveTo::create(10.0f,ccp(s.width + 200, actualY));
auto actionMoveDone = CallFuncN::create(CC_CALLBACK_1(PlayLayer::spriteMoveFinished,this,(void*)adventurer));
adventurer->moveAction = Sequence::create(actionMove,actionMoveDone,NULL);
adventurer->charSprite->runAction(adventurer->walkAction);
adventurer->charSprite->runAction(adventurer->moveAction);
this->addChild(adventurer->charSprite)
charArray.pushBack(adventurer);
}
}
void PlayLayer::spriteMoveFinished(Node* sender, void* adv)
{
auto adventurer = (Adventurer*)adv;
Size s = Director::getInstance()->getWinSize();
int minY = adventurer->charSprite->getContentSize().height / 2;
int maxY = s.height - adventurer->charSprite->getContentSize().height / 2;
int rangeY = maxY - minY;
int actualY = (CCRANDOM_0_1() * rangeY) + minY;
int minX = -300;
int maxX = 0;
int rangeX = maxX - minX;
int actualX = (CCRANDOM_0_1() * rangeX) + minX;
adventurer->charSprite->setPosition(ccp(actualX, actualY));
adventurer->stopAction(adventurer->moveAction);
adventurer->charSprite->runAction(adventurer->moveAction);
}
void PlayLayer::gameLogic(float dt)
{
this->addAdventurer();
}
void PlayLayer::onEnter()
{
Layer::onEnter();
auto listener = EventListenerTouchOneByOne::create();
listener->setSwallowTouches(true);
listener->onTouchBegan = CC_CALLBACK_2(PlayLayer::onTouchBegan, this);
listener->onTouchMoved = CC_CALLBACK_2(PlayLayer::onTouchMoved, this);
listener->onTouchEnded = CC_CALLBACK_2(PlayLayer::onTouchEnded, this);
auto dispatcher = Director::getInstance()->getEventDispatcher();
dispatcher->addEventListenerWithSceneGraphPriority(listener, this);
}
void PlayLayer::onTouchEnded(Touch* touch,Event* event)
{
Point touchLocation = this->convertToWorldSpace(this->convertTouchToNodeSpace(touch));
Point moveVector = touchLocation - dragon->getPosition();
float distanceToMove = ccpLength(moveVector);
float moveAngle = ccpToAngle(moveVector);
float cocosAngle = CC_RADIANS_TO_DEGREES(-1 * moveAngle);
float dragonVelocity = 480.0 / 3.0;
float moveDuration = distanceToMove / dragonVelocity;
cocosAngle += 23;
if (cocosAngle < 0)
{
cocosAngle += 360;
}
int runAnim = (int)((cocosAngle)/45);
dragon->stopAction(flyAction);
this->flyAction = flyActionArray.at(runAnim);
dragon->runAction(flyAction);
this->moveAction = Sequence::create(MoveTo::create(moveDuration,touchLocation),NULL);
dragon->runAction(moveAction);
}
OK,千万别头疼--接下来我会一点点分解:
bool PlayLayer::init()
{
if (!Layer::init())
{
return false;
}
auto background = Sprite::create("Terrain.png");
background->setPosition(ccp(160,240));
this->addChild(background);
auto texture = TextureCache::getInstance()->addImage("dragon.png");
auto sheet = SpriteBatchNode::create("dragon.png",10);
this->addChild(sheet, 0, ktagSpriteSheet);
Size s = Director::getInstance()->getWinSize();
Vector<SpriteFrame*> animFrames;
for (int i = 0; i < 8; i++)
{
animFrames.clear();
for (int j = 0; j < 10; j++)
{
auto frame = SpriteFrame::createWithTexture(texture, Rect(j * 75, i * 70, 75, 70), false, Director::getInstance()->getVisibleOrigin(), Size(75, 70));
animFrames.pushBack(frame);
}
auto animation = Animation::createWithSpriteFrames(animFrames, 0.1f);
auto animate = Animate::create(animation);
auto seq = Sequence::create(animate, NULL);
this->flyAction = RepeatForever::create(seq);
flyActionArray.pushBack(this->flyAction);
}
auto frame1 = SpriteFrame::createWithTexture(texture, Rect(0, 0, 75, 70), false, Director::getInstance()->getVisibleOrigin(), Size(75, 70));
this->dragon = Sprite::createWithSpriteFrame(frame1);
dragon->setPosition(ccp(s.width/2-80,s.height/2));
sheet->addChild(dragon);
this->flyAction = flyActionArray.at(0);
dragon->runAction(flyAction);
texture = TextureCache::getInstance()->addImage("adventurer.png");
spriteSheet = SpriteBatchNode::createWithTexture(texture,100);
this->addChild(spriteSheet,0,ktagSpriteSheet);
this->schedule(schedule_selector(PlayLayer::gameLogic), 1.0f);
return true;
}
好,首先看到“init”函数,它和我们之前的adventurer 类一样,先调super init,如果失败的话,就直接返回nil。然后我们添加了一张背景图片叫做"Terrain.png"并把它放置在屏幕的中心(因为我们知道图片的默认中心点anchorPoint是0.5,0.5)。然后直接把它加到当前层中。
接下来,我们初始化纹理和SpriteBatchNode--首先把"adventurer.png"加载到CCTexture2D变量中,然后使用createWithTexture来建立一个精灵表单。(我们也可以用create这个函数来建立SpriteBatchNode,但是,我想向你展示另外一种方法)。然后,我们把spritesheet加到Layer中。之后,我们所有的精灵,如果作为孩子加到SpriteBatchNode中的话,就可以得到“批处理”。
最后,我们触发一个回调函数gamelogic,它会每隔1秒钟回调一次。函数如下所示:
void PlayLayer::gameLogic(float dt)
{
this->addAdventurer();
}
我们将使用这个函数,每隔一秒钟创建一个新的adventurer 对象。
接下来,看看AddAventurer函数。这个函数不仅仅创建一个新的角色,同时还会使之移动并播放相应方向行走的动画。
void PlayLayer::addAdventurer()
{
Vector<SpriteFrame*>animFrames;
animFrames.clear();
for (int i = 0; i < 9; i++)
{
auto *frame = SpriteFrame::createWithTexture(this->texture, Rect(i * 16, 0, 16, 29));
animFrames.pushBack(frame);
}
上面的代码我们之前已经见过了,我们只是为walking动画存储了9个动画帧(SpriteFrames)。
auto adventurer = Adventurer::create();
if (adventurer != NULL)
{
auto *frame1 = SpriteFrame::createWithTexture(this->texture, Rect(0, 0, 19, 29));
adventurer->charSprite = Sprite::createWithSpriteFrame(frame1);
接下来,我们创建一个新的adventurer 实例,然后把charSprite成员初始化为第一个动画帧,调用的方法是createWithSpriteFrame。
Size s = Director::getInstance()->getWinSize();
int minY = adventurer->charSprite->getContentSize().width/2;
int maxY = s.height - adventurer->charSprite->getContentSize().height / 2;
int rangeY = maxY - minY;
int actualY = (CCRANDOM_0_1() * rangeY) + minY;
int minX = -300;
int maxX = 0;
int rangeX = maxX - minX;
int actualX = (CCRANDOM_0_1() * rangeX) + minX;
adventurer->charSprite->setPosition(ccp(actualX, actualY));
好了,即使我们的精灵按照粒子数量去增加,所有的精灵刚开始的位置都是在左下角,除非我们人为改变它们的位置。因此,上面的代码就是产生一个随机坐标,同时又要保证这个随机坐标在屏幕范围之内。然后把这个随机坐标点赋值给adventurer。
auto *animation = Animation::createWithSpriteFrames(animFrames,0.2);
auto *animate = Animate::create(animation);
auto *seq = Sequence::create(animate,NULL);
adventurer->walkAction = RepeatForever::create(seq);
auto actionMove = MoveTo::create(10.0f,ccp(s.width + 200, actualY));
auto actionMoveDone = CallFuncN::create(CC_CALLBACK_1(PlayLayer::spriteMoveFinished,this,(void*)adventurer));
adventurer->moveAction = Sequence::create(actionMove,actionMoveDone,NULL);
adventurer->charSprite->runAction(adventurer->walkAction);
adventurer->charSprite->runAction(adventurer->moveAction);
this->addChild(adventurer->charSprite);
charArray.pushBack(adventurer);
}
}
addAdventurer方法的最后一个部分就是处理角色在屏幕上面的行走和移动。我们把之前存储CCSpriteFrame 的animFrames拿过来,然后把它转换成动画。(每个动画帧之间的间隔是0.2秒,整个动画差不多就要2秒的时间来运行完)。然后我们把这个动画放到一个sequence 动作中(使用CCSequence 类),最后,我们使用CCRepeatForever创建walkAction,并把它赋值给adventurer。
我们现在已经可以让角色有行走的动画了,但是,我们还想让它实际移动。所以,我们需要创建另外一个action,叫做CCMoveTo 。并且使用CCSequence类把它与一个回调函数关联起来。当CCMoveTo结束的时候,就运行CCCallFuncN指定的回调函数。
Side Note: 如果你想指定带一个参数的函数,那么就使用CCCallFuncN--它会把Sprite本身传递进去,通过sender参数传递:
auto actionMoveDone = CallFuncN::create(this,callfuncN_selector(PlayLayer::spriteMoveFinished));
如果你不想让任何参数传递的话,就使用CCCallFunc函数。
现在,我们还剩下一件事情没有涉及了,就是之前CCMove结束的时候,通过CCCallFuncND指定的回调函数,如下所示:void PlayLayer::spriteMoveFinished(Node* sender, void* adv)
{
auto adventurer = (Adventurer*)adv;
Size s = Director::getInstance()->getWinSize();
int minY = adventurer->charSprite->getContentSize().height / 2;
int maxY = s.height - adventurer->charSprite->getContentSize().height / 2;
int rangeY = maxY - minY;
int actualY = (CCRANDOM_0_1() * rangeY) + minY;
int minX = -300;
int maxX = 0;
int rangeX = maxX - minX;
int actualX = (CCRANDOM_0_1() * rangeX) + minX;
adventurer->charSprite->setPosition(ccp(actualX, actualY));
adventurer->stopAction(adventurer->moveAction);
adventurer->charSprite->runAction(adventurer->moveAction);
}
这里再重复解释上面的代码的话,就有点烦人了。简言之,就是在CCMoveTo结束之后,随机再生成一个x,y坐标,然后让advertuere移动到这个位置去,再开始行走的动画。
原文链接地址:http://www.iphonegametutorials.com/2010/09/14/cocos2d-sprite-tutorial-part-3/