preface

prepare

Have you ever wondered if the world of a game is like a car? How does the “car” start and keep running?

As mentioned above, the content of this paper is mainly the startup process and main loop of Cocos Creator engine.

The content of the main loop also involves: component life cycle and timer, slow system, animation system, physics system…

This article will explain the relationship between the main loop and each module at a macro level, and will briefly introduce each module, but will not go into the concrete implementation of the module.

Because if you “touch” every module, then this article will not be finished.

Go!

Hopefully you will know more about the Cocos Creator engine after reading this article.

At the same time, I also hope that this article can play a role of “master lead the door”, everyone refuelling practice duck ~

In addition, the source code interpretation series (should) be constantly updated, if you want Pippi to interpret a module of the interpretation engine, feel free to let me know, I… I’ll think about it

This article uses Cocos Creator version 2.4.3 as a reference.


The body of the

Start the process

index.html

For Web platforms the index.html file is the absolute starting point.

In the default index.html file, the layout of the game’s startup page is defined, the main.js file is loaded, and there is a piece of code that executes immediately.

Here’s a snippet of key code from the file:

// Load the engine script
loadScript(debug ? 'cocos2d-js.js' : 'cocos2d-js-min.ec334.js'.function () {
    // Is the physical system enabled?
    if (CC_PHYSICS_BUILTIN || CC_PHYSICS_CANNON) {
        // Load the physical system script and start the engine
        loadScript(debug ? 'physics.js' : 'physics-min.js'.window.boot);
    } else {
        // Start the engine
        window.boot(); }});Copy the code

The above code is used to load engine scripts and physical system scripts. Once the script is loaded, it calls the window.boot() function defined in main.js.

๐Ÿ’ก For native platforms, In directory} {project build, JSB – link, frameworks, the runtime – SRC \ Classes \ AppDelegate applicationDidFinishLaunching of CPP file loading the main () function. The js File. (Thank you for letting me sleep big guy’s supplement)

๐Ÿงต code compression

The -min word in the script name usually indicates that the code in the file has been compressed.

Compressed code can save the space occupied by code files, speed up file loading, reduce traffic consumption, but at the same time, it makes the code lose readability, which is not conducive to debugging.

Therefore, after debugging mode is enabled, uncompressed code files are directly used to facilitate debugging and error locating.

main.js

window.boot()

There are also slight differences between main.js content on different platforms, but we’ll ignore the differences here and focus on the key common behaviors.

The main. Js file basically defines the window.boot() function.

๐Ÿ’ก for non-Web platforms, window.boot() is called directly after definition, so main.js is their starting point.

The window.boot() function has the following key behavior inside:

  1. defineonStartCallback function: mainly used to load startup scenarios
  2. cc.assetManager.init(...): Initializes AssetManager
  3. cc.assetManager.loadScript(...): Loads the plug-in script in the SRC directory
  4. cc.assetManager.loadBundle(...): loads the bundle in the project
  5. cc.game.run(...): Start the engine

This part of the code will not be posted, partners can look at their own project after the construction of the main.js file.

cc.game

The cc.game object is an instance of the cc.game class, which contains the game body information and is responsible for driving the game.

In human terms, the cc.game object is the module that manages the engine’s life cycle and is used for start, pause, and restart operations.

CCGame. Js:Github.com/cocos-creat…

run()

The cc.game.run() function specifies the engine configuration and onStart callback and triggers the cc.game.prepare() function.

run: function (config, onStart) {
    // Specify the engine configuration
    this._initConfig(config);
    this.onStart = onStart;
    this.prepare(game.onStart && game.onStart.bind(game));
}
Copy the code

Portal:Github.com/cocos-creat…

prepare()

The main function inside cc.game.prepare() is to quickly compile the project code and call the _prepareFinished() function during the project preview.

prepare(cb) {
    // If there is one, skip it
    if (this._prepared) {
        if (cb) cb();
        return;
    }
    // Load the preview project code
    this._loadPreviewScript(() = > {
        this._prepareFinished(cb);
    });
}
Copy the code

Portal:Github.com/cocos-creat…

For quick compilation details, open your browser’s developer tools during project preview and search (Ctrl + P) __quick_compile_project__ in the Sources bar to find the __quick_compile_project__.js file.

_prepareFinished()

The cc.game._prepareFinished() function initializes the engine, sets the frame rate timer, and initializes the built-in resources (effect and material).

Cc.game._runmainloop () is called to start the engine main loop when the built-in resource is loaded.

_prepareFinished(cb) {
    // Initialize the engine
    this._initEngine();
    // Set the frame rate timer
    this._setAnimFrame();
    // Initialize the built-in resources (load the built-in effect and material resources)
    cc.assetManager.builtins.init(() = > {
        // Print engine version to console
        console.log('Cocos Creator v' + cc.ENGINE_VERSION);
        this._prepared = true;
        / / start the mainLoop
        this._runMainLoop();
        // Fire the 'game_inited' event (the engine is initialized)
        this.emit(this.EVENT_GAME_INITED);
        // Call the onStart function defined in main.js
        if (cb) cb();
    });
}
Copy the code

Portal:Github.com/cocos-creat…

๐Ÿ’ก We must mention the _setAnimFrame() function called in _prepareFinished().

_setAnimFrame()

Cc.game._setanimframe () internally ADAPTS to different game frame rates.

Also on the window. RequestAnimationFrame () encapsulation, interface to do the compatibility to compatible with different browser environment, detailed below us.

The _setAnimFrame() code will not be posted here. If you need it, please refer to it yourself.

Portal:Github.com/cocos-creat…

_runMainLoop()

Cc.game._runmainloop () the name of this function is straightforward, and it is used to run the mainLoop() function.

Let’s look at the code:

_runMainLoop: function () {
    if (CC_EDITOR) return;
    if (!this._prepared) return;
    // Define local variables
    var self = this, callback, config = self.config,
        director = cc.director,
        skip = true, frameRate = config.frameRate;
    // Show or hide performance statistics
    debug.setDisplayStats(config.showFPS);
    // Set the frame callback
    callback = function (now) {
        if(! self._paused) {// Loop calls the callback
            self._intervalId = window.requestAnimFrame(callback);
            if(! CC_JSB && ! CC_RUNTIME && frameRate ===30) {
                if(skip = ! skip)return;
            }
            / / calls mainLoopdirector.mainLoop(now); }};// The loop callback will begin in the next frame
    self._intervalId = window.requestAnimFrame(callback);
    self._paused = false;
}
Copy the code

Portal:Github.com/cocos-creat…

_runMainLoop() uses the window.requestAnimFrame() interface to loop mainLoop().

window.requestAnimFrame()

Window. RequestAnimFrame () is what we said above to _setAnimFrame () internal to the window. The requestAnimationFrame () the compatibility of encapsulation.

On front end not too familiar friend there may be a doubt, the window. The requestAnimationFrame () is what, is used to doing, and how to operate?

window.requestAnimationFrame()

. In simple terms, the window requestAnimationFrame () to the browser requests a redraw (repaint), and before the redraw calls the specified callback function.

Receive a callback window. RequestAnimationFrame () as a parameter and returns an integer as a unique identifier, the browser will before the next redrawing execution the callback; And a callback is passed in with a value equal to that returned by performance.now().

The return value of performing.now () can be simply interpreted as the running time of the browser window, the time difference between opening the window and the current moment.

MDN documents:Developer.mozilla.org/zh-CN/docs/…

The number of callbacks usually matches the number of browser screen refreshes, which means that for a monitor with a refresh rate of 60Hz, the browser will execute 60 callbacks in a second.

Instructions for Windows. RequestAnimationFrame () so far, if you want to know more information please make your own search.

MDN documents:Developer.mozilla.org/zh-CN/docs/…

summary

Draw a diagram to give a little summary of the engine startup process

The main loop

After some twists and turns, we finally arrived at the most anticipated part of the engine’s main loop. Without further ado, let’s continue!

cc.director

The cc.director object is an instance of the director class. The engine uses the cc.director object to manage the logical flow of the game.

CCDirector. Js:Github.com/cocos-creat…

mainLoop()

The ๐Ÿ– cc.director.mainloop () function is probably one of the most critical pieces of logic in the engine, and contains many things that are critical.

Now let’s take a look inside the mainLoop() function.

Here I have selectively removed some code from the function and added some comments:

mainLoop: function(now) {
    // calculate the "global" DeltaTime (DeltaTime)
    // The time elapsed since the last mainLoop call
    this.calculateDeltaTime(now);
    // The game is updated without pausing
    if (!this._paused) {
        // Fires the 'director_before_update' event
        this.emit(cc.Director.EVENT_BEFORE_UPDATE);
        // Call the new (enabled) component's start function
        this._compScheduler.startPhase();
        // Call the (enabled) update function for all components
        this._compScheduler.updatePhase(this._deltaTime);
        // Update Scheduler (cc.scheduler)
        this._scheduler.update(this._deltaTime);
        // Call the lateUpdate function for all components (enabled)
        this._compScheduler.lateUpdatePhase(this._deltaTime);
        // Fires the 'director_after_update' event
        this.emit(cc.Director.EVENT_AFTER_UPDATE);
        // Destroy the recently removed entity (node)
        Obj._deferredDestroy();
    }
    // Fires the 'director_before_draw' event
    this.emit(cc.Director.EVENT_BEFORE_DRAW);
    // Render the game scene
    renderer.render(this._scene, this._deltaTime);
    // Fire the 'director_after_draw' event
    this.emit(cc.Director.EVENT_AFTER_DRAW);
    // update event listener for eventManager (cc.eventmanager deprecated)
    eventManager.frameUpdateListeners();
    // Add up the total number of frames the game runs
    this._totalFrames++;
}
Copy the code

Portal:Github.com/cocos-creat…

Let’s break down the key points in the main loop.

ComponentScheduler

The _compScheduler property in the cc.director object is an instance of the ComponentScheduler class.

Most of you may not be familiar with the ComponentScheduler class, but let me explain it briefly.

The name ComponentScheduler literally translates to “ComponentScheduler,” and as the name suggests, this class is used to schedule components.

Speaking of which, the ComponentScheduler class is used to centrally schedule (manage) the lifecycle of all components (CC.component) in a game scenario.

The text is not intuitive, look at the following picture will probably understand:

Component – the scheduler. Js:Github.com/cocos-creat…

startPhase

// Call the new (enabled) component's start function
this._compScheduler.startPhase();
Copy the code

The component’s start callback is fired before the component is first activated, that is, before the first update is performed.

The start callback will only be triggered once in the lifetime of the component, as will onLoad and onEnable.

Except onLoad and onEnable are managed by instances of the NodeActivator class:

  • onLoadFires when the node is activated
  • onEnableIs triggered when the component is enabled

Start does not fire until the next mainLoop, mainLoop().

๐Ÿฅ NodeActivator

The NodeActivator class is mainly used to enable and disable nodes and their components.

The cc.director object has an instance of _nodeActivator, which is used to enable and disable all nodes in the game.

Like this: cc. Director. _nodeActivator. ActivateNode (this, value);

Node – the activator. Js:Github.com/cocos-creat…

updatePhase

// Call the (enabled) update function for all components
this._compScheduler.updatePhase(deltaTime);
Copy the code

The update function of the component is triggered once every frame.

lateUpdatePhase

// Call the lateUpdate function for all components (enabled)
this._compScheduler.lateUpdatePhase(deltaTime);
Copy the code

The lateUpdate function of the component is triggered after update and the Scheduler cc.Scheduler is updated. Updates to the scheduler include easing, animation, and physics, which are expanded below.

ParticleSystem

BTW, the ParticleSystem component (cc.particlesystem) is updated in the lateUpdate callback function.

CCParticleSystem. Js:Github.com/cocos-creat…

Tips

Be careful with update and lateUpdate callbacks, because they are triggered every Frame. Too much logic in update or lateUpdate will make the execution time (Frame time) of each Frame longer. The number of frames running in the game is reduced or unstable.

๐Ÿ“ข pay attention to this is not not to let you use, the use is still used, but do not abuse, do not what toys are to inside the game ~

Scheduler

The _scheduler attribute of the cc.director object is an instance of the cc.Scheduler class.

Cc.Scheduler is the class responsible for triggering callback functions.

The Scheduler. Js:Github.com/cocos-creat…

๐Ÿ’ฃ You’ll never guess what happens after the execution of this seemingly unremarkable line of code.

// Update the Scheduler (cc.Scheduler instance)
this._scheduler.update(this._deltaTime);
Copy the code

Cc.director.mainloop () uses the _scheduler.update() function to distribute updates, and within the scheduler (cc.director._scheduler) triggers timer updates for each system module and component based on priority.

System module

The scheduler update will first trigger the following system module update:

  • ActionManager
  • AnimationManager
  • CollisionManager
  • PhysicsManager
  • Physics3DManager
  • InputManager

These modules are registered with the scheduler as cc.director._scheduler.scheduleUpdate() because they need to be updated every frame.

All modules except InputManager have a priority of CC.scheduler.PRIORITY_SYSTEM, which is the system priority.

ActionManager

The ActionManager is the Action manager that manages all the actions in the game, the Action and Tween systems (which are essentially the same thing).

CCActionManager. Js:Github.com/cocos-creat…

AnimationManager

AnimationManager is the Animation manager that manages all the animations in the game and drives the Animation component on the node to play the animations.

Animation – manager. Js:Github.com/cocos-creat…

CollisionManager

CollisionManager, the Collision component manager, is used to handle whether collision components have occurred between nodes and invoke the corresponding callback function.

CCCollisionManager. Js:Github.com/cocos-creat…

PhysicsManager

PhysicsManager is a physical system manager that uses Box2D as a 2D physics engine to encapsulate and expose some commonly used interfaces. The PhysicsManager also manages the distribution of collision information.

CCPhysicsManager. Js:Github.com/cocos-creat…

Physics3DManager

Physics3DManager is the 3D physics system manager. The 3D physics engine in Cocos Creator is available as cannons. js and Builtin, and Physics3DManager encapsulates them with a common interface.

Physics – manager. Ts:Github.com/cocos-creat…

InputManager

InputManager is the input event manager used to manage all input events. Developers take the initiative to enable Accelerometer (Accelerometer), engine will regularly sent via InputManager cc SystemEvent. EventType. DEVICEMOTION events (default intervals of 0.2 seconds).

CCInputManager. Js:Github.com/cocos-creat…

Component timer

Most of you have used the schedule() and scheduleOnce() interfaces of a component, which are used to execute functions repeatedly or at a scheduled time.

In fact, the schedule() interface of CC.componentrelies on the cc.scheduler class, which uses the _scheduler instance of the cc.director object.

The component’s schedule() interface encapsulates the cc.director._scheduler.schedule() interface with the component itself as the target, so that scheduled tasks within the component are bound to the component’s life cycle and removed when the component is destroyed.

The scheduleOnce() interface adds another layer of encapsulation to the component’s Schedule () interface, which is fixed to execute only once after a specified time.

CCComponent. Js:Github.com/cocos-creat…

[Document] Use timer:Docs.cocos.com/creator/man…

SetTimeout (), setInterval(), setTimeout(

setTimeout & setInterval

SetTimeout () and setInterval() are both interfaces provided by browsers or runtime such as Node.js.

  • setTimeout()This interface is used to set a timer that executes a function or a specified piece of code when the timer expires.
  • setInterval()Interfaces are used to repeatedly call a function or execute a code segment, with a fixed time delay between calls.

๐Ÿ’ก one more tidbit:

The minimum delay (interval) for setTimeout() and setInterval() in the browser is 4ms.

For inactive tabs (background), the minimum delay (interval) is extended to 1000ms.

๐ŸŒฐ for example

If I set a log output timer every 500ms on the current TAB, when I switch to another TAB, the timer will change to log output every 1000ms.

Like this, if you’re interested, try it out for yourself:

setInterval(() = > {
    console.log(new Date().getTime());
}, 500);
// Simulate output
// The tabs are in the foreground
/ / 1604373521000
/ / 1604373521500
/ / 1604373522000
// Switch to another TAB
/ / 1604373523000
/ / 1604373524000
/ / 1604373525000
Copy the code
Difference & Usage

A component’s timer depends on the engine’s mainLoop() and the component itself. If the engine is paused, the component’s timer is also paused, and if the component or node on which the component is located is destroyed, the timer is also lost.

Both setTimeout() and setInterval() depend on the current window object, which means that setTimeout() and setInterval() will still be executed as long as the current browser TAB is open.

Use a component’s timer when you need to time or repeat a function or operation on a node within the component.

๐Ÿ’ฌ Let’s imagine a scenario:

Using setInterval() in a script in the current scenario to repeatedly move a node in the scenario, what happens when we switch scenarios?

When the timer calls the callback again to try to move the node, an error is reported because the node has been destroyed along with the previous scenario and the timer is still executing.

Timers that use components in this case do not have this problem because timers are cleared when the components are destroyed.

Use setTimeout() or setInterval() when you need to do something unrelated to the context of the game.

๐ŸŽƒ of course, if you can use a component timer, it is best to use a component timer

summary

Again, draw a diagram to briefly summarize the Scheduler.

conclusion

๐ŸŽ‰ so much for the engine startup process and main loop.

If there are omissions or mistakes, you are welcome to put forward, after all, staying up late writing articles trance leakage is also understandable, right ha ha ha ~

Finally, draw a picture to make a final conclusion ~ (๐Ÿคฃ gradually fall in love with drawing ~)


portal

Wechat twitter version

Personal blog: Rookie small stack

Open source home page: Chen PI PI

Eazax-ccc game development scaffolding

Eazax-ccc sample online preview


More share

Why Use TypeScript?

Gaussian Blur Shader

Understanding YAML

Cocos Creator Performance Optimization: DrawCall

Internet Operation Terminology Literacy

“Draw a cool radar in Cocos Creator.”

Write a Perfect Wave with Shader

Gracefully and efficiently managing popovers in Cocos Creator


The public,

Novice small stack

๐Ÿ˜บ MY name is Pipi Chen, a game developer who is still learning, and a Cocos Star Writer who loves sharing.

๐ŸŽจ this is my personal public account, focusing on but not limited to game development and front-end technology sharing.

๐Ÿ’– each original is very carefully, your attention is my original power!

Input and output.