cocos2dx裡骨骼動畫代碼在cocos -> editor-support -> cocostudio文件夾中,win下通過篩選器,文件結構如下。(mac下沒有分,是整個一坨)
armature(目錄): animation(目錄):動畫控制相關。 CCProcessBase(文件): ProcessBase(類):CCTween和ArmatureAnimation的基類。 CCTWeen(文件): Tween(類):控制flash裡一個layer的動畫。 CCArmatureAnimation(文件): ArmatureAnimation(類):控制整個動畫,內有多個Tween。 datas(目錄):xml或json轉成c++中直接用的數據結構。 CCDatas(文件): BaseData(類):BoneData、FrameData的基類,包含大小位置顏色等信息。 DisplayData(類): SpriteDisplayData、ArmatureDisplayData、ParticleDisplayData的基類。 SpriteDisplayData(類):骨骼中的顯示數據。 ArmatureDisplayData(類): ParticleDisplayData(類): BoneData(類):單個骨骼數據,flash中一個layer是一個骨骼。 ArmatureData(類):骨骼數據,整個骨骼結構數據。 FrameData(類):關鍵幀數據。 MovementBoneData(類):帶有關鍵幀的骨骼數據。 MovementData(類):一個完整動畫數據。 AnimationData(類):組動畫數據,包含多個MovementData。 ContourData(類): TextureData(類):顯示圖片數據。 utils(目錄): CCArmatureDataManager(文件): RelativeData(類): ArmatureDataManager(類):管理ArmatureData、AnimationData、TextureData。 CCArmatureDefine(文件): CCDataReaderHelper(文件): _AsyncStruct(類): _DataInfo(類): DataReaderHelper(類):這正解析xml或json的類。 CCSpriteFrameCacheHelper(文件): SpriteFrameCacheHelper(類): CCTransformHelp(文件): TransformHelp(類):矩陣運算。 CCUtilMath(文件): CCArmature(文件): Armature(類):控制整個骨骼動畫,內有ArmatureAnimation和ArmatureData。 CCBone(文件): Bone(類):骨骼控制類 display(目錄):顯示的圖片管理。 CCBatchNode(文件): BatchNode(類): CCDecorativeDisplay(文件): DecorativeDisplay(類): CCDisplayFactory(文件): DisplayFactory(類): CCDisplayManager(文件): DisplayManager(類): CCSkin(文件): Skin(類): physics(目錄):物理引擎相關,不分析。 ColliderFilter(文件): ColliderFilter(類): ColliderBody(類): ColliderDetecotor(類)
再來看下數據相關的UML,總體來說,就是ArmatureDataManager依賴DataReaderHelper把flash導出的xml文件解析成程序直接用的XXData,XXData對應於xml的某個節點,比如FrameData就對應於<f>節點(<animaton><mov><b><f>)。
BaseData
BaseData:用來表示骨骼或幀的位置、旋轉、顏色、縮放。
BaseData.h
1 class BaseData : public cocos2d::Ref 2 { 3 public: 4 //Calculate two BaseData's between value(to - from) and set to self 5 virtual void subtract(BaseData *from, BaseData *to, bool limit); 6 public: 7 //位置,xml的x,y 8 float x; 9 float y; 10 //xml中z 11 int zOrder; 12 //旋轉,xml的kX,kY 13 float skewX; 14 float skewY; 15 //縮放,xml的cX,cY 16 float scaleX; 17 float scaleY; 18 //啥?? 19 float tweenRotate; 20 //顏色的變化屬性 21 bool isUseColorInfo; 22 int a, r, g, b; 23 };
作為FrameData和BoneData的基類,提供骨骼的狀態信息。從下文可知BoneData對應xml中的<armature<b>>中的b節點,FrameData對應xml中的<f>節點,BoneData和FrameData都有
<x,y,kX,kY,cX,cY,pX,pY,z>
等屬性,BaseDa代表了這些屬性。
BoneData對應xml中的<armature<b>>中的b節點
1 class BoneData : public BaseData 2 { 3 public: 4 void addDisplayData(DisplayData *displayData); 5 DisplayData *getDisplayData(int index); 6 public: 7 std::string name; //! the bone's name 8 std::string parentName; //! the bone parent's name 9 //! save DisplayData informations for the Bone 10 cocos2d::Vector<DisplayData*> displayDataList; 11 //仿射變換,程序裡好像沒用這個屬性 12 cocos2d::AffineTransform boneDataTransform; 13 };
BoneData裡有displayDataList,用來放這個骨頭上的皮膚(就是DisplayData), DisplayData對應xml節點中的<b<d>>節點,一個BoneData裡可以有多個皮膚,換裝等功能需要多個皮膚。
FrameData對應xml中的<f>節點,就是flash裡的關鍵幀信息。
1 class FrameData : public BaseData 2 { 3 public: 4 int frameID; 5 //xml中dr,這一幀長度 6 int duration; 7 //不知要他干啥 8 bool isTween; 9 //xml中dI,顯示哪個圖 10 int displayIndex; 11 };
DisplayData是SpriteDisplayData、ArmatureDisplayData、ParticleDisplayData的父類,用來表示展示節點信息。
ArmatureData是對應<armature>節點,裡面有這個骨骼的所有骨頭,可以看成骨骼動畫的骨骼。
1 class ArmatureData : public cocos2d::Ref 2 { 3 public: 4 //添加骨骼信息 5 void addBoneData(BoneData *boneData); 6 BoneData *getBoneData(const std::string& boneName); 7 public: 8 std::string name; 9 //多個骨頭信息 10 cocos2d::Map<std::string, BoneData*> boneDataDic; 11 float dataVersion; 12 };
AnimationData對應<animation>節點,裡面有多個MovementData,MovementData(下面介紹)對應xml中的mov,為flash中的一個帶幀標簽的動畫。
1 class AnimationData : public cocos2d::Ref 2 { 3 public: 4 void addMovement(MovementData *movData); 5 MovementData *getMovement(const std::string& movementName); 6 ssize_t getMovementCount(); 7 public: 8 //<animation name="Dragon">中的name 9 std::string name; 10 //所有帶幀標簽的動畫map 11 cocos2d::Map<std::string, MovementData*> movementDataDic; 12 //所有帶幀標簽的動畫名 13 std::vector<std::string> movementNames; 14 };
MovementData對應xml中<animation<mov>>, 其中有所有的帶幀信息的骨骼MovementBoneData(mov中的b)。
1 class MovementData : public cocos2d::Ref 2 { 3 public: 4 void addMovementBoneData(MovementBoneData *movBoneData); 5 MovementBoneData *getMovementBoneData(const std::string& boneName); 6 public: 7 std::string name; 8 //xml 中 dr 9 int duration; 10 //這怎麼有個scale?? 11 float scale; 12 //xml中to 13 int durationTo; 14 //xml中drTW 15 int durationTween; 16 //xml中lp 17 bool loop; 18 //帶幀信息的骨骼 19 cocos2d::Map<std::string, MovementBoneData*> movBoneDataDic; 20 };
MovementBoneData對應xml中<mov<b>>的b,裡面有frameList,即為關鍵幀信息。
1 class MovementBoneData : public cocos2d::Ref 2 { 3 void addFrameData(FrameData *frameData); 4 FrameData *getFrameData(int index); 5 public: 6 //xml中的dl 7 float delay; 8 //xml中的sc 9 float scale; 10 //這個和MovementData中的duration是不是一個?? 11 float duration; 12 std::string name; 13 //關鍵幀信息 14 cocos2d::Vector<FrameData*> frameList; 15 };
xml中的各個節點和XXData的對應關系如下表,xml各個字段的意義可以參考上篇文章再來看產生動畫相關的代碼
ArmatureDataManager
ArmatureDataManager利用DataReaderHelper解析出armarureDatas、animationDatas和_textureDatas。ArmatureDataManager是個單例,用到動畫時會到ArmatureDataManager取得要生成動畫的數據。
1 class ArmatureDataManager : public cocos2d::Ref 2 { 3 public: 4 //單例 5 static ArmatureDataManager *getInstance(); 6 static void destroyInstance(); 7 public: 8 void addArmatureData(const std::string& id, ArmatureData *armatureData, const std::string& configFilePath = ""); 9 ArmatureData *getArmatureData(const std::string& id); 10 void removeArmatureData(const std::string& id); 11 void addAnimationData(const std::string& id, AnimationData *animationData, const std::string& configFilePath = ""); 12 AnimationData *getAnimationData(const std::string& id); 13 void removeAnimationData(const std::string& id); 14 void addTextureData(const std::string& id, TextureData *textureData, const std::string& configFilePath = ""); 15 TextureData *getTextureData(const std::string& id); 16 void removeTextureData(const std::string& id); 17 void addArmatureFileInfo(const std::string& configFilePath); 18 const cocos2d::Map<std::string, ArmatureData*>& getArmatureDatas() const; 19 const cocos2d::Map<std::string, AnimationData*>& getAnimationDatas() const; 20 const cocos2d::Map<std::string, TextureData*>& getTextureDatas() const; 21 protected: 22 void addRelativeData(const std::string& configFilePath); 23 RelativeData *getRelativeData(const std::string& configFilePath); 24 private: 25 cocos2d::Map<std::string, ArmatureData*> _armarureDatas; 26 cocos2d::Map<std::string, AnimationData*> _animationDatas; 27 cocos2d::Map<std::string, TextureData*> _textureDatas; 28 std::unordered_map<std::string, RelativeData> _relativeDatas; 29 };
主要就是armarureDatas、animationDatas、_textureDatas三個map,那這三個map是怎麼產生的呢?當執行
1 ArmatureDataManager::getInstance()->addArmatureFileInfo(“dragon.xml”);
後,那三個map變生成了。addArmatureFileInfo代碼如下
1 void ArmatureDataManager::addArmatureFileInfo(const std::string& configFilePath) 2 { 3 addRelativeData(configFilePath); 4 _autoLoadSpriteFile = true; 5 DataReaderHelper::getInstance()->addDataFromFile(configFilePath); 6 }
又調用了DataReaderHelper::getInstance()->addDataFromFile(),可知是DataReaderHelper真正完成了數據的解析。DataReaderHelper類裡有一堆decodeXXX()(比如decodeArmature、decodeBone)解析xml的某個節點。看下addDataFromFile這個代碼:
1 void DataReaderHelper::addDataFromFile(const std::string& filePath) 2 { 3 //省略一些代碼 4 5 DataInfo dataInfo; 6 dataInfo.filename = filePathStr; 7 dataInfo.asyncStruct = nullptr; 8 dataInfo.baseFilePath = basefilePath; 9 if (str == ".xml") 10 { 11 DataReaderHelper::addDataFromCache(contentStr, &dataInfo); 12 } 13 else if(str == ".json" || str == ".ExportJson") 14 { 15 DataReaderHelper::addDataFromJsonCache(contentStr, &dataInfo); 16 } 17 else if(isbinaryfilesrc) 18 { 19 DataReaderHelper::addDataFromBinaryCache(contentStr.c_str(),&dataInfo); 20 } 21 22 CC_SAFE_DELETE_ARRAY(pBytes); 23 }
對應不同的文件(xml、json、二進制)解析方式,xml用到是addDataFromCache
1 void DataReaderHelper::addDataFromCache(const std::string& pFileContent, DataInfo *dataInfo) 2 { 3 tinyxml2::XMLDocument document; 4 document.Parse(pFileContent.c_str()); 5 6 tinyxml2::XMLElement *root = document.RootElement(); 7 CCASSERT(root, "XML error or XML is empty."); 8 9 root->QueryFloatAttribute(VERSION, &dataInfo->flashToolVersion); 10 11 12 /* 13 * Begin decode armature data from xml 14 */ 15 tinyxml2::XMLElement *armaturesXML = root->FirstChildElement(ARMATURES); 16 tinyxml2::XMLElement *armatureXML = armaturesXML->FirstChildElement(ARMATURE); 17 while(armatureXML) 18 { 19 ArmatureData *armatureData = DataReaderHelper::decodeArmature(armatureXML, dataInfo); 20 21 if (dataInfo->asyncStruct) 22 { 23 _dataReaderHelper->_addDataMutex.lock(); 24 } 25 ArmatureDataManager::getInstance()->addArmatureData(armatureData->name.c_str(), armatureData, dataInfo->filename.c_str()); 26 armatureData->release(); 27 if (dataInfo->asyncStruct) 28 { 29 _dataReaderHelper->_addDataMutex.unlock(); 30 } 31 32 armatureXML = armatureXML->NextSiblingElement(ARMATURE); 33 } 34 35 36 /* 37 * Begin decode animation data from xml 38 */ 39 tinyxml2::XMLElement *animationsXML = root->FirstChildElement(ANIMATIONS); 40 tinyxml2::XMLElement *animationXML = animationsXML->FirstChildElement(ANIMATION); 41 while(animationXML) 42 { 43 AnimationData *animationData = DataReaderHelper::decodeAnimation(animationXML, dataInfo); 44 if (dataInfo->asyncStruct) 45 { 46 _dataReaderHelper->_addDataMutex.lock(); 47 } 48 ArmatureDataManager::getInstance()->addAnimationData(animationData->name.c_str(), animationData, dataInfo->filename.c_str()); 49 animationData->release(); 50 if (dataInfo->asyncStruct) 51 { 52 _dataReaderHelper->_addDataMutex.unlock(); 53 } 54 animationXML = animationXML->NextSiblingElement(ANIMATION); 55 } 56 57 58 /* 59 * Begin decode texture data from xml 60 */ 61 tinyxml2::XMLElement *texturesXML = root->FirstChildElement(TEXTURE_ATLAS); 62 tinyxml2::XMLElement *textureXML = texturesXML->FirstChildElement(SUB_TEXTURE); 63 while(textureXML) 64 { 65 TextureData *textureData = DataReaderHelper::decodeTexture(textureXML, dataInfo); 66 67 if (dataInfo->asyncStruct) 68 { 69 _dataReaderHelper->_addDataMutex.lock(); 70 } 71 ArmatureDataManager::getInstance()->addTextureData(textureData->name.c_str(), textureData, dataInfo->filename.c_str()); 72 textureData->release(); 73 if (dataInfo->asyncStruct) 74 { 75 _dataReaderHelper->_addDataMutex.unlock(); 76 } 77 textureXML = textureXML->NextSiblingElement(SUB_TEXTURE); 78 } 79 }
裡面有三個while,分別decodeArmature、decodeAnimation、decodeTexture,生成ArmatureData、AnimationData、TextureData之後又ArmatureDataManager::getInstance()->addArmatureData、addAnimationData、addTextureData,加到ArmatureDataManager對應map裡。decodeXXX裡又會調用各種decodeXX來生成相應的XXXData。
在載入了xml數據後,調用
1 armature = Armature::create("Dragon"); 2 armature->getAnimation()->play("walk"); 3 armature->getAnimation()->setSpeedScale(1); 4 armature->setPosition(VisibleRect::center().x, VisibleRect::center().y * 0.3f); 5 armature->setScale(0.6f); 6 addChild(armature);
便展示了動畫,那麼這是如何做到的呢?
Armature部分代碼如下,ArmatureAnimation控制xml的mov節點,Bone中有Tween,這個Tween對應xml中b(MovementBoneData)
1 class Armature: public cocos2d::Node, public cocos2d::BlendProtocol { 2 protected: 3 //要展示動畫的ArmatureData 4 ArmatureData *_armatureData; 5 BatchNode *_batchNode; 6 Bone *_parentBone; 7 float _version; 8 mutable bool _armatureTransformDirty; 9 //所有Bone 10 cocos2d::Map<std::string, Bone*> _boneDic; cocos2d::Vector<Bone*> _topBoneList; 11 12 cocos2d::BlendFunc _blendFunc; 13 cocos2d::Vec2 _offsetPoint; 14 cocos2d::Vec2 _realAnchorPointInPoints; 15 //動畫控制器 16 ArmatureAnimation *_animation; 17 };
部分代碼如下,tweenData為當前Bone的狀態,每幀都會更新這個值,並用tweenData確定worldInfo,提供Skin顯示信息。tween為骨頭的整個動畫過程。
1 class Bone: public cocos2d::Node { 2 protected: 3 BoneData *_boneData; 4 5 //! A weak reference to the Armature 6 Armature *_armature; 7 8 //! A weak reference to the child Armature 9 Armature *_childArmature; 10 11 DisplayManager *_displayManager; 12 13 /* 14 * When Armature play an animation, if there is not a MovementBoneData of this bone in this MovementData, this bone will be hidden. 15 * Set IgnoreMovementBoneData to true, then this bone will also be shown. 16 */ 17 bool _ignoreMovementBoneData; 18 19 cocos2d::BlendFunc _blendFunc; 20 bool _blendDirty; 21 22 Tween *_tween; //! Calculate tween effect 23 24 //! Used for making tween effect in every frame 25 FrameData *_tweenData; 26 27 Bone *_parentBone; //! A weak reference to its parent 28 bool _boneTransformDirty; //! Whether or not transform dirty 29 30 //! self Transform, use this to change display's state 31 cocos2d::Mat4 _worldTransform; 32 33 BaseData *_worldInfo; 34 35 //! Armature's parent bone 36 Bone *_armatureParentBone; 37 38 };
這個是每個骨頭的動畫過程,見下面的movementBoneData。tweenData是Bone中tweenData的引用,在這每幀會計算這個tweenData值。
1 class Tween : public ProcessBase{ 2 protected: 3 //! A weak reference to the current MovementBoneData. The data is in the data pool 4 MovementBoneData *_movementBoneData; 5 6 FrameData *_tweenData; //! The computational tween frame data, //! A weak reference to the Bone's tweenData 7 FrameData *_from; //! From frame data, used for calculate between value 8 FrameData *_to; //! To frame data, used for calculate between value 9 10 // total diff guan 11 FrameData *_between; //! Between frame data, used for calculate current FrameData(m_pNode) value 12 13 Bone *_bone; //! A weak reference to the Bone 14 15 TweenType _frameTweenEasing; //! Dedermine which tween effect current frame use 16 17 int _betweenDuration; //! Current key frame will last _betweenDuration frames 18 19 // 總共運行了多少幀 guan 20 int _totalDuration; 21 22 int _fromIndex; //! The current frame index in FrameList of MovementBoneData, it's different from m_iFrameIndex 23 int _toIndex; //! The next frame index in FrameList of MovementBoneData, it's different from m_iFrameIndex 24 25 ArmatureAnimation *_animation; 26 27 bool _passLastFrame; //! If current frame index is more than the last frame's index 28 };
控制動畫的播放,看到_tweenList,所有骨頭的集合就是動畫了。
class ArmatureAnimation : public ProcessBase { protected: //! AnimationData save all MovementDatas this animation used. AnimationData *_animationData; MovementData *_movementData; //! MovementData save all MovementFrameDatas this animation used. Armature *_armature; //! A weak reference of armature std::string _movementID; //! Current movment's name int _toIndex; //! The frame index in MovementData->m_pMovFrameDataArr, it's different from m_iFrameIndex. cocos2d::Vector<Tween*> _tweenList; }
addChild(armature)後,Armaure中的onEnter(node進入舞台就會調用,比如addchild),onEnter調scheduleUpdate調scheduleUpdateWithPriority調_scheduler->scheduleUpdate。這樣就每幀調用armature的update。
1 void Armature::update(float dt) 2 { 3 _animation->update(dt); 4 for(const auto &bone : _topBoneList) { 5 bone->update(dt); 6 } 7 _armatureTransformDirty = false; 8 }
又調用了animation->update(dt);及遍歷調用bone->update(dt);animation->update(dt)如下:
1 void ArmatureAnimation::update(float dt) 2 { 3 ProcessBase::update(dt); 4 5 for (const auto &tween : _tweenList) 6 { 7 tween->update(dt); 8 } 9 //省略一堆代碼 10 }
又調用了tween->update(dt); 每一個update都會調用updateHandler(ProcessBase中update調用了update裡調用updateHandler)
1 void Tween::updateHandler() 2 { 3 //省略一堆代碼 4 if (_loopType > ANIMATION_TO_LOOP_BACK) 5 { 6 percent = updateFrameData(percent); 7 } 8 9 if(_frameTweenEasing != ::cocos2d::tweenfunc::TWEEN_EASING_MAX) 10 { 11 tweenNodeTo(percent); 12 } 13 }
tweenNodeTo調用了tweenNodeTo,其中的tweenData其實就是Bone的tweenData。根據percent計算了_tweenData的變化量。
1 FrameData *Tween::tweenNodeTo(float percent, FrameData *node) 2 { 3 node = node == nullptr ? _tweenData : node; 4 5 if (!_from->isTween) 6 { 7 percent = 0; 8 } 9 10 node->x = _from->x + percent * _between->x; 11 node->y = _from->y + percent * _between->y; 12 node->scaleX = _from->scaleX + percent * _between->scaleX; 13 node->scaleY = _from->scaleY + percent * _between->scaleY; 14 node->skewX = _from->skewX + percent * _between->skewX; 15 node->skewY = _from->skewY + percent * _between->skewY; 16 17 _bone->setTransformDirty(true); 18 19 if (node && _between->isUseColorInfo) 20 { 21 tweenColorTo(percent, node); 22 } 23 24 return node; 25 }
轉了一大圈終於在每幀更新了Bone中的tweenData,最後看Bone的update,其根據tweenData計算了worldInfo、worldTransform。而且updateDisplay更新skin的信息,staticcast<skin*>(display)->updateArmatureTransform();再transform = TransformConcat(_bone->getNodeToArmatureTransform(), _skinTransform);
1 void Bone::update(float delta) 2 { 3 if (_parentBone) 4 _boneTransformDirty = _boneTransformDirty || _parentBone->isTransformDirty(); 5 6 if (_armatureParentBone && !_boneTransformDirty) 7 { 8 _boneTransformDirty = _armatureParentBone->isTransformDirty(); 9 } 10 11 if (_boneTransformDirty) 12 { 13 if (_dataVersion >= VERSION_COMBINED) 14 { 15 TransformHelp::nodeConcat(*_tweenData, *_boneData); 16 _tweenData->scaleX -= 1; 17 _tweenData->scaleY -= 1; 18 } 19 20 _worldInfo->copy(_tweenData); 21 22 _worldInfo->x = _tweenData->x + _position.x; 23 _worldInfo->y = _tweenData->y + _position.y; 24 _worldInfo->scaleX = _tweenData->scaleX * _scaleX; 25 _worldInfo->scaleY = _tweenData->scaleY * _scaleY; 26 _worldInfo->skewX = _tweenData->skewX + _skewX + _rotationZ_X; 27 _worldInfo->skewY = _tweenData->skewY + _skewY - _rotationZ_Y; 28 29 if(_parentBone) 30 { 31 applyParentTransform(_parentBone); 32 } 33 else 34 { 35 if (_armatureParentBone) 36 { 37 applyParentTransform(_armatureParentBone); 38 } 39 } 40 41 TransformHelp::nodeToMatrix(*_worldInfo, _worldTransform); 42 43 if (_armatureParentBone) 44 { 45 _worldTransform = TransformConcat(_worldTransform, _armature->getNodeToParentTransform()); 46 } 47 } 48 49 DisplayFactory::updateDisplay(this, delta, _boneTransformDirty || _armature->getArmatureTransformDirty()); 50 51 for(const auto &obj: _children) { 52 Bone *childBone = static_cast<Bone*>(obj); 53 childBone->update(delta); 54 } 55 56 _boneTransformDirty = false;
Armature詩歌node,加入父節點後會調用其draw函數,遍歷draw了bone的顯示元素。
1 void Armature::draw(cocos2d::Renderer *renderer, const Mat4 &transform, uint32_t flags) 2 { 3 if (_parentBone == nullptr && _batchNode == nullptr) 4 { 5 // CC_NODE_DRAW_SETUP(); 6 } 7 8 9 for (auto& object : _children) 10 { 11 if (Bone *bone = dynamic_cast<Bone *>(object)) 12 { 13 Node *node = bone->getDisplayRenderNode(); 14 15 if (nullptr == node) 16 continue; 17 18 switch (bone->getDisplayRenderNodeType()) 19 { 20 case CS_DISPLAY_SPRITE: 21 { 22 Skin *skin = static_cast<Skin *>(node); 23 skin->updateTransform(); 24 25 BlendFunc func = bone->getBlendFunc(); 26 27 if (func.src != _blendFunc.src || func.dst != _blendFunc.dst) 28 { 29 skin->setBlendFunc(bone->getBlendFunc()); 30 } 31 else 32 { 33 skin->setBlendFunc(_blendFunc); 34 } 35 skin->draw(renderer, transform, flags); 36 } 37 break; 38 case CS_DISPLAY_ARMATURE: 39 { 40 node->draw(renderer, transform, flags); 41 } 42 break; 43 default: 44 { 45 node->visit(renderer, transform, flags); 46 // CC_NODE_DRAW_SETUP(); 47 } 48 break; 49 } 50 } 51 else if(Node *node = dynamic_cast<Node *>(object)) 52 { 53 node->visit(renderer, transform, flags); 54 // CC_NODE_DRAW_SETUP(); 55 } 56 } 57 }
再skin->draw(renderer, transform, flags);會用到剛剛更新的_quad,顯示出最新的圖片信息。
1 { 2 Mat4 mv = Director::getInstance()->getMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW); 3 4 //TODO implement z order 5 _quadCommand.init(_globalZOrder, _texture->getName(), getGLProgramState(), _blendFunc, &_quad, 1, mv); 6 renderer->addCommand(&_quadCommand); 7 }
至此,大家對cocos2dx裡的骨骼動畫應該有了全面的認識,三篇文章介紹的比較粗糙,其實有些細節內容我也沒看懂,不過不要在意這些細節,沒有實際的改動需求的話,懂80%就可以了,細節可以需要的時候在仔細理解。