current position:Home>Action system of coco2d-x-html5

Action system of coco2d-x-html5

2022-04-29 13:13:00Sihang 0701

This article has participated [ New people's creation ceremony ] Activities , Start the road of nuggets creation together .

One 、 Preface

hi , Hello everyone , I'm Sihang . Today, let's take a simple example , Let's study cocos2dx Is the execution process of the action , Finally, implement a custom action class .

Two 、 Code and effects

1、 Code

var pSprite = cc.Sprite.create("src/res/ui/icon.png");
pSprite.setPosition(cc.p(cc.winSize.width/2, cc.winSize.height/2));
cc.director.getRunningScene().addChild(pSprite)
var pMoveByAction = cc.moveBy(1, cc.p(100, 100));
pSprite.runAction(pMoveByAction);
 Copy code 

2、 effect

We played an abscissa and ordinate to move each 100 Pixel action  Please add a picture description

3、 ... and 、 analysis

1、 perform CCDirector.js Of drawScene Method

Why does each frame execute drawScene Method ? Refer to previous articles “coco2d-x-html5 And Director analysis ”

// CCDirector.js
drawScene: function () {
        var renderer = cc.renderer;

        // calculate "global" dt
        //  Calculate slice time ( The time interval between calls between frames )
        this.calculateDeltaTime();

        //tick before glClear: issue #533
        if (!this._paused) {
            //  Scheduler execution update function 
            this._scheduler.update(this._deltaTime);
            cc.eventManager.dispatchEvent(this._eventAfterUpdate);
        }

 Copy code 

stay drawScene Function , We see execution CCScheduler.js Of update function

2、 perform CCScheduler.js Of update function

// CCScheduler.js
/** * 'update' the scheduler. (You should NEVER call this method, unless you know what you are doing.) * @param {Number} dt delta time */
update:function (dt) {
    this._updateHashLocked = true;
    if(this._timeScale !== 1)
        dt *= this._timeScale;

    var i, list, len, entry;

    //  The default is cc.ActionManager
    for(i=0,list=this._updatesNegList, len = list.length; i<len; i++){
        entry = list[i];
        if(!entry.paused && !entry.markedForDeletion)
            entry.callback(dt);
    }
    ......
 }
 Copy code 

2.1 CCActionManager If entered, add to CCScheduler Of _updatesNegList

From the source code above, we can see that , stay CCScheduler.js Of update In the function , Traverse the execution _updatesNegList The elements of the callback function . That means we need to look at CCActionManager Instantiated object , How it was added to CCActionManager Of _updatesNegList Inside

// CCDirector.js
init: function () {
      ......
      //scheduler
      //  Instantiation  Scheduler
      this._scheduler = new cc.Scheduler();
      //action manager
      if (cc.ActionManager) {
      	  //  Instantiation  ActionManager
          this._actionManager = new cc.ActionManager();
          //  take actionManager Sign up to scheduler._updatesNegList
          this._scheduler.scheduleUpdate(this._actionManager, cc.Scheduler.PRIORITY_SYSTEM, false);
      } else {
          this._actionManager = null;
      }
      ...... 
}
 Copy code 

So let's see CCScheduler.js Of scheduleUpdate function

// CCScheduler.js 
 scheduleUpdate: function(target, priority, paused){
    this._schedulePerFrame(function(dt){
         //  After registration , Execute every frame target.update Method 
         target.update(dt);
     }, target, priority, paused);
 },
 Copy code 

scheduleUpdate Called _schedulePerFrame function , So let's continue to play and see

 _schedulePerFrame: function(callback, target, priority, paused){
        ......
        //  According to different priorities , Push into different queues 
        if (priority === 0){
            this._appendIn(this._updates0List, callback, target, paused);
        }else if (priority < 0){
            this._priorityIn(this._updatesNegList, callback, target, priority, paused);
        }else{
            // priority > 0
            this._priorityIn(this._updatesPosList, callback, target, priority, paused);
        }
    },
 Copy code 

above CCDirector.js Of init Inside , Yes

this._scheduler.scheduleUpdate(this._actionManager, cc.Scheduler.PRIORITY_SYSTEM, false);
 Copy code 

because cc.Scheduler.PRIORITY_SYSTEM It's a negative number , namely priority It's a negative number , such ActionManager Just sign up for _updatesNegList Inside .

Here we will understand how to execute CCActionManager.js Of update function .

3、 perform CCActionManager.js Of update function

// CCActionManager.js
/** * @param {Number} dt delta time in seconds */
update:function (dt) {
   var locTargets = this._arrayTargets , locCurrTarget;
   // this._arrayTargets  Is an array , The elements inside elt  It's a dictionary 
   // elt  The format is  {actions: [ action 1, action 2,...], }
   for (var elt = 0; elt < locTargets.length; elt++) {
       this._currentTarget = locTargets[elt];
       locCurrTarget = this._currentTarget;
       //  There is no pause , And actions Valuable 
       if (!locCurrTarget.paused && locCurrTarget.actions) {
           locCurrTarget.lock = true;
           // The 'actions' CCMutableArray may change while inside this loop.
           for (locCurrTarget.actionIndex = 0; locCurrTarget.actionIndex < locCurrTarget.actions.length; locCurrTarget.actionIndex++) {
               locCurrTarget.currentAction = locCurrTarget.actions[locCurrTarget.actionIndex];
               if (!locCurrTarget.currentAction)
                   continue;

               //use for speed
               //  Here is the specific action , such as  cc.MoveBy Instantiated object 
               locCurrTarget.currentAction.step(dt * ( locCurrTarget.currentAction._speedMethod ? locCurrTarget.currentAction._speed : 1 ) );
               
               if (locCurrTarget.currentAction && locCurrTarget.currentAction.isDone()) {
                   locCurrTarget.currentAction.stop();
                   var action = locCurrTarget.currentAction;
                   locCurrTarget.currentAction = null;
                   this.removeAction(action);
               }

               locCurrTarget.currentAction = null;
           }
           locCurrTarget.lock = false;
       }
       // only delete currentTarget i
 Copy code 

Through the above source code , We found that... Was executed here CCAction Of step function Let's see how the next action is added to _arrayTargets Inside

3.1 How actions are added to CCActionManager.js Of _arrayTargets

Let's look at the code in another way . Let's go back to our top example code .

var pSprite = cc.Sprite.create("src/res/ui/icon.png");
pSprite.setPosition(cc.p(cc.winSize.width/2, cc.winSize.height/2));
cc.director.getRunningScene().addChild(pSprite)
var pMoveByAction = cc.moveBy(1, cc.p(100, 100));
pSprite.runAction(pMoveByAction);
 Copy code 

Take a look first CCSprite.js Of runAction function , We found that the function was not rewritten , Let's look at the parent class CCNode.js Of runAction function ,

// CCNode.js
runAction: function (action) {
    cc.assert(action, cc._LogInfos.Node_runAction);
	//  To ActionManager management 
    this.actionManager.addAction(action, this, !this._running);
    return action;
},
 Copy code 

See through the code ,runAction Called actionManager Of addAction Method

// CCActionManager.js
addAction:function (action, target, paused) {
        if(!action)
            throw new Error("cc.ActionManager.addAction(): action must be non-null");
        if(!target)
            throw new Error("cc.ActionManager.addAction(): target must be non-null");

        //check if the action target already exists
        var element = this._hashTargets[target.__instanceId];
        //if doesn't exists, create a hashelement and push in mpTargets
        if (!element) {
            element = this._getElement(target, paused);
            this._hashTargets[target.__instanceId] = element;
            this._arrayTargets.push(element);
        }
        else if (!element.actions) {
            element.actions = [];
        }
		//  Add to actions Inside 
        element.actions.push(action);
        //  Added target object 
        action.startWithTarget(target);
    },
 Copy code 

4、 perform cc.MoveBy Of step function

 Insert picture description here We found that cc.MoveBy No rewriting step function Let's look at the parent class cc.ActionInterval Of step function

step: function (dt) {
     if (this._firstTick) {
         this._firstTick = false;
         this._elapsed = 0;
     } else
     {
         //  Lost time 
         this._elapsed += dt;
     }

     // this._duration  The action is duration 

     //this.update((1 > (this._elapsed / this._duration)) ? this._elapsed / this._duration : 1);
     //this.update(Math.max(0, Math.min(1, this._elapsed / Math.max(this._duration, cc.FLT_EPSILON))));
     var t = this._elapsed / (this._duration > 0.0000001192092896 ? this._duration : 0.0000001192092896);
     // t  The value of is  0 To 1 Between , Current cumulative time / The total action takes time 
     // t  Is the current frame scale , Then this value is multiplied by the total distance to be moved , You can calculate the current offset value 
     t = (1 > t ? t : 1);
     this.update(t > 0 ? t : 0);
 Copy code 

We see in the step It's called in update function , Let's take a look at cc.MoveBy Of updata function


    /** * Called once per frame. Time is the number of seconds of a frame interval. * @param {Number} dt */
    update: function (dt) {
        dt = this._computeEaseTime(dt);
        if (this.target) {
            // this._positionDelta.x :  Total offset x 
            // dt  by  0  To  1  The ratio between  
            var x = this._positionDelta.x * dt;
            var y = this._positionDelta.y * dt;
            var locStartPosition = this._startPosition;
            //  Action superposition 
            if (cc.ENABLE_STACKABLE_ACTIONS) {
                //  Comment out the code of action superposition first , First look at the simple branch 
                ......
            } else {
                this.target.setPosition(locStartPosition.x + x, locStartPosition.y + y);
            }
        }
    },
 Copy code 

It can be seen that the animation is essentially refreshing the coordinates of the wizard every frame .

Four 、 Custom action class

Through the above analysis, we know cc.moveBy How did the action take effect , Then let's customize an action class ,cc.rectBy, This action is within the specified time , Move the square with the specified side length .

4.1 Custom action class code

cc.RectBy = cc.ActionInterval.extend(/** @lends cc.MoveBy# */{
    _positionDelta: null,
    _startPosition: null,
    _previousPosition: null,

    /** * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function. * @param {Number} duration duration in seconds * @param {Number} borderLenght  Square side length  * @param {Number} [deltaY] */
    ctor: function (duration, borderLenght) {
        cc.ActionInterval.prototype.ctor.call(this);
        cc.ActionInterval.prototype.initWithDuration.call(this, duration)
        this._borderLenght = borderLenght;
        this._startPosition = cc.p(0, 0);
    },

    /** * Start the action with target. * @param {cc.Node} target */
    startWithTarget: function (target) {
        cc.ActionInterval.prototype.startWithTarget.call(this, target);
        var locPosX = target.getPositionX();
        var locPosY = target.getPositionY();
        this._startPosition.x = locPosX;
        this._startPosition.y = locPosY;
    },

    /** * Called once per frame. Time is the number of seconds of a frame interval. * @param {Number} dt */
    update: function (dt) {
        dt = this._computeEaseTime(dt);
        if (this.target) {
            // this._positionDelta.x :  Total offset x 
            // dt  by  0  To  1  The ratio between  
            var x = 0;
            var y = 0;
            
            if (dt <= 0.25) { //  rising 
                y = this._borderLenght * dt * 4;
            } else if (dt <= 0.5) { //  Move right 
                y = this._borderLenght;
                x = this._borderLenght * (dt - 0.25) * 4;
            } else if (dt <= 0.75) { //  falling 
                x = this._borderLenght;
                y = this._borderLenght - (dt - 0.5) * this._borderLenght * 4;
            } else { //  Move left 
                x =  this._borderLenght - (dt - 0.75) * this._borderLenght * 4;
            }
                    
            var locStartPosition = this._startPosition;
            this.target.setPosition(locStartPosition.x + x, locStartPosition.y + y);
            
        }
    },

});


cc.rectBy = function (duration, borderLenght) {
    return new cc.RectBy(duration, borderLenght);
};
 Copy code 

4.2 Custom action class representation

var pSprite = cc.Sprite.create("src/res/ui/icon.png");
pSprite.setPosition(cc.p(cc.winSize.width/2, cc.winSize.height/2));
cc.director.getRunningScene().addChild(pSprite)
var pRectByAction = cc.rectBy(2, 100);
pSprite.runAction(pRectByAction);
 Copy code 

 Insert picture description here

copyright notice
author[Sihang 0701],Please bring the original link to reprint, thank you.
https://en.qdmana.com/2022/04/202204291312542706.html

Random recommended