123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242 |
- //
- // 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<std::string, REDSequenceProperty*>&
- 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<FiniteTimeAction*> 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<FiniteTimeAction*> 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<Vec2> pathPoints = kf0->getEqualPoints(); // 路径动画点
- std::vector<Vec2> 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<std::string, REDSequenceProperty*>& seqNodeProps) {
- // 输入: cocos2d::Map<std::string, REDSequenceProperty*>& 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());
- }
|