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%就可以了,細節可以需要的時候在仔細理解。