cocos2d-x - Action 动作

Action

(一)Action

Action是所有动作的基类。继承关系如下所示:
在这里插入图片描述

  • Action常用成员函数:
    class CC_DLL Action : public Ref, public Clonable
    {
    public:
        virtual Action* clone() const
        {
            CC_ASSERT(0);
            return nullptr;
        }
    
        /** Returns a new action that performs the exact reverse of the action. 
         *
         * @return A new action that performs the exact reverse of the action.
         * @js NA
         */
        virtual Action* reverse() const
        {
            CC_ASSERT(0);
            return nullptr;
        }
        /** Return true if the action has finished. 
         * 
         * @return Is true if the action has finished.
         */
        virtual bool isDone() const;
    
        /** Called before the action start. It will also set the target. 
         *
         * @param target A certain target.
         */
        virtual void startWithTarget(Node *target);
    
        /** 
         * Called after the action has finished. It will set the 'target' to nil.
         * IMPORTANT: You should never call "Action::stop()" manually. Instead, use: "target->stopAction(action);".
         */
        virtual void stop();
    
        /** Called every frame with it's delta time, dt in seconds. DON'T override unless you know what you are doing. 
         *
         * @param dt In seconds.
         */
        virtual void step(float dt);
    
        /** 
         * Called once per frame. time a value between 0 and 1.
    
         * For example:
         * - 0 Means that the action just started.
         * - 0.5 Means that the action is in the middle.
         * - 1 Means that the action is over.
         *
         * @param time A value between 0 and 1.
         */
        virtual void update(float time);
        
        /** Changes the tag that is used to identify the action easily. 
         *
         * @param tag Used to identify the action easily.
         */
        void setTag(int tag) { _tag = tag; }
    }
    
  • Node与动作相关的成员函数:
    /**
     * Sets the ActionManager object that is used by all actions.
     *
     * @warning If you set a new ActionManager, then previously created actions will be removed.
     *
     * @param actionManager     A ActionManager object that is used by all actions.
     */
    virtual void setActionManager(ActionManager* actionManager);
    /**
     * Gets the ActionManager object that is used by all actions.
     * @see setActionManager(ActionManager*)
     * @return A ActionManager object.
     */
    virtual ActionManager* getActionManager() { return _actionManager; }
    virtual const ActionManager* getActionManager() const { return _actionManager; }
    
    /**
     * Executes an action, and returns the action that is executed.
     *
     * This node becomes the action's target. Refer to Action::getTarget().
     * @warning Actions don't retain their target.
     *
     * @param action An Action pointer.
     */
    virtual Action* runAction(Action* action);
    
    /**
     * Stops and removes all actions from the running action list .
     */
    void stopAllActions();
    
    /**
     * Stops and removes an action from the running action list.
     *
     * @param action    The action object to be removed.
     */
    void stopAction(Action* action);
    
    /**
     * Removes an action from the running action list by its tag.
     *
     * @param tag   A tag that indicates the action to be removed.
     */
    void stopActionByTag(int tag);
    
    /**
     * Removes all actions from the running action list by its tag.
     *
     * @param tag   A tag that indicates the action to be removed.
     */
    void stopAllActionsByTag(int tag);
    

(二)时间动作

FiniteTimeAction是以时间相关的动作类,会记录动作执行的持续时间。

class CC_DLL FiniteTimeAction : public Action
{
public:
    float getDuration() const { return _duration; }
    void setDuration(float duration) { _duration = duration; }
protected:
    //! Duration in seconds.
    float _duration;

FiniteTimeAction的子类包括:

  • ActionInstant:即时动作类。

    常用子类:

    • 水平与垂直翻转:FlipXFlipY。注:只有e.g.
      mySprite->runAction(FlipX::create(true));	// 参数表示是否翻转
      
      mySprite->runAction(FlipY::create(true));
      
    • 放置:Place,相当于setPosition。e.g.
      mySprite->runAction(Place::create(Vec2(100, 200)));
      
    • 隐藏和显示:HideShow,相当于setVisible。e.g.
      mySprite->runAction(Hide::create());
      
      mySprite->runAction(Show::create());
      
    • 可见切换:ToggleVisibility,可见切换为不可见,不可见切换为可见。e.g.
      mySprite->runAction(ToggleVisibility::create());
      
    • 函数调用动作:CallFunc。e.g.
      auto callFunc = CallFunc::create([](){
          log("hello");
      });
      mySprite->runAction(callFunc);
      
  • ActionInterval:持续动作类。

    常用子类(带有To的方法参数表示动作结果大小(dst),带有By的方法参数表示动作变化大小(delta)):

    • 旋转:RotateToRotateBy

      static RotateTo* create(float duration, float dstAngleX, float dstAngleY);
      static RotateTo* create(float duration, float dstAngle);
      static RotateTo* create(float duration, const Vec3& dstAngle3D);
      
    • 移动:MoveToMoveBy

      static MoveTo* create(float duration, const Vec2& position);
      static MoveTo* create(float duration, const Vec3& position);
      
    • 跳跃:JumpToJumpBy

      static JumpTo* create(float duration, const Vec2& position, float height, int jumps);	// 第3个参数表示跳跃高度,第4个参数表示跳跃次数
      
    • 贝塞尔曲线运动:BezierToBezierBy。贝塞尔曲线的轨迹如下图所示。
      在这里插入图片描述

      static BezierTo* create(float t, const ccBezierConfig& c);
      typedef struct _ccBezierConfig {
          //! end position of the bezier
          Vec2 endPosition;
          //! Bezier control point 1
          Vec2 controlPoint_1;
          //! Bezier control point 2
          Vec2 controlPoint_2;
      } ccBezierConfig;
      
    • 缩放:ScaleToRotateBy

      static ScaleTo* create(float duration, float s);
      static ScaleTo* create(float duration, float sx, float sy);
      static ScaleTo* create(float duration, float sx, float sy, float sz);
      
    • 倾斜:SkewToSkewBy

      static SkewTo* create(float t, float sx, float sy);
      
    • 调整大小:ResizeToResizeBy

      static ResizeTo* create(float duration, const cocos2d::Size& final_size);
      
    • 淡入淡出:FadeIn(从完全透明到不透明)、FadeOut(从完全不透明到透明)、FadeTo(转化至指定透明度)。e.g.

      static FadeIn* create(float d);
      static FadeOut* create(float d);
      static FadeTo* create(float duration, uint8_t opacity);	// 第二个参数的范围在0~255之间
      
    • 色彩混合:TintToTintBy。e.g.

      static TintTo* create(float duration, uint8_t red, uint8_t green, uint8_t blue);
      static TintTo* create(float duration, const Color3B& color);
      
    • 闪烁:Blink,其实就是来回变更可见性。

      static Blink* create(float duration, int blinks);	// 第2个参数表示闪烁次数
      
    • 延时:DelayTime

      static DelayTime* create(float d);
      
    • 倒转:ReverseTime

      static ReverseTime* create(FiniteTimeAction *action);
      
    • 帧动画:Animate。e.g.

      // now lets animate the sprite we moved
      Vector<SpriteFrame*> animFrames;
      animFrames.reserve(3);
      animFrames.pushBack(SpriteFrame::create("frame1.png", Rect(0,0,65,81)));
      animFrames.pushBack(SpriteFrame::create("frame2.png", Rect(0,0,65,81)));
      animFrames.pushBack(SpriteFrame::create("frame3.png", Rect(0,0,65,81)));
      
      // create the animation out of the frames
      Animation* animation = Animation::createWithSpriteFrames(animFrames, 0.1f);
      Animate* animate = Animate::create(animation);
      
      // run it and repeat it forever
      mySprite->runAction(RepeatForever::create(animate));
      
      • SpriteFrame::create方法的第二个参数Rect记录了图片的原点坐标和宽高。
        // CCSpriteFrame.cpp
        SpriteFrame* SpriteFrame::create(const std::string& filename, const Rect& rect)
        {
            SpriteFrame *spriteFrame = new (std::nothrow) SpriteFrame();
            spriteFrame->initWithTextureFilename(filename, rect);
            spriteFrame->autorelease();
            return spriteFrame;
        }
        bool SpriteFrame::initWithTextureFilename(const std::string& filename, const Rect& rect)
        {
            Rect rectInPixels = CC_RECT_POINTS_TO_PIXELS( rect );
            return initWithTextureFilename(filename, rectInPixels, false, Vec2::ZERO, rectInPixels.size);
        }
        
        // ccMacros.h
        /** @def CC_RECT_POINTS_TO_PIXELS
         Converts a rect in points to pixels
         */
        #define CC_RECT_POINTS_TO_PIXELS(__rect_in_points_points__)                                                                        \
            cocos2d::Rect( (__rect_in_points_points__).origin.x * CC_CONTENT_SCALE_FACTOR(), (__rect_in_points_points__).origin.y * CC_CONTENT_SCALE_FACTOR(),    \
                    (__rect_in_points_points__).size.width * CC_CONTENT_SCALE_FACTOR(), (__rect_in_points_points__).size.height * CC_CONTENT_SCALE_FACTOR() )
        
      • Animation::createWithSpriteFrames方法的第二个参数表示延迟,第三个参数表示循环次数。
        Animation* Animation::createWithSpriteFrames(const Vector<SpriteFrame*>& frames, float delay/* = 0.0f*/, unsigned int loops/* = 1*/)
        {
            Animation *animation = new (std::nothrow) Animation();
            animation->initWithSpriteFrames(frames, delay, loops);
            animation->autorelease();
        
            return animation;
        }
        
    • Sequence:序列动作类(串行执行)。

      static Sequence* create(FiniteTimeAction *action1, ...) CC_REQUIRES_NULL_TERMINATION;
      static Sequence* create(const Vector<FiniteTimeAction*>& arrayOfActions);
      static Sequence* createWithVariableList(FiniteTimeAction *action1, va_list args);
      static Sequence* createWithTwoActions(FiniteTimeAction *actionOne, FiniteTimeAction *actionTwo);
      

      Sequence* Sequence::create(FiniteTimeAction *action1, ...)方法的参数个数不限,不过要求最后一个参数必须为nullptrNULL。e.g.

      // create a few actions.
      auto jump = JumpBy::create(0.5, Vec2(0, 0), 100, 1);
      
      auto rotate = RotateTo::create(2.0f, 10);
      
      // create a few callbacks
      auto callbackJump = CallFunc::create([](){
          log("Jumped!");
      });
      
      auto callbackRotate = CallFunc::create([](){
          log("Rotated!");
      });
      
      // create a sequence with the actions and callbacks
      auto seq = Sequence::create(jump, callbackJump, rotate, callbackRotate, nullptr);
      
      // run it
      mySprite->runAction(seq);
      
    • Spawn:同步动作类(并行执行)。

      static Spawn* create(FiniteTimeAction *action1, ...) CC_REQUIRES_NULL_TERMINATION;
      static Spawn* createWithVariableList(FiniteTimeAction *action1, va_list args);
      static Spawn* create(const Vector<FiniteTimeAction*>& arrayOfActions);
      static Spawn* createWithTwoActions(FiniteTimeAction *action1, FiniteTimeAction *action2);
      

      e.g.

      auto moveBy = MoveBy::create(10, Vec2(400,100));
      auto fadeTo = FadeTo::create(2.0f, 120.0f);
      
      // running the above Actions with Spawn.
      auto mySpawn = Spawn::createWithTwoActions(moveBy, fadeTo);
      mySprite->runAction(mySpawn);
      

      虽然mySprite->runAction(mySpawn);的效果等价于mySprite->runAction(moveBy); mySprite->runAction(fadeTo);,但Spawn有一个好处是它可以添加到Sequence。e.g.

      // create a few Actions
      auto moveBy = MoveBy::create(10, Vec2(400,100));
      auto fadeTo = FadeTo::create(2.0f, 120.0f);
      auto scaleBy = ScaleBy::create(2.0f, 3.0f);
      
      // create a Spawn to use
      auto mySpawn = Spawn::createWithTwoActions(scaleBy, fadeTo);
      
      // tie everything together in a sequence
      auto seq = Sequence::create(moveBy, mySpawn, moveBy, nullptr);
      
      // run it
      mySprite->runAction(seq);
      
    • Repeat:重复动作。

      static Repeat* create(FiniteTimeAction *action, unsigned int times);
      
    • RepeatForever:无限重复动作。

      static RepeatForever* create(ActionInterval *action);
      
    • 可变速度类:ActionEase为抽象基类。

      • EaseRateAction:直接根据 rate调节动作速度。
        static EaseRateAction* create(ActionInterval* action, float rate);	// rate=1.0为动作原始速度。数值越小,速度越快;数值越大,速度越慢
        
      • EaseIn:由慢变快;EaseOut:由快变慢;EaseInOut:先有慢变快,再由快变慢。命名带有该前后缀的类型都是通过宏定义来声明的:
        #define EASE_TEMPLATE_DECL_CLASS(CLASSNAME) \
        class CC_DLL CLASSNAME : public ActionEase \
        { \
        CC_CONSTRUCTOR_ACCESS: \
            virtual ~CLASSNAME() { } \
            CLASSNAME() { } \
        public: \
            static CLASSNAME* create(ActionInterval* action); \
            virtual CLASSNAME* clone() const override; \
            virtual void update(float time) override; \
            virtual ActionEase* reverse() const override; \
        private: \
            CC_DISALLOW_COPY_AND_ASSIGN(CLASSNAME); \
        };
        
        EASERATE_TEMPLATE_DECL_CLASS(EaseIn);
        EASERATE_TEMPLATE_DECL_CLASS(EaseOut);
        EASERATE_TEMPLATE_DECL_CLASS(EaseInOut);
        
      • 至于带有特定变速方程(e.g. EaseSineIn, EaseExponentIn)的动作也不用去记,有需要再查就好。

(三)动作克隆 & 序列倒转

在前面提到,一个动作不能多次执行,除非使用动作的克隆。e.g.

// create our Sprites
auto heroSprite = Sprite::create("herosprite.png");
auto enemySprite = Sprite::create("enemysprite.png");

// create an Action
auto moveBy = MoveBy::create(10, Vec2(400,100));

// run it on our hero
heroSprite->runAction(moveBy);

// run it on our enemy
enemySprite->runAction(moveBy->clone()); 

Sequence可以通过调用reverse方法获取倒转的序列。

// create a few Actions
auto moveBy = MoveBy::create(2.0f, Vec2(500,0));
auto scaleBy = ScaleBy::create(2.0f, 2.0f);
auto delay = DelayTime::create(2.0f);

// create a sequence
auto delaySequence = Sequence::create(delay, delay->clone(), delay->clone(),
delay->clone(), nullptr);

auto sequence = Sequence::create(moveBy, delay, scaleBy, delaySequence, nullptr);

// run it
mySprite->runAction(sequence);

// reverse it
mySprite->runAction(sequence->reverse());
  • sequence:移动2s – 暂停2s – 伸缩2s – 暂停2s – 暂停2s – 暂停2s – 暂停2s;
  • sequence->reverse():暂停2s – 暂停2s – 暂停2s – 暂停2s – 伸缩2s – 暂停2s – 移动2s。

(四)速度类 & 跟随动作类

  • Speed:用于设置动作的速度。
    class CC_DLL Speed : public Action
    {
    public:
    	static Speed* create(ActionInterval* action, float speed);	// rate=1.0表示正常速度;rate=2.0表示速度加倍;rate=0.5表示速度减半
    	float getSpeed() const { return _speed; }
    	void setSpeed(float speed) { _speed = speed; }
    }
    
  • Follow:用于跟随另一个节点做同步运动。
    class CC_DLL Follow : public Action
    {
    public:
    	/**
         * Creates the action with a set boundary or with no boundary.
         *
         * @param followedNode  The node to be followed.
         * @param rect  The boundary. If \p rect is equal to Rect::ZERO, it'll work
         *              with no boundary.
        */
        
        static Follow* create(Node *followedNode, const Rect& rect = Rect::ZERO);
        
        /**
         * Creates the action with a set boundary or with no boundary with offsets.
         *
         * @param followedNode  The node to be followed.
         * @param rect  The boundary. If \p rect is equal to Rect::ZERO, it'll work
         *              with no boundary.
         * @param xOffset The horizontal offset from the center of the screen from which the
         *               node  is to be followed.It can be positive,negative or zero.If
         *               set to zero the node will be horizontally centered followed.
         *  @param yOffset The vertical offset from the center of the screen from which the
         *                 node is to be followed.It can be positive,negative or zero.
         *                 If set to zero the node will be vertically centered followed.
         *   If both xOffset and yOffset are set to zero,then the node will be horizontally and vertically centered followed.
         */
    
        static Follow* createWithOffset(Node* followedNode,float xOffset,float yOffset,const Rect& rect = Rect::ZERO);
        
        /** Return boundarySet.
         *
         * @return Return boundarySet.
         */
        bool isBoundarySet() const { return _boundarySet; }
        /** Alter behavior - turn on/off boundary. 
         *
         * @param value Turn on/off boundary.
         */
        void setBoundarySet(bool value) { _boundarySet = value; }
    }
    

(五)CocosStudio编辑动画

可以参考博客:使用cocos studio制作动画 | Metoor
e.g. 增加了两个动画animation1animation2animation1Button节点进行旋转和淡入淡出;animation2Button节点进行伸缩变换。
在这里插入图片描述
保存过后的csd为:

<GameFile>
  <PropertyGroup Name="MainScene" Type="Scene" ID="a2ee0952-26b5-49ae-8bf9-4f1d6279b798" Version="3.10.0.0" />
  <Content ctype="GameProjectContent">
    <Content>
      <Animation Duration="30" Speed="1.0000">
        <Timeline ActionTag="-509134901" Property="RotationSkew">
          <ScaleFrame FrameIndex="0" X="0.0000" Y="0.0000">
            <EasingData Type="0" />
          </ScaleFrame>
          <ScaleFrame FrameIndex="5" X="90.0000" Y="90.0000">
            <EasingData Type="0" />
          </ScaleFrame>
          <ScaleFrame FrameIndex="10" X="180.0000" Y="180.0000">
            <EasingData Type="0" />
          </ScaleFrame>
          <ScaleFrame FrameIndex="15" X="270.0000" Y="270.0000">
            <EasingData Type="0" />
          </ScaleFrame>
          <ScaleFrame FrameIndex="20" X="360.0000" Y="360.0000">
            <EasingData Type="0" />
          </ScaleFrame>
        </Timeline>
        <Timeline ActionTag="-509134901" Property="Scale">
          <ScaleFrame FrameIndex="20" X="4.0000" Y="3.0000">
            <EasingData Type="0" />
          </ScaleFrame>
          <ScaleFrame FrameIndex="25" X="2.0000" Y="1.5000">
            <EasingData Type="0" />
          </ScaleFrame>
          <ScaleFrame FrameIndex="30" X="4.0000" Y="3.0000">
            <EasingData Type="0" />
          </ScaleFrame>
        </Timeline>
        <Timeline ActionTag="-509134901" Property="Alpha">
          <IntFrame FrameIndex="0" Value="255">
            <EasingData Type="0" />
          </IntFrame>
          <IntFrame FrameIndex="5" Value="127">
            <EasingData Type="0" />
          </IntFrame>
          <IntFrame FrameIndex="10" Value="0">
            <EasingData Type="0" />
          </IntFrame>
          <IntFrame FrameIndex="15" Value="127">
            <EasingData Type="0" />
          </IntFrame>
          <IntFrame FrameIndex="20" Value="255">
            <EasingData Type="0" />
          </IntFrame>
        </Timeline>
      </Animation>
      <AnimationList>
        <AnimationInfo Name="animation0" StartIndex="0" EndIndex="20">
          <RenderColor A="150" R="220" G="20" B="60" />
        </AnimationInfo>
        <AnimationInfo Name="animation1" StartIndex="20" EndIndex="30">
          <RenderColor A="150" R="65" G="105" B="225" />
        </AnimationInfo>
      </AnimationList>
      <ObjectData Name="Scene" ctype="GameNodeObjectData">
        <Children>
          <AbstractNodeData Name="Default" ActionTag="953446860">
          </AbstractNodeData>
          <AbstractNodeData Name="Button_1" ActionTag="-509134901">
          </AbstractNodeData>
        </Children>
      </ObjectData>
    </Content>
  </Content>
</GameFile>
  • AnimationList节点记录了动画的起始索引StartIndex、终止索引EndIndex和渲染颜色RenderColor(即图中的红色和蓝色)。
  • Animation节点记录了各个动作的时间线TimeLine(对应cocos2d-x中的ActionTimeLine类型),而TimeLine记录了动作标记ActionTag(和执行动作的节点相关联)、动作属性Property(动作修改了节点的哪些属性)以及关键帧IntFrame

通过csb文件加载并执行动作:

#include "cocostudio/CocoStudio.h"
using namespace cocostudio::timeline;

auto rootNode = CSLoader::createNode("MainScene.csb");
addChild(rootNode);

ActionTimeline* action = CSLoader::createTimeline("MainScene.csb");
if (action) 
{
    rootNode->runAction(action); 
    action->gotoFrameAndPlay(0, false);	// 第2个参数表示是否循环,默认值为true。如果动作只需执行一次,则需要传递参数false
}

ActionTimeLine重写了isDone方法永远返回falsevirtual bool isDone() const override { return false; }),即动画执行结束后并不会被release


(六)动作执行流程

  1. 节点添加动画
    // CCNode.cpp
    Action * Node::runAction(Action* action)
    {
        CCASSERT( action != nullptr, "Argument must be non-nil");
        _actionManager->addAction(action, this, !_running);
        return action;
    }
    
    // CCActionManager.cpp
    void ActionManager::addAction(Action *action, Node *target, bool paused)
    {
        if(action == nullptr || target == nullptr)
            return;
    
        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), "action already be added!");
         ccArrayAppendObject(element->actions, action);
     
         action->startWithTarget(target);	// 动作绑定目标节点
    }
    
  2. director定时访问_actionMananger
    // CCDirector.cpp
    bool Director::init()
    {
        // action manager
    	_actionManager = new (std::nothrow) ActionManager();
    	_scheduler->scheduleUpdate(_actionManager, Scheduler::PRIORITY_SYSTEM, false);
    }
    
    // CCScheduler.h
    template <class T>
    void scheduleUpdate(T *target, int priority, bool paused)
    {
        this->schedulePerFrame([target](float dt){
            target->update(dt);
        }, target, priority, paused);
    }
    
  3. 遍历所有动作并执行其step方法,step方法中一般会调用update方法来执行动画的实际逻辑;如果动画执行结束(isDone())则调用stop方法,最终动作会被release掉(ActionTimeLine除外)。
    // main loop
    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 = static_cast<Action*>(_currentTarget->actions->arr[_currentTarget->actionIndex]);
                    if (_currentTarget->currentAction == nullptr)
                    {
                        continue;
                    }
    
                    _currentTarget->currentActionSalvaged = false;
    
                    _currentTarget->currentAction->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())
                    {
                        _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);
            }
            //if some node reference 'target', it's reference count >= 2 (issues #14050)
            else if (_currentTarget->target->getReferenceCount() == 1)
            {
                deleteHashElement(_currentTarget);
            }
        }
    
        // issue #635
        _currentTarget = nullptr;
    }
    
  4. 基类Actionstep方法和update方法需要子类来重写。
    // CCAction.cpp
    void Action::step(float /*dt*/)
    {
        CCLOG("[Action step]. override me");
    }
    
    void Action::update(float /*time*/)
    {
        CCLOG("[Action update]. override me");
    }
    
    // CCActionInstant.cpp
    void ActionInstant::step(float /*dt*/)
    {
        float updateDt = 1;
        update(updateDt);
    }
    
    void ActionInstant::update(float /*time*/)
    {
        _done = true;
    }
    
    // CCActionInterval.cpp
    void ActionInterval::step(float dt)
    {
        if (_firstTick)
        {
            _firstTick = false;
            _elapsed = 0;
        }
        else
        {
            _elapsed += dt;
        }
        
        float updateDt = std::max(0.0f,                                  // needed for rewind. elapsed could be negative
                                  std::min(1.0f, _elapsed / _duration)
                                  );
    
        if (sendUpdateEventToScript(updateDt, this)) return;
        
        this->update(updateDt);	// updateDt是一个比例,范围在0~1
    
        _done = _elapsed >= _duration;
    }
    
  5. Place派生类重写的update方法:
    void Place::update(float time)
    {
        ActionInstant::update(time);
        _target->setPosition(_position);
    }
    
    MoveBy派生类重写的update方法:
    void MoveBy::update(float t)
    {
        if (_target)
        {
    #if CC_ENABLE_STACKABLE_ACTIONS
    		// 如果支持动作叠加,那么_startPosition在其他动作的影响下可能会发生变化,所以需要对当前位置和MoveBy记录的_previousPosition做diff,然后校正MoveBy的_startPosition
            Vec3 currentPos = _target->getPosition3D();
            Vec3 diff = currentPos - _previousPosition;
            _startPosition = _startPosition + diff;
            Vec3 newPos =  _startPosition + (_positionDelta * t);
            _target->setPosition3D(newPos);
            _previousPosition = newPos;
    #else
            _target->setPosition3D(_startPosition + _positionDelta * t);
    #endif // CC_ENABLE_STACKABLE_ACTIONS
        }
    }
    

【参考资料】
[1] Cocos2D-X游戏开发技术精解 - 第4章 动作功能
[2] 动作(Action) · GitBook
[3] 使用cocos studio制作动画 | Metoor
[4] Cocos2dx源码赏析(4)之Action动作

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值