Source code analysis of actions in cocos2dx

Source: Internet
Author: User

Source code analysis of actions in cocos2dx

Action is a very important role played by cocos2dx. Many special effects are implemented by cocos2dx, and it can produce a lot of Results conveniently,

You do not need to have too much relevant knowledge reserves or implementation skills. With the learning idea, let's take a look at the action Process Analysis in cocos2dx.

Right.

 

[ActionManager]

 

Generally, the action entry is:

 

 

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

 

It is an excuse for node, and node is the root class of most classes in cocos2dx. This means that all the cocos2dx classes can call this interface,

It also means that you can add special effects to it.

 

The _ actionManager member is assigned a value in the Node: Node () constructor, as follows:

 

 _actionManager = director->getActionManager();

The director class is obtained from the director class, and the director class is the most basic function class in cocosdx. In its init method,

 

 

_ ActionManager = new ActionManager (); // After initialization, update _ scheduler-> scheduleUpdate (_ actionManager, schedupdate: PRIORITY_SYSTEM, false) will be registered immediately );

After initialization, the update event is registered. Therefore, the update method of ActionManager is called at each frame to update the action.

 

Next, analyze ActionManager: addAction to see how action is added to ActionManager.

 

Void ActionManager: addAction (Action * action, Node * target, bool paused) {CCASSERT (action! = Nullptr, ""); CCASSERT (target! = Nullptr, ""); tHashElement * element = nullptr; // we shoshould convert it to Ref *, because we save it as Ref * tmp = target; // find the corresponding data (tHashElement) HASH_FIND_PTR (_ targets, & tmp, element) based on the target; // if (! Element) {// construct the tHashElement space element = (tHashElement *) calloc (sizeof (* element), 1); element-> paused = paused; // target is referenced here, to prevent the host from being released when the action is being updated, retain target-> retain (); element-> target = target; // Add it to _ targets, use the target pointer As keyHASH_ADD_PTR (_ targets, target, element);} // allocate the member space actionAllocWithHashElement (element) in the element; // CCASSERT (! CcArrayContainsObject (element-> actions, action), ""); // put action into element-> actionsccArrayAppendObject (element-> actions, action ); // set the action object action-> startWithTarget (target );}

The method involved is as follows:

 

 

// Allocate the tHashElement member space void ActionManager: actionAllocWithHashElement (tHashElement * element) {// allocate the action bucket, the default value is 4 // 4 actions per Node by default if (element-> actions = nullptr) {element-> actions = ccArrayNew (4 );} else if (element-> actions-> num = element-> actions-> max) // when the action is full, the space doubles {ccArrayDoubleCapacity (element-> actions );}}

 

 

// Put the action into elementvoid ccArrayAppendObject (ccArray * arr, Ref * object) {CCASSERT (object! = Nullptr, "Invalid parameter! "); // Here, the action retain is an object-> retain (); // place arr-> arr [arr-> num] = object at the end of the action sequence; arr-> num ++ ;}

// Set the void action object of the Action: startWithTarget (Node * aTarget) {_ originalTarget = _ target = aTarget ;}

Now, our action has been added to ActionManager. Now, the cocos2dx framework is used to drive the action. As we have analyzed above, ActionManager: update

Call each frame. We will analyze the update
 

 

Void ActionManager: update (float dt) {// traverse _ targets for (tHashElement * elt = _ targets; elt! = Nullptr;) {// _ currentTarget = elt; _ currentTargetSalvaged = false; // the object is not paused if (! _ CurrentTarget-> paused) {// traverses all actions of The object. // an English prompt indicates that The actions may change in The loop. // The 'actions 'MutableArray may change while inside this loop. for (_ currentTarget-> actionIndex = 0; _ currentTarget-> actionIndex <_ currentTarget-> actions-> num; _ currentTarget-> actionIndex ++) {// current action _ currentTarget-> currentAction = (Action *) _ currentTarget-> actions-> arr [_ currentTarget-> actionIndex]; if (_ currentTarget-> currentAction = nullptr) {continue;} _ currentTarget-> currentActionSalvaged = false; // callback action :: step _ currentTarget-> currentAction-> step (dt); if (_ currentTarget-> currentActionSalvaged) {// here, action: release. Remember the previous analysis, at // void ccArrayAppendObject (ccArray * arr, Ref * object), retain a moment. // 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 () // action completed {// callback action :: stop _ currentTarget-> currentAction-> stop (); // remove this action * Action = _ currentTarget-> currentAction; // Make currentAction nil to prevent removeAction from salvaging it. _ currentTarget-> currentAction = nullptr; // when this action is removed, it will change the _ currentTargetSalvaged flag, // It will also change _ currentTarget-> actions-> numremoveAction (action );} // clear the current 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); // if this actions has no action and _ currentTargetSalvaged is true // 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 ;}

To put it simply, update is to traverse _ targets. In other words, it is to traverse all Node objects that call the runAction method and execute the step method of its Action. Each frame is passed in.

 

The elapsed time. After processing, check whether the action is complete. If it is complete, remove the action. At the end, if the actions of the tHashElement have no action, remove it.

The tHashElement involves the following method analysis:

 

Void ActionManager: removeAction (Action * action) {// explicit null handling if (action = nullptr) {return ;} // find tHashElement * element = nullptr corresponding to this action; Ref * target = action-> getOriginalTarget (); HASH_FIND_PTR (_ targets, & target, element); if (element) {// obtain the index auto I = ccArrayGetIndexOfObject (element-> actions, action) of the action in actions; if (I! = CC_INVALID_INDEX) {// action removeActionAtIndex (I, element);} else {CCLOG ("cocos2d: removeAction: Target not found ");}}

The method involved is as follows:

 

 

Void ActionManager: removeActionAtIndex (ssize_t index, tHashElement * element) {Action * action = (Action *) element-> actions-> arr [index]; // if this action is the currently processed action, repeat this action and set the // currentActionSalvaged flag if (action = element-> currentAction &&(! Element-> currentActionSalvaged) {element-> currentAction-> retain (); element-> currentActionSalvaged = true;} // here, the action at the index location is released, combine the positional relationships of the remaining actions. // if the final parameter is set to true, the object ccArrayRemoveObjectAtIndex (element-> actions, index, true) needs to be cleared. // The current index is removed, the subsequent index is moved forward to fill, so the cyclic index should be rolled back. // It is only equal to the actual condition. // update actionIndex in case we are in tick. looping over the actions if (element-> actionIndex >=index) {element-> actionIndex --;} // After the index is removed, there is no action in actions. if (element-> actions-> num = 0) {// The currently processed element, is to remove the tHashElement to which the action belongs. // it means that this tHashElement has no action, and it indicates that it wants to remove the _ targets if (_ currentTarget = element) {// It is the currently processed tHashElement, hold the removal, and Mark _ currentTargetSalvaged = true;} else {// If the element is not currently processed tHashElement, remove deleteHashElement (element );}}}

Then:

 

 

Void ccArrayRemoveObjectAtIndex (ccArray * arr, ssize_t index, bool releaseObj/* = true */) {// do you want to clear the CCASSERT (arr & arr-> num> 0 & index> = 0 & index <arr-> num, "Invalid index. out of bounds "); if (releaseObj) {CC_SAFE_RELEASE (arr-> arr [index]);} // The number of actions minus one arr-> num --; // this location is removed. Enter ssize_t remaining = arr-> num-index; if (remaining> 0) {memmove (void *) & arr-> arr [index], (void *) & arr-> arr [index + 1], remaining * sizeof (Ref *));}}

At this point, the ActionManager part is the Aciton framework part. At this point, the context has come out, and Scheduler is driving all of this. We only need to call runAction,

 

Just give the Action to ActionManager,

 

[Action]

Next we will analyze the system of action and how to implement the action, which has created such a rich set of action effects,

The following is the definition of the key action class I have removed.

 

class CC_DLL Action : public Ref, public Clonable{public:/** 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;//! 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.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);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);protected: 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;}

Several pieces of information are disclosed.

 

1. This class inherits from Clonable

 

class CC_DLL Clonable{public:    /** returns a copy of the Ref */    virtual Clonable* clone() const = 0;    /**     * @js NA     * @lua NA     */    virtual ~Clonable() {};}

The clone interface is defined. Because action is frequently used, cloning is an important feature,

 

2. There is a reverse interface, which indicates that reverse is also a common feature. An action often provides a reversal action, but it does not always implement this reversal.

3. step is called by the Framework. You must be careful when implementing it yourself. It will be analyzed later. This interface basically does not need to be overloaded.

4. The member variable of the action has an original target and a current target, indicating that the target is a simple value assignment and is not responsible for maintaining the reference.

 

This action is an abstract class that specifies some interfaces to provide operation interfaces for ActionManager.

Next let's take a look at the most common limited-time actions. The important parts of my removal are as follows:

 

class CC_DLL FiniteTimeAction : public Action{protected:    //! duration in seconds    float _duration;}

 

In fact, it adds a time period. It is also an abstract class with no specific function. Let's take a look at its specific application and the interval action (ActionInterval)

 

Class CC_DLL ActionInterval: public FiniteTimeAction {public: // The condition for completion, that is, the passing time is greater than the action range virtual bool isDone (void) const override; // you need to analyze it here, this is the basic virtual void step (float dt) override; protected: float _ elapsed; bool _ firstTick;} implemented by reverse ;};

Note the implementation of step

 

 

Void ActionInterval: step (float dt) {// The first call. if (_ firstTick) {_ firstTick = false; // the elapsed time is initialized. _ elapsed = 0 ;} else {// record the elapsed time and _ elapsed + = dt;} // This expression is expressed as the update parameter, which is the ratio of the elapsed time to the entire action // time, instead of the time interval. // _ Elapsed = 0, that is, update (0) // _ elapsed = _ duration is update (1) this-> update (MAX (0, // needed for rewind. elapsed cocould be negative MIN (1, _ elapsed/MAX (_ duration, FLT_EPSILON) // division by 0 )));}

Let's analyze an instance. Repeat implementation

 

Secondary data of Repeat is as follows:

 

Protected: // Number of repeaters unsigned int _ times; // Number of repeaters unsigned int _ total; // ratio of the time of repeated actions, used to measure _ total, float _ nextDt; // mark whether the action is an instantaneous action bool _ actionInstant;/** Inner action * // The repeated action FiniteTimeAction * _ innerAction;

The details are all commented on.

 

 

Void Repeat: startWithTarget (Node * target) {// initialize repeated times _ total = 0; // percentage of the total time of this action _ nextDt = _ innerAction-> getDuration ()/_ duration; ActionInterval: startWithTarget (target ); // The internal action also initializes the object _ innerAction-> startWithTarget (target );}

The core implementation of update is as follows:

 

 

Void Repeat: update (float dt) {// current time ratio, which exceeds the time ratio of one action if (dt >=_ nextDt) {// This situation occurs, only the card is available. // The time ratio exceeds the action time ratio, and the number of times does not reach the target while (dt> _ nextDt & _ total <_ times) {// The target action is directly updated _ innerAction-> update (1.0f); // The number of repeated occurrences increases _ total ++; // The Action stops and starts again, make sure that the callback function of the target action calls _ innerAction-> stop (); _ innerAction-> startWithTarget (_ target ); // calculate the next target percentage _ nextDt = _ innerAction-> getDuration ()/_ duration * (_ total + 1);} // The total time proportion is completed, but the number of times is not Yes, usually critical. // fix for issue #1288, incorrect end value of repeat if (dt> = 1.0f & _ total <_ times) {_ total ++;} // don't set an instant action back or update it, it has no use because it has no duration if (! _ ActionInstant) {if (_ total = _ times) {_ innerAction-> update (1); _ innerAction-> stop ();} else // run the last frame {// issue #390 prevent jerk, use right update _ innerAction-> update (dt-(_ nextDt-_ innerAction-> getDuration ()/_ duration ));}}} else {// update of the target action (time ratio of a single action), _ innerAction-> update (fmodf (dt * _ times, 1.0f ));}}

The above is a little analysis of action, and I hope it will be helpful to you.

 

 

 

 

 

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.