// // RedreamAnim.cpp // redream_runtime // // Created by zhuge on 2023/1/17. // #include "RedreamAnim.h" #include "BezierEqualPointsAction.hpp" #include "Redream.h" redream::RedreamAnim* redream::RedreamAnim::createFromAnimFile(std::string filePath) { auto anim = new redream::RedreamAnim(); if (anim && anim->init(filePath.c_str())) { anim->autorelease(); return anim; } delete anim; anim = nullptr; return nullptr; } redream::RedreamAnim::~RedreamAnim() { _propKeyframes.clear(); } bool redream::RedreamAnim::init(const char* filePath) { auto animationManager = _loadAnimFile(filePath); // 时间线时长 _duration = animationManager->_sequences.front()->getDuration(); // 节点 cocos2d::Node* auto node = animationManager->_nodeSequences.begin()->first; // 节点动画属性 cocos2d::Map& auto& seqNodeProps = animationManager->_nodeSequences.begin()->second.begin()->second; // 组织所有的关键帧入map _initPropKeyframes(animationManager, node, seqNodeProps); // 初始化默认的起止位置 CC_ASSERT(_propKeyframes.find("position") != _propKeyframes.end()); auto keyframes = _propKeyframes.at("position")->keyframes; _startPosition = _valueToVec2(keyframes.front()->getValue()); _endPosition = _valueToVec2(keyframes.back()->getValue()); return true; } cocos2d::Action* redream::RedreamAnim::getAction() { REDAnimationManager tempAnimationManager; // 调用起函数,友元 // 取原始的起止位置 const auto& posKeyframes = _propKeyframes.at("position")->keyframes; Vec2 oldStartPos = _valueToVec2(posKeyframes.front()->getValue()); Vec2 oldEndPos = _valueToVec2(posKeyframes.back()->getValue()); Vector allActions; /* 每种属性所有的keyframe生成对应类型的动画,放入一个Sequence中 所有属性最后生成的Sequence放到一个Spawn中 */ for (auto it = _propKeyframes.begin(); it != _propKeyframes.end(); it++) { auto propName = it->first; if (propName == "displayFrame") { // 纹理动画是无效的 continue; } auto keyframes = _propKeyframes.at(propName)->keyframes; Vector actions; for (ssize_t i = 0; i < keyframes.size() - 1; i++) { auto kf0 = keyframes.at(i); auto kf1 = keyframes.at(i + 1); float duration = kf1->getTime() - kf0->getTime(); cocos2d::ActionInterval* action; if (propName == "position") { std::vector pathPoints = kf0->getEqualPoints(); // 路径动画点 std::vector convertedPathPoints = {}; // 关键位置,应用首尾位置信息,转换中间所有坐标 Vec2 toPos; // 首尾可以直接使用,无需计算 if (i == 0) { toPos = _startPosition; } else if (i == keyframes.size() - 1) { toPos = _endPosition; } else { if (pathPoints.empty()) { // 转换坐标 toPos = _getControlPoint(oldStartPos, oldEndPos, _valueToVec2(kf1->getValue()), _startPosition, _endPosition); } else { for (auto point: pathPoints) { auto newPoint = _getControlPoint(oldStartPos, oldEndPos, point, _startPosition, _endPosition); convertedPathPoints.push_back(newPoint); } } } if (convertedPathPoints.empty()) { action = MoveTo::create(duration, toPos); } else { action = BezierEqualPointsAction::create(duration, convertedPathPoints); } } else if (propName == "scale") { // 忽略需要节点Size的属性类型,直接取真实值 Vec2 scale = _valueToVec2(kf1->getValue()); action = ScaleTo::create(duration, scale.x, scale.y); } else { // 普通的动画属性,不需要节点信息,直接创建就可以 action = tempAnimationManager.getAction(kf0, kf1, propName, nullptr); } // 应用easing action = tempAnimationManager.getEaseAction(action, kf0->getEasingType(), kf0->getEasingOpt()); // 存好,回头放到Sequence中去 actions.pushBack(action); } // 同一种类型的动画,用Sequence allActions.pushBack(Sequence::create(actions)); } // 所有类型的动画,用Spawn auto action = Spawn::create(allActions); return action; } #pragma mark - private redream::REDAnimationManager* redream::RedreamAnim::_loadAnimFile(const char* filePath) { std::string fileWithoutPathExtension = REDReader::deletePathExtension(filePath); std::string outFilePath = fileWithoutPathExtension + ".redanim"; redream::NodeLoaderLibrary *ccNodeLoaderLibrary = redream::NodeLoaderLibrary::newDefaultNodeLoaderLibrary(); redream::REDReader *ccbReader = new redream::REDReader(ccNodeLoaderLibrary); auto node = (Node*)ccbReader->readNodeGraphFromFile(outFilePath.c_str()); CC_ASSERT(node != nullptr); auto animationManager = ccbReader->getAnimationManager(); // 只有一条时间线 CC_ASSERT(animationManager->_sequences.size() == 1); // 只有一个节点 CC_ASSERT(animationManager->_nodeSequences.size() == 1); // 只有一条时间线 CC_ASSERT(animationManager->_nodeSequences.begin()->second.size() == 1); return animationManager; } void redream::RedreamAnim::_initPropKeyframes(REDAnimationManager* animationManager, cocos2d::Node* node, cocos2d::Map& seqNodeProps) { // 输入: cocos2d::Map& seqNodeProps <属性名, 该属性对应的关键帧信息(所有关键帧)> // 生成: map<属性名, PropKeyframes属性的关键帧列表> // 纹理动画忽略 // 首帧/末帧不存在则补一个,其他所有帧直接放进来 for (auto it = seqNodeProps.begin(); it != seqNodeProps.end(); it++) { const std::string propName = it->first; if (propName == "displayFrame") { // 纹理动画无意义 continue; } redream::REDSequenceProperty *seqProp = it->second; auto& nodeKeyframes = seqProp->getKeyframes(); if (nodeKeyframes.size() < 0) { // 异常状态,无任何帧信息 continue; } PropKeyframes* keyframes = new PropKeyframes(); keyframes->autorelease(); // 如果没有首尾帧,需要补上一个 redream::REDKeyframe* firstKeyframe = nodeKeyframes.front(); if (firstKeyframe->getTime() < 0.000001) { // 首帧不为0 const auto& baseValue = animationManager->getBaseValue(node, propName); auto frame = _createRedKeyframe(baseValue, 0); keyframes->keyframes.pushBack(frame); } // 中间所有关键帧 for (auto keyframe: nodeKeyframes) { keyframes->keyframes.pushBack(keyframe); } // 如果没有尾帧,需要补上一个 redream::REDKeyframe* lastKeyframe = nodeKeyframes.back(); if (abs(lastKeyframe->getTime() - _duration) > 0.000001) { // 不等于时间线的长度,补 auto frame = _createRedKeyframe(lastKeyframe->getValue(), _duration); keyframes->keyframes.pushBack(frame); } // 放入map中 _propKeyframes.insert(propName, keyframes); } } redream::REDKeyframe* redream::RedreamAnim::_createRedKeyframe(const cocos2d::Value& value, float atTime) const { redream::REDKeyframe* frame = new redream::REDKeyframe(); frame->autorelease(); frame->setValue(value); frame->setTime(atTime); frame->setEasingType(redream::REDKeyframe::EasingType::LINEAR); return frame; } Vec2 redream::RedreamAnim::_getControlPoint(Vec2 A_start, Vec2 A_end, Vec2 A_control, Vec2 B_start, Vec2 B_end) { Vec2 ab = A_end - A_start; Vec2 ac = A_control - A_start; Vec2 de = B_end - B_start; float s; Vec2 ret; if (A_start != A_end){ s = de.length() / ab.length(); ac.scale(s); long double angle1 = std::atan2l(ab.y, ab.x); ac.rotate(Vec2::ZERO, -angle1); long double angle2 = std::atan2l(de.y, de.x); ac.rotate(Vec2::ZERO, angle2); ret = ac + B_start; } else { ret = B_start; } return ret; } Vec2 redream::RedreamAnim::_valueToVec2(const cocos2d::Value& value) { auto vector = value.asValueVector(); return Vec2(vector[0].asInt(), vector[1].asInt()); }