• 【Cocos2d-x 3.x】 动作类Action源码分析


    游戏设计中,动作是不可缺少的,Cocos2d-x中所有的动作都继承自Action类,而Action类继承自Ref和Clonable类,整个动作类继承体系如图:



    FiniteTimeAction是所有瞬时动作和延时动作的父类,Follow跟随一个节点的动作,Speed改变一个动作的时间。,其中FiniteTimeAction的两个子类以及这两个子类的子类是重点。


    瞬时性动作类

    <ActionInstant.h>中的类是瞬时动作类,它的子类有:

    //... 显示一个节点 setVisible(true)
    class Show : public ActionInstant {
    
    };
    
    //... 隐藏一个节点 setVisible(false)
    class Hide : public ActionInstant {
    
    };
    
    //... 切换节点的可视属性 setVisible(!_target->isVisible())
    class ToggleVisibility : public ActionInstant {
    };
    
    //... 移除自己
    class RemoveSelf : public ActionInstant {
    
    };
    
    // 水平翻转精灵
    class FlipX : public ActionInstant {
    
    };
    
    // 垂直翻转精灵
    class FlipY : public ActionInstant {
    
    };
    
    // 将节点放置到某个位置 
    class Place : public ActionInstant {
    
    };
    
    // 设置动作的回调函数为 std::function<void()>
    class CallFunc : public ActionInstant {
    public :
        static CallFunc * create(const std::function<void()>& func);
    };
    
    // 设置动作的回调函数为 std::function<void(Node*)>
    class CallFuncN : public CallFunc {
    public :
        static CallFuncN * create(const std::function<void(Node*)>& func);
    };


    延时性动作类

    <ActionInterval.h>中的类是瞬时动作类,它的子类有:


    // 创建序列动画
    class Sequence : public ActionInterval {
    public :
        // 这种方式创建序列动画最后需要加nullptr
        // 比如: Sequence::create(action1, action2, nullptr);
        static Sequence* create(FiniteTimeAction *action1, ...);
    
        // 根据一个动作vector来创建
        static Sequence* create(const Vector<FiniteTimeAction*>& arrayOfActions);
      
        // 创建两个动作
        static Sequence* createWithTwoActions(FiniteTimeAction *actionOne, FiniteTimeAction *actionTwo);
    
        // 根据变长动作数组创建序列动作
        static Sequence* createWithVariableList(FiniteTimeAction *action1, va_list args);
    };
    
    // 重复一个动作(一次)
    class Repeat : public ActionInterval {
    public :
        // 创建一个FiniteTimeAction动作
        static Repeat* create(FiniteTimeAction *action, unsigned int times);
    };
    
    // 创建不断重复的动作
    class RepeatForever : public ActionInterval {
    public :
        // 由一个延时动作ActionInterval而创建
        static RepeatForever* create(ActionInterval *action);
    };
    
    // 创建同时执行的动作
    class Spawn : public ActionInterval {
    public :
        // 同序列式动作, 最后需要添加nullptr
        static Spawn* create(FiniteTimeAction *action1, ...);
    
        static Spawn* createWithVariableList(FiniteTimeAction *action1, va_list args);
    
        static Spawn* create(const Vector<FiniteTimeAction*>& arrayOfActions);
    
        static Spawn* createWithTwoActions(FiniteTimeAction *action1, FiniteTimeAction *action2);
    };
    
    // 旋转动作  旋转到某个角度
    class RotateTo : public ActionInterval {
    
    };
    
    // 旋转动作 旋转一定角度
    class CC_DLL RotateBy : public ActionInterval {
    
    };
    
    // 移动一定距离
    class MoveBy : public ActionInterval {
    
    };
    
    // 移动到某个点
    class MoveTo : public MoveBy {
    
    };
    
    /*---------- 这个动作By版本继承自To版本 ----------*/
    // 使某个倾斜到某个角度 
    class SkewTo : public ActionInterval {
    
    };
    
    // 倾斜一定角度
    class SkewBy : public SkewTo {
    
    };
    
    // 跳跃一定距离
    class JumpBy : public ActionInterval {
    
    };
    
    // 跳跃到某个点
    class JumpTo : public JumpBy {
    
    };
    
    // 贝塞尔曲线
    class BezierBy : public ActionInterval {
    
    };
    
    // 
    class BezierTo : public BezierBy {
    
    };
    
    // 缩放
    class ScaleTo : public ActionInterval {
    
    };
    
    class ScaleBy : public ScaleTo {
    
    };
    
    // 闪烁
    class Blink : public ActionInterval {
    
    };
    
    // 设置透明度
    class FadeTo : public ActionInterval
    
    // 淡入
    class FadeIn : public FadeTo {
    
    };
    
    // 淡出
    class FadeOut : public FadeTo {
    
    };
    
    // 延时动作
    class DelayTime : public ActionInterval {
    
    };
    
    // 动画
    class Animate : public ActionInterval {
    
    };

    在所有延时动作里:

    • SkewBy继承自SkewTo, ScaleBy继承自ScaleTo
    • RotateBy和RotateTo分别继承自ActionInterval
      TinkTo和TinkBy分别继承自ActionInterval

    • BesizerTo继承自BezierBy
      MoveTo继承自MoveBy 
      JumpTo继承自JumpBy


    动作管理

    所有的动作执行都由动作管理类ActionManager对象_actionManager来管理动作,_actionManager将所有的动作添加到执行序列中,然后该对象定时刷新自己的update函数,然后调用动作序列中每个动作的step函数,这些step再根据自身的update或结束动作:


    假设先添加一个延时性动作:

    tihs->runAction(action);

    然后由ActionManager来添加到动作队列中:

    Action * Node::runAction(Action* action)
    {
        CCASSERT( action != nullptr, "Argument must be non-nil");
        _actionManager->addAction(action, this, !_running);
        return action;
    }

    看看addAction发生了什么:

    void ActionManager::addAction(Action *action, Node *target, bool paused)
    {
        CCASSERT(action != nullptr, "");
        CCASSERT(target != nullptr, "");
    
        tHashElement *element = nullptr;
        // we should convert it to Ref*, because we save it as Ref*
        Ref *tmp = target;
        HASH_FIND_PTR(_targets, &tmp, element);
        if (! element)
        {
            element = (tHashElement*)calloc(sizeof(*element), 1);
            element->paused = paused;
            target->retain();
            element->target = target;
            HASH_ADD_PTR(_targets, target, element);
        }
    
         actionAllocWithHashElement(element);
     
         CCASSERT(! ccArrayContainsObject(element->actions, action), "");
         ccArrayAppendObject(element->actions, action); // 此处将action添加到动作列表里
     
         action->startWithTarget(target); // 然后绑定该动作的执行节点
    }

    添加了新的动作之后, 在帧刷新时会调用ActionManager的update函数,然后会遍历每个动作并执行相应的step函数:

    void ActionManager::update(float dt)
    {
        for (tHashElement *elt = _targets; elt != nullptr; )
        {
            _currentTarget = elt;
            _currentTargetSalvaged = false;
    
            if (! _currentTarget->paused)
            {
                // The 'actions' MutableArray may change while inside this loop.
                for (_currentTarget->actionIndex = 0; _currentTarget->actionIndex < _currentTarget->actions->num;
                    _currentTarget->actionIndex++)
                {
                    _currentTarget->currentAction = (Action*)_currentTarget->actions->arr[_currentTarget->actionIndex];
                    if (_currentTarget->currentAction == nullptr)
                    {
                        continue;
                    }
    
                    _currentTarget->currentActionSalvaged = false;
    
                    _currentTarget->currentAction->step(dt); // 对于延时性动作而言,执行ActionInterval::step(dt); 
    
                    if (_currentTarget->currentActionSalvaged)
                    {
                        // The currentAction told the node to remove it. To prevent the action from
                        // accidentally deallocating itself before finishing its step, we retained
                        // it. Now that step is done, it's safe to release it.
                        _currentTarget->currentAction->release();
                    } else
                    if (_currentTarget->currentAction->isDone()) // 动作执行结束后,isDone函数返回true,然后将动作停止并移除
                    {
                        _currentTarget->currentAction->stop();
    
                        Action *action = _currentTarget->currentAction;
                        // Make currentAction nil to prevent removeAction from salvaging it.
                        _currentTarget->currentAction = nullptr;
                        removeAction(action);
                    }
    
                    _currentTarget->currentAction = nullptr;
                }
            }
    
            // elt, at this moment, is still valid
            // so it is safe to ask this here (issue #490)
            elt = (tHashElement*)(elt->hh.next);
    
            // only delete currentTarget if no actions were scheduled during the cycle (issue #481)
            if (_currentTargetSalvaged && _currentTarget->actions->num == 0)
            {
                deleteHashElement(_currentTarget);
            }
        }
    
        // issue #635
        _currentTarget = nullptr;
    }

    对于延时性动作ActionInterval来说, 只有RepeatForever重写了基类ActionInterval的step函数,其他默认使用基类的版本:

    void ActionInterval::step(float dt)
    {
        if (_firstTick)
        {
            _firstTick = false;
            _elapsed = 0;
        }
        else
        {
            _elapsed += dt;
        }
        // 该调用会遍历每个ActionInterval的子类的update函数,从而执行相应的动作
        this->update(MAX (0,                                  // needed for rewind. elapsed could be negative
                          MIN(1, _elapsed /
                              MAX(_duration, FLT_EPSILON)   // division by 0
                              )
                          )
                     );
    }

    在每一帧结束后, ActionMagener的update会根据函数isDone()来检测动作是否完成,如果完成,就执行stop函数将动作停止,并且执行 removeAction函数将动作移除



  • 相关阅读:
    Java并发(三):重排序
    Java并发(六):volatile的实现原理
    Java并发(五):synchronized实现原理
    Java并发(一):多线程干货总结
    JDK源码学习笔记——String
    Java并发(二):Java内存模型
    JVM命令-java服务器故障排查
    vue之数据请求方式
    vue之菜单添加选择,知识:数据双向绑定、循环渲染、事件点击以及按键的点击
    Vue【第3章】:Vue常用指令二:事件和方法
  • 原文地址:https://www.cnblogs.com/averson/p/5149998.html
Copyright © 2020-2023  润新知