最新文章专题视频专题问答1问答10问答100问答1000问答2000关键字专题1关键字专题50关键字专题500关键字专题1500TAG最新视频文章推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37视频文章20视频文章30视频文章40视频文章50视频文章60 视频文章70视频文章80视频文章90视频文章100视频文章120视频文章140 视频2关键字专题关键字专题tag2tag3文章专题文章专题2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章专题3
问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501
当前位置: 首页 - 科技 - 知识百科 - 正文

cocos2dx骨骼动画Armature源码剖析(三)_javascript技巧

来源:懂视网 责编:小采 时间:2020-11-27 21:43:26
文档

cocos2dx骨骼动画Armature源码剖析(三)_javascript技巧

cocos2dx骨骼动画Armature源码剖析(三)_javascript技巧:cocos2dx里骨骼动画代码在cocos -> editor-support -> cocostudio文件夹中,win下通过筛选器,文件结构如下。(mac下没有分,是整个一坨) armature(目录): animation(目录):动画控制相关。 CCProcessBase(文件): ProcessBase
推荐度:
导读cocos2dx骨骼动画Armature源码剖析(三)_javascript技巧:cocos2dx里骨骼动画代码在cocos -> editor-support -> cocostudio文件夹中,win下通过筛选器,文件结构如下。(mac下没有分,是整个一坨) armature(目录): animation(目录):动画控制相关。 CCProcessBase(文件): ProcessBase
cocos2dx里骨骼动画代码在cocos -> editor-support -> cocostudio文件夹中,win下通过筛选器,文件结构如下。(mac下没有分,是整个一坨)

数据相关源码

从底层到高层分析一个类一个类分析

再来看下数据相关的UML,总体来说,就是ArmatureDataManager依赖DataReaderHelper把flash导出的xml文件解析成程序直接用的XXData,XXData对应于xml的某个节点,比如FrameData就对应于节点()。

BaseData

BaseData:用来表示骨骼或帧的位置、旋转、颜色、缩放。

作为FrameData和BoneData的基类,提供骨骼的状态信息。从下文可知BoneData对应xml中的>中的b节点,FrameData对应xml中的节点,BoneData和FrameData都有

等属性,BaseDa代表了这些属性。

BoneData

BoneData对应xml中的>中的b节点

BoneData里有displayDataList,用来放这个骨头上的皮肤(就是DisplayData), DisplayData对应xml节点中的>节点,一个BoneData里可以有多个皮肤,换装等功能需要多个皮肤。

FrameData

FrameData对应xml中的节点,就是flash里的关键帧信息。

DisplayData

DisplayData是SpriteDisplayData、ArmatureDisplayData、ParticleDisplayData的父类,用来表示展示节点信息。

ArmatureData

ArmatureData是对应节点,里面有这个骨骼的所有骨头,可以看成骨骼动画的骨骼。

AnimationData

AnimationData对应节点,里面有多个MovementData,MovementData(下面介绍)对应xml中的mov,为flash中的一个带帧标签的动画。

MovementData

MovementData对应xml中>, 其中有所有的带帧信息的骨骼MovementBoneData(mov中的b)。

MovementBoneData

MovementBoneData对应xml中>的b,里面有frameList,即为关键帧信息。

小总结

xml中的各个节点和XXData的对应关系如下表,xml各个字段的意义可以参考上篇文章

再来看产生动画相关的代码

ArmatureDataManager

ArmatureDataManager利用DataReaderHelper解析出armarureDatas、animationDatas和_textureDatas。

ArmatureDataManager是个单例,用到动画时会到ArmatureDataManager取得要生成动画的数据。

 class ArmatureDataManager : public cocosd::Ref
 {
 public:
 //单例 
 static ArmatureDataManager *getInstance();
 static void destroyInstance();
 public:
 void addArmatureData(const std::string& id, ArmatureData *armatureData, const std::string& configFilePath = "");
 ArmatureData *getArmatureData(const std::string& id);
 void removeArmatureData(const std::string& id);
 void addAnimationData(const std::string& id, AnimationData *animationData, const std::string& configFilePath = "");
 AnimationData *getAnimationData(const std::string& id);
 void removeAnimationData(const std::string& id);
 void addTextureData(const std::string& id, TextureData *textureData, const std::string& configFilePath = "");
 TextureData *getTextureData(const std::string& id);
 void removeTextureData(const std::string& id);
 void addArmatureFileInfo(const std::string& configFilePath);
 const cocosd::Map& getArmatureDatas() const;
 const cocosd::Map& getAnimationDatas() const;
 const cocosd::Map& getTextureDatas() const;
 protected:
 void addRelativeData(const std::string& configFilePath);
 RelativeData *getRelativeData(const std::string& configFilePath);
 private:
 cocosd::Map _armarureDatas;
 cocosd::Map _animationDatas;
 cocosd::Map _textureDatas;
 std::unordered_map _relativeDatas;
 };

主要就是armarureDatas、animationDatas、_textureDatas三个map,那这三个map是怎么产生的呢?当执行


后,那三个map变生成了。addArmatureFileInfo代码如下

又调用了DataReaderHelper::getInstance()->addDataFromFile(),可知是DataReaderHelper真正完成了数据的解析。

DataReaderHelper类里有一堆decodeXXX()(比如decodeArmature、decodeBone)解析xml的某个节点。看下

addDataFromFile这个代码:

对应不同的文件(xml、json、二进制)解析方式,xml用到是addDataFromCache

里面有三个while,分别decodeArmature、decodeAnimation、decodeTexture,生成ArmatureData、AnimationData、TextureData之后又ArmatureDataManager::getInstance()->addArmatureData、addAnimationData、addTextureData,加到ArmatureDataManager对应map里。decodeXXX里又会调用各种decodeXX来生成相应的XXXData。

Armature

在载入了xml数据后,调用

便展示了动画,那么这是如何做到的呢?

Armature部分代码如下,ArmatureAnimation控制xml的mov节点,Bone中有Tween,这个Tween对应xml中b(MovementBoneData)

class Armature: public cocosd::Node, public cocosd::BlendProtocol {
 protected:
 //要展示动画的ArmatureData
 ArmatureData *_armatureData;
 BatchNode *_batchNode;
 Bone *_parentBone;
 float _version;
 mutable bool _armatureTransformDirty;
 //所有Bone
 cocosd::Map _boneDic; cocosd::Vector _topBoneList;
 
 cocosd::BlendFunc _blendFunc; 
 cocosd::Vec _offsetPoint;
 cocosd::Vec _realAnchorPointInPoints;
 //动画控制器
 ArmatureAnimation *_animation;
 };

Bone

部分代码如下,tweenData为当前Bone的状态,每帧都会更新这个值,并用tweenData确定worldInfo,提供Skin显示信息。tween为骨头的整个动画过程。

class Bone: public cocosd::Node {
 protected:
 BoneData *_boneData;
 
 //! A weak reference to the Armature
 Armature *_armature;
 
 //! A weak reference to the child Armature
 Armature *_childArmature;
 
 DisplayManager *_displayManager;
 
 /*
 * When Armature play an animation, if there is not a MovementBoneData of this bone in this MovementData, this bone will be hidden.
 * Set IgnoreMovementBoneData to true, then this bone will also be shown.
 */
 bool _ignoreMovementBoneData;
 
 cocosd::BlendFunc _blendFunc;
 bool _blendDirty;
 
 Tween *_tween; //! Calculate tween effect
 
 //! Used for making tween effect in every frame
 FrameData *_tweenData;
 
 Bone *_parentBone; //! A weak reference to its parent
 bool _boneTransformDirty; //! Whether or not transform dirty
 
 //! self Transform, use this to change display's state
 cocosd::Mat _worldTransform;
 
 BaseData *_worldInfo;
 
 //! Armature's parent bone
 Bone *_armatureParentBone;
 
 };

Tween

这个是每个骨头的动画过程,见下面的movementBoneData。tweenData是Bone中tweenData的引用,在这每帧会计算这个tweenData值。

class Tween : public ProcessBase{
 protected:
 //! A weak reference to the current MovementBoneData. The data is in the data pool
 MovementBoneData *_movementBoneData;
 
 FrameData *_tweenData; //! The computational tween frame data, //! A weak reference to the Bone's tweenData
 FrameData *_from; //! From frame data, used for calculate between value
 FrameData *_to; //! To frame data, used for calculate between value
 
 // total diff guan
 FrameData *_between; //! Between frame data, used for calculate current FrameData(m_pNode) value
 
 Bone *_bone; //! A weak reference to the Bone
 
 TweenType _frameTweenEasing; //! Dedermine which tween effect current frame use
 
 int _betweenDuration; //! Current key frame will last _betweenDuration frames
 
 // 总共运行了多少帧 guan
 int _totalDuration;
 
 int _fromIndex; //! The current frame index in FrameList of MovementBoneData, it's different from m_iFrameIndex
 int _toIndex; //! The next frame index in FrameList of MovementBoneData, it's different from m_iFrameIndex
 
 ArmatureAnimation *_animation;
 
 bool _passLastFrame; //! If current frame index is more than the last frame's index
 };

ArmatureAnimation

控制动画的播放,看到_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 _tweenList;
}

如何做到每帧更新骨头的信息?

addChild(armature)后,Armaure中的onEnter(node进入舞台就会调用,比如addchild),onEnter调scheduleUpdate调scheduleUpdateWithPriority调_scheduler->scheduleUpdate。这样就每帧调用armature的update。

void Armature::update(float dt)
 {
 _animation->update(dt);
 for(const auto &bone : _topBoneList) {
 bone->update(dt);
 }
 _armatureTransformDirty = false;
 }

又调用了animation->update(dt);及遍历调用bone->update(dt);animation->update(dt)如下:

void ArmatureAnimation::update(float dt)
 {
 ProcessBase::update(dt);
 
 for (const auto &tween : _tweenList)
 {
 tween->update(dt);
 }
 //省略一堆代码
 }

又调用了tween->update(dt); 每一个update都会调用updateHandler(ProcessBase中update调用了update里调用updateHandler)

 void Tween::updateHandler()
 {
 //省略一堆代码
 if (_loopType > ANIMATION_TO_LOOP_BACK)
 {
 percent = updateFrameData(percent);
 }
 
 if(_frameTweenEasing != ::cocosd::tweenfunc::TWEEN_EASING_MAX)
 {
 tweenNodeTo(percent);
 }
 }

tweenNodeTo调用了tweenNodeTo,其中的tweenData其实就是Bone的tweenData。根据percent计算了_tweenData的变化量。

 FrameData *Tween::tweenNodeTo(float percent, FrameData *node)
 {
 node = node == nullptr ? _tweenData : node;
 
 if (!_from->isTween)
 {
 percent = ;
 }
 
 node->x = _from->x + percent * _between->x;
 node->y = _from->y + percent * _between->y;
 node->scaleX = _from->scaleX + percent * _between->scaleX;
 node->scaleY = _from->scaleY + percent * _between->scaleY;
 node->skewX = _from->skewX + percent * _between->skewX;
 node->skewY = _from->skewY + percent * _between->skewY;
 
 _bone->setTransformDirty(true);
 
 if (node && _between->isUseColorInfo)
 {
 tweenColorTo(percent, node);
 }
 
 return node;
 }

转了一大圈终于在每帧更新了Bone中的tweenData,最后看Bone的update,其根据tweenData计算了worldInfo、worldTransform。而且updateDisplay更新skin的信息,staticcast(display)->updateArmatureTransform();再transform = TransformConcat(_bone->getNodeToArmatureTransform(), _skinTransform);

 void Bone::update(float delta)
 {
 if (_parentBone)
 _boneTransformDirty = _boneTransformDirty || _parentBone->isTransformDirty();
 
 if (_armatureParentBone && !_boneTransformDirty)
 {
 _boneTransformDirty = _armatureParentBone->isTransformDirty();
 }
 
 if (_boneTransformDirty)
 {
 if (_dataVersion >= VERSION_COMBINED)
 {
 TransformHelp::nodeConcat(*_tweenData, *_boneData);
 _tweenData->scaleX -= ;
 _tweenData->scaleY -= ;
 }
 
 _worldInfo->copy(_tweenData);
 
 _worldInfo->x = _tweenData->x + _position.x;
 _worldInfo->y = _tweenData->y + _position.y;
 _worldInfo->scaleX = _tweenData->scaleX * _scaleX;
 _worldInfo->scaleY = _tweenData->scaleY * _scaleY;
 _worldInfo->skewX = _tweenData->skewX + _skewX + _rotationZ_X;
 _worldInfo->skewY = _tweenData->skewY + _skewY - _rotationZ_Y;
 
 if(_parentBone)
 {
 applyParentTransform(_parentBone);
 }
 else
 {
 if (_armatureParentBone)
 {
 applyParentTransform(_armatureParentBone);
 }
 }
 
 TransformHelp::nodeToMatrix(*_worldInfo, _worldTransform);
 
 if (_armatureParentBone)
 {
 _worldTransform = TransformConcat(_worldTransform, _armature->getNodeToParentTransform());
 }
 }
 
 DisplayFactory::updateDisplay(this, delta, _boneTransformDirty || _armature->getArmatureTransformDirty());
 
 for(const auto &obj: _children) {
 Bone *childBone = static_cast(obj);
 childBone->update(delta);
 }
 
 _boneTransformDirty = false;

如何展示(draw)出图片(skin)

Armature诗歌node,加入父节点后会调用其draw函数,遍历draw了bone的显示元素。

 void Armature::draw(cocosd::Renderer *renderer, const Mat &transform, uint_t flags)
 {
 if (_parentBone == nullptr && _batchNode == nullptr)
 {
 // CC_NODE_DRAW_SETUP();
 }
 
 
 for (auto& object : _children)
 {
 if (Bone *bone = dynamic_cast(object))
 {
 Node *node = bone->getDisplayRenderNode();
 
 if (nullptr == node)
 continue;
 
 switch (bone->getDisplayRenderNodeType())
 {
 case CS_DISPLAY_SPRITE:
 {
 Skin *skin = static_cast(node);
 skin->updateTransform();
 
 BlendFunc func = bone->getBlendFunc();
 
 if (func.src != _blendFunc.src || func.dst != _blendFunc.dst)
 {
 skin->setBlendFunc(bone->getBlendFunc());
 }
 else
 {
 skin->setBlendFunc(_blendFunc);
 }
 skin->draw(renderer, transform, flags);
 }
 break;
 case CS_DISPLAY_ARMATURE:
 {
 node->draw(renderer, transform, flags);
 }
 break;
 default:
 {
 node->visit(renderer, transform, flags);
 // CC_NODE_DRAW_SETUP();
 }
 break;
 }
 }
 else if(Node *node = dynamic_cast(object))
 {
 node->visit(renderer, transform, flags);
 // CC_NODE_DRAW_SETUP();
 }
 }
 }

再skin->draw(renderer, transform, flags);会用到刚刚更新的_quad,显示出最新的图片信息。

{
 Mat mv = Director::getInstance()->getMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
 
 //TODO implement z order
 _quadCommand.init(_globalZOrder, _texture->getName(), getGLProgramState(), _blendFunc, &_quad, , mv);
 renderer->addCommand(&_quadCommand);
 }

至此,大家对cocos2dx里的骨骼动画应该有了全面的认识,三篇文章介绍的比较粗糙,其实有些细节内容我也没看懂,不过不要在意这些细节,没有实际的改动需求的话,懂80%就可以了,细节可以需要的时候在仔细理解。

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。TEL:177 7030 7066 E-MAIL:11247931@qq.com

文档

cocos2dx骨骼动画Armature源码剖析(三)_javascript技巧

cocos2dx骨骼动画Armature源码剖析(三)_javascript技巧:cocos2dx里骨骼动画代码在cocos -> editor-support -> cocostudio文件夹中,win下通过筛选器,文件结构如下。(mac下没有分,是整个一坨) armature(目录): animation(目录):动画控制相关。 CCProcessBase(文件): ProcessBase
推荐度:
  • 热门焦点

最新推荐

猜你喜欢

热门推荐

专题
Top