[In-depth understanding of cocos2d-x 3. x] timer (sched) use and principle of inquiry (3), cocos2d-xscheduler
The previous article analyzed the definition of the timer. The focus of this article is how the timer runs.
1. Find the timer callback from main
When talking about the running of the timer, you have to touch the main function of cocos2dx, because the timer runs on the main thread and is not a separate thread, therefore, its call is bound to be called every frame in the main function.
The following code is the main function on the win32 platform.
int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow){ UNREFERENCED_PARAMETER(hPrevInstance); UNREFERENCED_PARAMETER(lpCmdLine); // create the application instance AppDelegate app; return Application::getInstance()->run();}
Directly calling the run function directly enters the run
Int Application: run () {while (! Glview-> windowShouldClose () {QueryPerformanceCounter (& nNow); if (nNow. quadPart-nLast. quadPart> _ animationInterval. quadPart) {nLast. quadPart = nNow. quadPart-(nNow. quadPart % _ animationInterval. quadPart); director-> mainLoop (); // check glview-> pollEvents ();} else {Sleep (1) ;}} return 0 ;}
Other irrelevant calls in the run function have been removed. You can see that the mainLoop function is the real main loop.
Void DisplayLinkDirector: mainLoop () {// do other unrelated things if (! _ Invalid) {drawScene ();}}
The drawScene function is actually called here.
Void Director: drawScene () {if (! _ Paused) {_ schedcher-> update (_ deltaTime); _ eventDispatcher-> dispatchEvent (_ eventAfterUpdate);} // draw only}
DrawScene has a lot to do. I have removed all the painting parts. It is worth noting that the painting scenario will be executed after the timer.
Here we can see that the update function of the timer is actually executed, so what exactly is executed in this update function?
2. update functions of the timer
First, let's take a look at the Update code.
// Main loopvoid Scheduler: update (float dt) {_ updateHashLocked = true; if (_ timeScale! = 1.0f) {dt * = _ timeScale;} // timer callback // tListEntry * entry, * tmp; // For a queue whose priority is less than 0 in the Update timer, run DL_FOREACH_SAFE (_ updatesNegList, entry, tmp) {if ((! Entry-> paused )&&(! Entry-> markedForDeletion) {entry-> callback (dt) ;}// The following is the DL_FOREACH_SAFE (_ updates0List, entry, tmp) {if ((! Entry-> paused )&&(! Entry-> markedForDeletion) {entry-> callback (dt) ;}// Finally, DL_FOREACH_SAFE (_ updatesPosList, entry, tmp) {if ((! Entry-> paused )&&(! Entry-> markedForDeletion) {entry-> callback (dt) ;}// the custom timer for (tHashTimerEntry * elt = _ hashForTimers; elt! = Nullptr;) {_ currentTarget = elt; _ currentTargetSalvaged = false; if (! _ CurrentTarget-> paused) {// traverses all the timers attached to the current object for (elt-> timerIndex = 0; elt-> timerIndex <elt-> timers-> num; ++ (elt-> timerIndex) {elt-> currentTimer = (Timer *) (elt-> timers-> arr [elt-> timerIndex]); elt-> currentTimerSalvaged = false; // in fact, the real callback elt is executed here-> currentTimer-> update (dt); if (elt-> currentTimerSalvaged) {// when the timer ends the task, elt-> currentTimer-> release ();} elt-> currentTimer = nullptr;} should be released ;}} // point to the next object elt = (tHashTimerEntry *) elt-> hh. next; // when all the timers of the object have been executed and the timer attached to the object is empty, the object will be removed from the hash linked list if (_ currentTargetSalvaged & _ currentTarget-> timers-> num = 0) {removeHashElement (_ currentTarget );}} // remove all update timer Elements marked as deleted with a priority less than 0, DL_FOREACH_SAFE (_ updatesNegList, entry, tmp) {if (entry-> markedForDeletion) {this-> removeUpdateFromHash (entry) ;}// remove all update timer Elements marked as deleted with a priority of 0 DL_FOREACH_SAFE (_ updates0List, entry, tmp) {if (entry-> markedForDeletion) {this-> removeUpdateFromHash (entry) ;}// remove all update timer Elements marked as deleted with a priority greater than 0, DL_FOREACH_SAFE (_ updatesPosList, entry, tmp) {if (entry-> markedForDeletion) {this-> removeUpdateFromHash (entry) ;}}_ updateHashLocked = false; _ currentTarget = nullptr ;}
There are three parts worth noting:
For the third point, we will continue to analyze this update function.
Void Timer: update (float dt) {// the first execution will enter this if to initialize if (_ elapsed =-1) {_ elapsed = 0; // executed time _ timesExecuted = 0; // Number of initialization repetitions} else {if (_ runForever &&! _ UseDelay) {// cyclic delay function _ elapsed + = dt; if (_ elapsed >=_ interval) {trigger (); // real callback _ elapsed = 0 ;}} else {// advanced usage _ elapsed + = dt; if (_ useDelay) // latency {if (_ elapsed >=_ delay) {trigger (); // real callback _ elapsed = _ elapsed-_ delay; _ timesExecuted + = 1; _ useDelay = false ;}} else // call each frame {if (_ elapsed >=_ interval) {trigger (); // real callback _ elapsed = 0; _ timesExecuted + = 1 ;}// the callback is complete. Execute the cancel function if (! _ RunForever & _ timesExecuted> _ repeat) {// unschedule timer cancel ();}}}}
The logic of this large piece of code is very clear, and the delay functions are mainly divided into permanent loop delay functions and limited loop delay functions; the latency function of a finite loop can be divided into the call of each frame and the call of a fixed time according to the optimization. The above code is optimized based on this classification.
In fact, the core functions are
trigger();cancel();
The first function is the real callback execution function, and the second function is to remove the execution function.
void TimerTargetSelector::trigger(){ if (_target && _selector) { (_target->*_selector)(_elapsed); }}void TimerTargetSelector::cancel(){ _scheduler->unschedule(_selector, _target);}
The above is the entire process of analyzing the implementation principle of the timer. In the article, I am still not very clear about the implementation of the timer. It should be clearer to go through the code and be more comfortable with future applications.