粗略寫了個Player類,用來測試人物的待機動作和奔跑動作的播放,以及兩種動作的切換播放。
1,這里要用到plist文件,對於plist,我目前的理解就是:plist和xml很像,可以說就是xml, 不過在mac下面就成了plist,美術資源如果是一張有9個幀動作的圖片,那么plist文件里就應該有9個dict節點來分別對應這9個動作,每個節點里的屬性包括精靈大小、需要渲染的紋理矩形的左上角和右下角這兩個點的坐標、坐標偏移值、精靈單張原始圖片的大小...
最開始時是想通過setPostition和 setAnothorPoint來解決人物的原點位置設置,但發現可以直接在plist里設置offset偏移量來決定原點位置。
plist文件里的spriteOffset屬性正好可以用來設置原點的位置。
這里給出我自己算出的spriteOffset坐標公式:
X = 人物單幀圖片寬度的一半 減去 注冊點到圖片的右邊距 (得到值可能是負數,就要負數,不要絕對值)
Y = 人物單幀圖片高度的一半 減去 注冊點到圖片的底邊
2,cocos2d-x真心很贊,圖片紋理渲染優化都有處理,使用這種紋理緩存機制,可以避免重復渲染相同的紋理。
這里涉及到幾個類:
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>