本文原创版权归 博客园 Terry_龙 所有,转载请标明原创作者及出处,以示尊重!
作者:Terry_龙
原文:http://www.cnblogs.com/TerryBlog/archive/2012/11/07/2759506.html
记得以前学习XNA游戏开发的时候,操作精灵角色的攻击或者行走动作都是给出特定的几张序列图或者一张长序列图然后通过切割来作一帧一帧的切片动画播放。
关于精灵sprite我从网上摘录了一段话如下
说白一点,精灵就是将图形资源加载到内存中,并根据游戏需要将其显示到屏幕中的工具,游戏中大到背景、UI,小到NPC、道具,只要是用图片展示的,都是精灵或它的子类。从技术上讲,精灵是一个可以不断变化的图片,这些变化包括:位置移动、旋转、缩放、换帧(就是像动画片一样播放几张连续的图片,每帧换一张,形成一个动画效果)在cocos2d-x 中精灵的关系如下图
该图来源于:http://www.xuanyusong.com/archives/1370 更多的相关知识可以来这里参考。
精灵序列图来源自:http://www.cnblogs.com/nowpaper/archive/2012/10/22/2733389.html ,使用他的三国兵种素材。用到的素材如下
图片命名也是根据nowpaper的示例命名,动画示例代码如下:
图片如下
上图序列图是一张序列图,可以看出分为横、竖各4个精灵动画帧,依次分割可以这么分
0-0,0-1,0-2,0-3
1-0,1-1,1-2,1-3
2-0,2-1,2-2,2-3
3-0,3-1,3-2,3-3
分割分部代码为
以上两种稍微复杂,cocos2d-x还可以使用一个叫TexturePackerGUI的工具,可以将多张序列图生成一个png的集合图和一个*.plist的文件,工具下载地址:TexturePackGUI
这个工具的使用,网上有很多教的,使用也方便多摸索下就可以了,使用该工具生成后的文件如下:
之后你就可以将它放入内存中操纵了,可能你会发现生成后的图片比之前的几张序列图要小好多,所以为了更快的游戏程序建议多使用该工具,动画代码如下:
好啦,来看看最后的效果图
源码下载:https://github.com/terryyhl/SpriteAnimation.git
好运。
原文:http://www.cnblogs.com/TerryBlog/archive/2012/11/11/2765413.html
上一篇文章讲述了利用cocos2d-x构建精灵的动画效果,今天打算以此为引子,创建一个在移动时同时指挥角色到我手指触摸的移动地点,那么就开始吧。
本篇要点
角色缓存使用CCSpriteFrameCache ,配合CCSpriteBatchNode将图片*.plist和*.png 加载进内存,方便以后调用。
以上为动作序列图,图片名称为:sg.png.图片来源于博客园:nowpaper.
角色缓存代码如下:
以上代码,CCSpriteFrameCache负责加载sg.plist,CCSpriteBatchNode负责加载sg.png,然后创建一个精灵指定初始化位置和精灵纹理,并添加进CCSpriteBatchNode。通过上面的代码即可以将一个plist序列图加载进缓存了,你要做的就是将这些缓存的数据拿出来操作它。
上面己经将数据加载进缓存了,可以使用其中的那些节点来制作动画缓存了。
缓存动画使用 CCAnimationCache,该动画同样需要使用到plist文件,代码如下
在将plist文件添加完后,即可以通过动画Animation将每一个动画的Animation添加进CCAnimationCache了,这里我写了一个函数,代码见下方
设定好让程序允许Touch之后,在回调函数ccTouchesEnded 里面通过捕获触摸位置指定精灵移动,代码见下方
ok了,精灵移动了,但你会发现,你想让精灵移动的时候不是一整张图片移动,而是边移动边两只脚在走路的移动,就像是我们人类一样,是走着过去的,而不是幽灵飘过去的,那么,我们要做些什么呢?
其实,很简单 ,只要记住要精灵移动的时候即:MoveTo时,同时再让精灵执行一个动作即walk的动作,代码如下:
当到达指定地点时,希望让角色以站立的姿势站在屏幕上,这时我们需要在moveTo的callback函数里面调用,让其停止当前action,并重新执行站立的action,代码如下:
全部代码如下:
using namespace cocos2d; using namespace CocosDenshion; #define LOG_TAG "main" ||- function #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__) CCScene* HelloWorld::scene() { // 'scene' is an autorelease object CCScene *scene = CCScene::create(); // 'layer' is an autorelease object HelloWorld *layer = HelloWorld::create(); // add layer as a child to scene scene->addChild(layer); // return the scene return scene; } // on "init" you need to initialize your instance bool HelloWorld::init() { ////////////////////////////// // 1. super init first if ( !CCLayer::init() ) { return false; } this->setTouchEnabled(true); CCSize size = CCDirector::sharedDirector()->getWinSize(); CCAnimationCache::purgeSharedAnimationCache(); CCAnimationCache *animCache = CCAnimationCache::sharedAnimationCache(); animCache->addAnimationsWithFile("sg.plist"); cache=CCSpriteFrameCache::sharedSpriteFrameCache(); cache->addSpriteFramesWithFile("sg.plist"); sprite=CCSprite::createWithSpriteFrameName("A1_6.png"); sprite->setPosition(ccp(size.width-sprite->getContentSize().width,size.height/2)); spriteBatchNode=CCSpriteBatchNode::create("sg.png"); spriteBatchNode->addChild(sprite); addChild(spriteBatchNode); cache->addSpriteFramesWithFile("hero.plist"); hero=CCSprite::createWithSpriteFrameName("Hero02_0.png"); hero->setPosition(ccp(hero->getContentSize().width,size.height/2)); heroBatchNode=CCSpriteBatchNode::create("hero.png"); heroBatchNode ->addChild(hero); hero->setFlipX(true); addChild(heroBatchNode); attackArray =CCArray::createWithCapacity(4); char attackName[20]; for(int i=0;i<4;i++){ sprintf(attackName, "A1_%d.png",i); CCSpriteFrame* frame =cache->spriteFrameByName(attackName); attackArray->addObject(frame); } CCAnimation *attackAnimation =CCAnimation::createWithSpriteFrames(attackArray,0.2f); CCAnimationCache::sharedAnimationCache()->addAnimation(attackAnimation, "attack"); attackArray->removeAllObjects(); standArray= CCArray::createWithCapacity(1); char standName[20]; for(int i=6;i<7;i++){ sprintf(standName, "A1_%d.png",i); CCSpriteFrame* frame =cache->spriteFrameByName(standName); standArray->addObject(frame); } CCAnimation *standAnimation =CCAnimation::createWithSpriteFrames(standArray,0.2f); CCAnimationCache::sharedAnimationCache()->addAnimation(standAnimation, "stand"); standArray->removeAllObjects(); return true; } void HelloWorld::moveDone(){ // sprite->stopAllActions(); CCAnimationCache *animCache = CCAnimationCache::sharedAnimationCache(); CCAnimation *standAnimation = animCache->animationByName("stand"); standAnimation->setRestoreOriginalFrame(true); CCAnimate *standAni=CCAnimate::create(standAnimation); CCActionInterval* s=(CCActionInterval*)(CCSequence::create(standAni, standAni->copy()->autorelease(), NULL )); CCAction *frameAction=CCRepeatForever::create(s); sprite->runAction(frameAction); } CCAction* HelloWorld::createAction(int begin,int end,char* cacheActionName,CCPoint point){ CCAnimationCache *animCache = CCAnimationCache::sharedAnimationCache(); CCArray *array = CCArray::createWithCapacity(end-begin); char name[20]; for(int i = begin ;i<end;i++){ sprintf(name,"A1_%d.png",i); CCSpriteFrame* frame =cache->spriteFrameByName(name); array->addObject(frame); } CCAnimation *plistAnimation = CCAnimation::createWithSpriteFrames(array,0.2f); CCAnimationCache::sharedAnimationCache()->addAnimation(plistAnimation, cacheActionName); array->removeAllObjects(); CCAnimation *animation = animCache->animationByName(cacheActionName); animation->setRestoreOriginalFrame(true); CCAnimate *ani=CCAnimate::create(animation); CCActionInterval* plistSeq=(CCActionInterval*)(CCSequence::create(ani, CCFlipX::create(point.x>0? true:false), ani->copy()->autorelease(), NULL )); return CCRepeatForever::create(plistSeq); } void HelloWorld::registerWithTouchDispatcher(){ CCDirector::sharedDirector()->getTouchDispatcher()->addStandardDelegate(this,0); } void HelloWorld::ccTouchesEnded(cocos2d::CCSet * touches,cocos2d::CCEvent * event){ sprite->stopAllActions(); CCTouch* touch=(CCTouch*)(touches->anyObject()); CCPoint location = touch ->getLocation(); float offX=location.x-sprite->getPosition().x; float offY=location.y-sprite->getPosition().y; walkAction=createAction(4,6,"move",ccp(offX,offY)); sprite->setFlipX(offX>0?true:false); float realX=offY/offX; CCPoint realDeast =ccp(location.x,location.y); CCActionInterval *actionTo=CCMoveTo::create(2.2f,realDeast); CCAction *moveToAction=CCSequence::create( actionTo, CCCallFunc::create(this,callfunc_selector(HelloWorld::moveDone)), NULL ); sprite->runAction(walkAction); sprite->runAction(moveToAction); } void HelloWorld::menuCloseCallback(CCObject* pSender) { CCDirector::sharedDirector()->end(); #if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS) exit(0); #endif
CCAnimation *attackAnimation =CCAnimation::createWithSpriteFrames(attackArray,0.2f); 与
最后实现的效果如下:
由于是在ubuntu下开发,好像没有什么抓取屏幕gif 图片的软件可用,简单截一屏!!!
代码没有做任何的处理,很多多余的代码,做个DEMO可以看看就行!!!
代码下载:https://github.com/terryyhl/SpriteAnimation.git
来自 博客园 居家懒人
原文:http://www.cnblogs.com/JD85/archive/2013/01/02/2842271.html
粗略写了个Player类,用来测试人物的待机动作和奔跑动作的播放,以及两种动作的切换播放。
1,这里要用到plist文件,对于plist,我目前的理解就是:plist和xml很像,可以说就是xml, 不过在mac下面就成了plist,美术资源如果是一张有9个帧动作的图片,那么plist文件里就应该有9个dict节点来分别对应这9个动作,每个节点里的属性包括精灵大小、需要渲染的纹理矩形的左上角和右下角这两个点的坐标、坐标偏移值、精灵单张原始图片的大小...
最开始时是想通过setPostition和 setAnothorPoint来解决人物的原点位置设置,但发现可以直接在plist里设置offset偏移量来决定原点位置。
plist文件里的spriteOffset属性正好可以用来设置原点的位置。
这里给出我自己算出的spriteOffset坐标公式:
CCSpriteBatchNode, CCSpriteFrameCache, CCSpriteFrame, CCAnimation
3, 代码中包括触屏之后除了人物的动作切换,还有位移、水平转向的处理,代码借鉴了官方test里的例子,贴心的2dx团队呀有么有!!!
4,新手要注意,draw方法不是主动去调用的,这里是重写了父类的draw方法,在这里绘制了一下人物的脚下画了个十字以以表示原点位置,方便测试,
ccDrawLine()方法就是直接调用openGL ES的接口在屏幕上绘制一条线。那么draw方法是被谁调用的呢,是cocos2d-x的UI主线程。
// // Player.h // PlayerActionTest // // Created by 嘉定 on 12-12-25. // // #ifndef __PlayerActionTest__Player__ #define __PlayerActionTest__Player__ #include "cocos2d.h" USING_NS_CC; class Player : public CCSprite { public: Player(); virtual ~Player(); virtual bool init(); virtual void draw(); typedef enum { state_idle = 1, state_run = 2, state_sit = 3, state_UnKnown = 4 } ActionState; typedef enum { headward_left = 1, headward_right = 2, headward_Unknow = 3 }HeadwardType; //跑向一个点,达到目标后是否回调 void runTo(CCPoint targetPos,bool callback); //播放某个动作动画 void playAction(ActionState state); //当前动作状态 ActionState currentActionState; //当前朝向 HeadwardType currentHeadward; CREATE_FUNC(Player); private: //动画批处理节点 CCSpriteBatchNode *idleSpriteBatch; CCSpriteBatchNode *runSpriteBatch; //侦动画缓存 CCSpriteFrameCache *idleFrameCache; CCSpriteFrameCache *runFrameCache; CCSprite *idleSprite; CCSprite *runSprite; void stopCurrentAction(); //播放待机动画 void idle(); //播放奔跑动画 void run(); //跑到目标点之后回调 //handler void runToCallBack(); }; #endif /* defined(__PlayerActionTest__Player__) */
// // Player.cpp // PlayerActionTest // // Created by 嘉定 on 12-12-25. // // #include "Player.h" Player::Player(): idleFrameCache(NULL), runFrameCache(NULL), idleSpriteBatch(NULL), runSpriteBatch(NULL), idleSprite(NULL), runSprite(NULL), currentActionState(Player::state_UnKnown), currentHeadward(Player::headward_Unknow) { } Player::~Player() { } bool Player::init() { if(!CCSprite::init()) { return false; } //默认朝向右边 currentHeadward = headward_right; //init total action FrameCache idleFrameCache = CCSpriteFrameCache::sharedSpriteFrameCache(); idleFrameCache->addSpriteFramesWithFile("player/heping.plist","player/heping.png"); runFrameCache = CCSpriteFrameCache::sharedSpriteFrameCache(); runFrameCache->addSpriteFramesWithFile("player/pao.plist", "player/pao.png"); return true; } void Player::runTo(CCPoint targetPos,bool callback) { this->stopAllActions(); //目前用CCMoveTO,需要设定一个从当前位置到目标位置的移动时间,那么应提前算出移动所需的时间 const float SPEED = 12; float playerX = this->getPosition().x; float playerY = this->getPosition().y; if(targetPos.x >= playerX) { this->currentHeadward = headward_right; } else { this->currentHeadward = headward_left; } this->playAction(state_run); float disX = playerX - targetPos.x; float disY = playerY - targetPos.y; const float DIS = sqrtf(disX * disX + disY * disY); float moveTime = (DIS / SPEED) / 60; //但是人物的移动时间要按照从当前点到touch点的真实距离来计算时间 CCAction *action = NULL; if(callback) { action = CCSequence::create( CCMoveTo::create(moveTime, targetPos), CCCallFunc::create(this, callfunc_selector(Player::runToCallBack)), NULL); } else { action = CCSequence::create(CCMoveTo::create(moveTime, targetPos),NULL); } this->runAction(action); //角度转向 // // float at = (float) CC_RADIANS_TO_DEGREES(atanf(disX/disY)); // // if(disX < 0) // { // if(disY < 0) // at = 180 + fabs(at); // else // at = 180 - fabs(at); // } // this->runAction(CCRotateTo::create(1, at)); } void Player::playAction(ActionState state) { if(currentActionState == state) { //这里逻辑有点细微,当正在向左跑时,如果点击了人物右边屏幕,那么不回重新创建一次奔跑的动画变量,但是人物时需要反转的 //idle状态不需要考虑这一点,跑到了就会idle,这时会自动根据之前奔跑的朝向来设定。 if(currentActionState == state_run) { if(runSprite->isFlipX() && currentHeadward == headward_right) { runSprite->setFlipX(false); } else if(!runSprite->isFlipX() && currentHeadward == headward_left) { runSprite->setFlipX(true); } } return; } if(state == state_idle) { idle(); } else if(state == state_run) { run(); } else { } } void Player::draw() { ccDrawColor4B(255, 0, 0, 255); glLineWidth(6.0f); ccDrawLine(ccp(-20,0),ccp(20,0)); ccDrawLine(ccp(0,15),ccp(0,-15)); } void Player::idle() { stopCurrentAction(); idleSprite = CCSprite::createWithSpriteFrameName("heping_01"); if(this->currentHeadward == headward_left) { idleSprite->setFlipX(true); } else if(this->currentHeadward == headward_right) { idleSprite->setFlipX(false); } CCSize idleSize = idleSprite->getContentSize(); idleSpriteBatch = CCSpriteBatchNode::create("player/heping.png"); idleSpriteBatch->addChild(idleSprite); addChild(idleSpriteBatch); //动画帧数组 CCArray *animatFrames = CCArray::createWithCapacity(8); char str[100] = {0}; for(int i = 1;i < 8;++i) { sprintf(str, "heping_%02d",i); CCSpriteFrame *frame = idleFrameCache->spriteFrameByName(str); animatFrames->addObject(frame); } CCAnimation *animation = CCAnimation::createWithSpriteFrames(animatFrames,0.2f); idleSprite->runAction(CCRepeatForever::create(CCAnimate::create(animation))); currentActionState = state_idle; } void Player::run() { stopCurrentAction(); runSprite = CCSprite::createWithSpriteFrameName("run_01"); if(this->currentHeadward == headward_left) { runSprite->setFlipX(true); } else if(this->currentHeadward == headward_right) { runSprite->setFlipX(false); } CCSize runSize = runSprite->getContentSize(); runSpriteBatch = CCSpriteBatchNode::create("player/pao.png"); runSpriteBatch->addChild(runSprite); addChild(runSpriteBatch); CCArray *animatFrames = CCArray::createWithCapacity(7); char str[100] = {0}; for(int i = 1;i < 7;++i) { sprintf(str, "run_%02d",i); CCSpriteFrame *frame = runFrameCache->spriteFrameByName(str); animatFrames->addObject(frame); } CCAnimation *animation = CCAnimation::createWithSpriteFrames(animatFrames,0.08f); runSprite->runAction(CCRepeatForever::create(CCAnimate::create(animation))); currentActionState = state_run; } void Player::stopCurrentAction() { if(currentActionState == state_idle) { idleSprite->stopAllActions(); idleSpriteBatch->removeChild(idleSprite,true); removeChild(idleSpriteBatch,true); } else if(currentActionState == state_run) { runSprite->stopAllActions(); runSpriteBatch->removeChild(runSprite, true); removeChild(runSprite,true); } else { } } void Player::runToCallBack() { this->playAction(state_idle); }
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>frames</key> <dict> <key>run_01</key> <dict> <key>spriteSize</key> <string>{151, 127}</string> <key>textureRect</key> <string>{{0, 0}, {151,127}}</string> <key>spriteOffset</key> <string>{-13, 42}</string> <key>textureRotated</key> <false/> <key>spriteSourceSize</key> <string>{151,127}</string> <key>aliases</key> <array> <string>run_01</string> </array> </dict> <key>run_02</key> <dict> <key>spriteSize</key> <string>{151,127}</string> <key>textureRect</key> <string>{{151, 0}, {302,127}}</string> <key>spriteOffset</key> <string>{-13, 42}</string> <key>textureRotated</key> <false/> <key>spriteSourceSize</key> <string>{151,127}</string> <key>aliases</key> <array> <string>run_02</string> </array> </dict> <key>run_03</key> <dict> <key>spriteSize</key> <string>{151,127}</string> <key>textureRect</key> <string>{{302, 0}, {453,127}}</string> <key>spriteOffset</key> <string>{-13, 42}</string> <key>textureRotated</key> <false/> <key>spriteSourceSize</key> <string>{151,127}</string> <key>aliases</key> <array> <string>run_03</string> </array> </dict> <key>run_04</key> <dict> <key>spriteSize</key> <string>{151,127}</string> <key>textureRect</key> <string>{{453, 0}, {604,127}}</string> <key>spriteOffset</key> <string>{-13, 42}</string> <key>textureRotated</key> <false/> <key>spriteSourceSize</key> <string>{151,127}</string> <key>aliases</key> <array> <string>run_04</string> </array> </dict> <key>run_05</key> <dict> <key>spriteSize</key> <string>{151,127}</string> <key>textureRect</key> <string>{{604, 0}, {755,127}}</string> <key>spriteOffset</key> <string>{-13, 42}</string> <key>textureRotated</key> <false/> <key>spriteSourceSize</key> <string>{151,127}</string> <key>aliases</key> <array> <string>run_05</string> </array> </dict> <key>run_06</key> <dict> <key>spriteSize</key> <string>{151,127}</string> <key>textureRect</key> <string>{{755, 0}, {906,127}}</string> <key>spriteOffset</key> <string>{-13, 42}</string> <key>textureRotated</key> <false/> <key>spriteSourceSize</key> <string>{151,127}</string> <key>aliases</key> <array> <string>run_06</string> </array> </dict> <key>run_07</key> <dict> <key>spriteSize</key> <string>{151,127}</string> <key>textureRect</key> <string>{{906, 0}, {1057,127}}</string> <key>spriteOffset</key> <string>{-13, 42}</string> <key>textureRotated</key> <false/> <key>spriteSourceSize</key> <string>{151,127}</string> <key>aliases</key> <array> <string>run_07</string> </array> </dict> </dict> <key>metadata</key> <dict> <key>format</key> <integer>3</integer> <key>size</key> <string>{1057, 127}</string> </dict> </dict> </plist>