20, Cocos2dx 3.0 game development to find small three Cocos2d-x action mechanism: Hey, good! So far?

Source: Internet
Author: User

 

Ding Wei Wen huijun Jie Niu, the touch of the hand, shoulder leaning, the foot of the performance, the knee of the rolling, suddenly to ran, playing a knife Yan ran, do not sound. ----- Zhuang Zhou, Pre-Qin Dynasty, Zhuangzi, Health Care master


After learning the full set of action usage, I can not help but feel very curious, what is the action mechanism in the Cocos2d-x is how to achieve? Then we are like ding Jie Niu. Let's unveil the mysteries of the action mechanism step by step.
First, analyze some member functions and member variables of Action and its subclass (mainly FiniteTimeAction and its subclass, we will use these variables and functions to analyze the basic process of the action. You can see from the definition of Action:

/** @brief Base class for Action objects. */class CC_DLL Action : public Ref, public Clonable{public:    /// Default tag used for all the actions    static const int INVALID_TAG = -1;    /**     * @js NA     * @lua NA     */    virtual std::string description() const; /** returns a clone of action */ virtual Action* clone() const = 0;    /** returns a new action that performs the exactly the reverse action */ virtual Action* reverse() const = 0;    //! return true if the action has finished    virtual bool isDone() const;    //! called before the action start. It will also set the 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. DON'T override unless you know what you are doing.    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    */    virtual void update(float time);        inline Node* getTarget() const { return _target; }    /** The action will modify the target properties. */    inline void setTarget(Node *target) { _target = target; }        inline Node* getOriginalTarget() const { return _originalTarget; }    /** Set the original target, since target can be nil.    Is the target that were used to run the action. Unless you are doing something complex, like ActionManager, you should NOT call this method.    The target is 'assigned', it is not 'retained'.    @since v0.8.2    */    inline void setOriginalTarget(Node *originalTarget) { _originalTarget = originalTarget; }    inline int getTag() const { return _tag; }    inline void setTag(int tag) { _tag = tag; }protected:    Action();    virtual ~Action();    Node *_originalTarget;    /** The target.    The target will be set with the 'startWithTarget' method.    When the 'stop' method is called, target will be set to nil.    The target is 'assigned', it is not 'retained'.    */    Node *_target;    /** The action tag. An identifier of the action */    int _tag;private:    CC_DISALLOW_COPY_AND_ASSIGN(Action);};

The FiniteTimeAction inherited from the Action mainly adds a member variable: float _ duration, which is used to save the total completion time of the Action. For the two subclasses ActionInstant and ActionInterval of FiniteTimeAction, the former does not add any functions and variables, while the latter adds two member variables float_elapsed; and bool_firstTick; here, _ elapsed is the elapsed time from the start of the action, and _ firstTick is a control variable.
Next, let's take a look at the update of the Action: When we call the runAction (action * Action) method for Node, the Action Manager (action Manager) is a singleton object) the new Action and the corresponding target node are added to the Action table managed by the node. In the addAction method of ActionManager, after an Action is added to the Action queue, the member function startWithTarget (Node * pTarget) of the Action is called to bind the Action executor. In the subclass of Action (such as ActionInterval), some parameters are also initialized: Let's see the startWithTarget method of ActionInterval:
virtual void startWithTarget(Node *target) override;void ActionInterval::startWithTarget(Node *target){    FiniteTimeAction::startWithTarget(target);    _elapsed = 0.0f;    _firstTick = true;}

After these preparations are completed, the system will refresh the screen at each frame, traverse every action in its action table in ActionManager, and call the step (float dt) method of the action. The step method calculates the value of _ elapsed and calls the update (float time) method. The related code is as follows:
void ActionInterval::step(float dt){    if (_firstTick)    {        _firstTick = false;        _elapsed = 0;    }    else    {        _elapsed += dt;    }        this->update(MAX (0, // needed for rewind. elapsed could be negative                      MIN(1, _elapsed /                          MAX(_duration, FLT_EPSILON) // division by 0                          )                      )                 );}
The time parameter passed in the update method indicates the ratio of the elapsed time to the time required for the completion of the action, which is a number between 0 and 1, that is, the percentage of the completion of the action.
ActionInterval does not further implement the update method. The following describes how to implement the update function by taking the update method inherited from the ActionInterval RotateTo action as an example. The implementation code is as follows:
void RotateTo::update(float time){    if (_target)    {        _target->setRotationSkewX(_startAngleX + _diffAngleX * time);        _target->setRotationSkewY(_startAngleY + _diffAngleY * time);    }}

Here we can see the entire workflow of the Cocos2d-x action mechanism. In RotateTo, the final operation is to modify the Rotation attribute value of the target node and update the Rotation attribute value of the target node. Finally, after each frame is refreshed, The isDone function of each action in the Action queue returns true in the update method of the ActionManager class. If true is returned, the action is completed and deleted from the queue.
The code of the isDone function is as follows:
virtual bool isDone(void) const override;bool ActionInterval::isDone(void) const{    return _elapsed >= _duration;}

For different sequence classes, although the overall process is generally to call the step method first, and then update the attribute of the target node according to the specific definition of each action, the specific implementation of different actions will be different. For example, the isDone function of the RepeatForever action always returns false because it is always executed. For example, in the step function of ActionInstant and its subclass, the parameter value passed to update is always 1, because the instantaneous action is completed after the next frame is refreshed, you do not need to perform the update multiple times.
After learning how Action is updated in each frame, let's look back at how Action Manager works. When initializing ctor, ActionManager is also initialized. The following code is part of the Director: init () method:
// Action manager _ actionManager = new ActionManager (); _ schedupdate-> scheduleUpdate (_ actionManager, schedupdate: PRIORITY_SYSTEM, false );

As you can see, after the ActionManager is initialized, The scheduleUpdate method of the scheduled Scheduler is called immediately. In the scheduleUpdate function, we have registered a regular update service for ActionManager, which means that both the action scheduling and the timer scheduling are controlled by Scheduler. Specifically, we can conveniently pause or resume timer and action operations at the same time without having to consider their non-synchronization issues. Schedmanager triggers the update method registered by ActionManager when each frame is updated. The following code shows the ActionManager: update method. At this time, ActionManager updates every action. Similar to Scheduler, in order to prevent the table traversed during the action scheduling process from being modified, the Cocos2d-x carefully processes the deletion of the action, ensure that the action can be safely deleted under any circumstances:
// Main loopvoid ActionManager: update (float dt) {// enumerate each target node in the action table for (tHashElement * elt = _ targets; elt! = Nullptr;) {_ currentTarget = elt; _ currentTargetSalvaged = false; if (! _ CurrentTarget-> paused) {// The 'actions' MutableArray may change while inside this loop. // enumerate every action corresponding to the target node // The actions array may be modified in the loop. Therefore, you need to be careful when processing 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; // trigger action update _ 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) ;}// issue #635 _ currentTarget = nullptr ;}


Haomeng master friendship reminder: Ding Jie Niu, the magic of its skills, for learning, you need to know its own, but also to know its nature ,,,

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.