This paper mainly introduces the use of webGL, cocos2D family history, and analyzes cocos2D-JS source code about resource loading, rendering principle, collision and animation use
Background knowledge
Introduce WebGL
WebGL is a set of Javascirpt API for drawing 2D 3D graphics supported by browsers. The bottom layer is based on OpenGL, and OpenGL is a graphics interface at hardware driver level (similar to DirectX). Therefore, WebGL can achieve 2D 3D graphics rendering at hardware level. Performance is generally better than Canvas rendering
WebGL rendering process:
- Access to elements
let canvas = document.getElementById("canvas");
Copy the code
- Request a drawing context from the Canvas element
let gl = canvas.getContext("webgl");
Copy the code
-
Call the corresponding drawing function in context
-
- Create shader program
// Vertex shader
let VSAHDER_SOURCE = 'void main() {gl_Position, gl_PointSize}'
// Chip shader
let FSAHDER_SOURCE = 'void main() {gl_FragColor}'
Copy the code
- Initialize the shader, compile, and check that it compiled successfully
- Binds the shader to the specified program to determine whether the association is successful
if(! initShaders(gl, VSAHDER_SOURCE, FSAHDER_SOURCE )){return}
Copy the code
- Execute shader program to process vertices and tiles
gl.drawSomething(gl.POINTS, 0.1);
Copy the code
The game engine
What is a game engine? Why do we need a game engine? To put it simply, it helps developers to achieve rapid development:
- Not only about what the user sees, but also about rendering in real time with frequent user interactions
- Provides a complete flow from data traversal, collection to rendering execution
- cross-platform
Introduction to Cocos2D-JS
**** comparison of the Cocos family
cocos2d-js
- Is the JS implementation of the Cocos2D-X game engine
- Cocos2d-js is cross-platform via cocos2D HTML5 and cocos2dx jsbinding
Cocos2d – js architecture diagram
cocos2d | cocos2dx | cocos2d-js | cocos creator | |
---|---|---|---|---|
Development of language | objective-c | c++ | JS, JSB, HTML5 | (Is a development tool) |
scaling | Inherits the node | Inherits the node | Inherits the node | To the node |
Apply colours to a drawing | OpenGL | WebGL | WebGL/Canvas | WebGL/Canvas |
Cross-platform | no | is | is | is |
Environment configuration
- Install python2.x + JDK
- Download cocos2dx, unzip it, run the setup.py file in the directory (NDK ROOT is required if you want to create a mobile application), and then follow the instructions in the source configuration file for the cocos variable to take effect
- Use webstorm’s own server or Xampp to launch a local service to open index.html for game preview
- Initialize a new cocos project command
cocos new <project-name> -l js -d <path>
Copy the code
- Package web-side executable applications
cocos compile -p web -m release
Copy the code
basis
The core code
Coco2dx-3.17.2 /web: Cocos2D: cocos2D: cocos2D: cocos2D: cocos2D: cocos2D: cocos2D: cocos2D: cocos2D: cocos2D: cocos2D
(PS: Most of the code related to game application in this article comes from the JS-test project of Cocos2D-JS)
It is important to have two JS files in the root directory
- CCBoot: The main namespace where all of the engine’s core classes, methods, properties, and constants are defined
- Jsb_apis: Provides API support for Native applications
hierarchy
Here’s an explanation
-
Director: a Director who controls scene creation and switching and exists in singleton mode throughout the life cycle
-
Scene
-
- Basic unit for scene switching, which can set different types of switching effects, titles and other basic rendering information
- Only one scene is allowed to render at the same stage. Here is a scene relationship chain. The appendChild method adds multiple layers to the scene
-
Layer: Layer is the basic unit of scene composition, foreground background layered processing, accept events, Layer can add various elements, CCLayer defines several subclasses at the same time
-
- CCLayerColor: Implements the CCRGBAProtocol
- CCLayerGradient: Painting a gradient background
- CCLayerMultiplex: Manages multiple layers and switches between them
-
You can use the Animation class to animate or pass in the action parameter to move or rotate. There are several ways to create a Sprite
-
- New cc.sprite (fileName) is generated from an image
- New cc.sprite (fileName, rect) is generated from an image and a rectangular area clipping
- New cc.sprite (srpiteFrame), created from frames in the global buffer class SpriteFrameCache
The life cycle
All of the elements mentioned above are subclasses of Node (Sprite, Layer, Menu, Label) and bind coordinates, scaling, rotation information, and other basic attributes needed for drawing
Node has its own life cycle to facilitate flow control
- Ctor: constructor
- OnEnter: calls into Node, where initialization is usually done
- OnEnterDidTransitionFinish: into the Node and the transition animation at the end of the call
- OnExit: Exits the Node call
- OnExitDidTransitionStart: called when exiting Node and the transition animation starts
- Cleanup: called when it is cleared
Based on using
Just like Tencent document defines different types of renderers in Canvas rendering, Cocos2DX also creates corresponding rendering objects according to the requirements of the scene. For example, the following Sprite objects are used to form 2D game characters, buildings and other units. You can set colors and play animations. Or we can build a lot of complex blending modes where the user just needs to pass in a few simple parameters (image, coordinates, action) to render a node element the way we want.
var LogicTest = ActionManagerTest.extend({
title:function () {
return "Logic test";
},
onEnter:function () {
this._super();
var grossini = new cc.Sprite(s_pathGrossini);
this.addChild(grossini, 0.2);
grossini.x = 200;
grossini.y = 200;
grossini.runAction(cc.sequence(
cc.moveBy(1, cc.p(150.0)),
cc.callFunc(this.onBugMe, this)));if ( autoTestEnabled ) {
this._grossini = grossini; }},onBugMe:function (node) {
node.stopAllActions();
node.runAction(cc.scaleTo(2.2));
},
testDuration: 4.0.getExpectedResult:function() {
var ret = [ {"scaleX":2."scaleY":2}];return JSON.stringify(ret);
},
getCurrentResult:function() {
var ret = [ {"scaleX":this._grossini.scaleX, "scaleY":this._grossini.scaleY} ];
return JSON.stringify(ret); }});Copy the code
Initialize the
Start the
First, ccboot. js and main.js files (program entry) are loaded by the Web through index.html. If it is Native, JSB. Js will be loaded through the Native code. Main.js overrides cc.game.onstart () to do view initialization
// Set the game to a fixed orientation
cc.view.setOrientation(cc.ORIENTATION_LANDSCAPE);
// Set the resolution policy size
cc.view.setDesignResolutionSize(800.450, cc.ResolutionPolicy.SHOW_ALL);
// Recalculate the size of the game interface after the browser size changes
cc.view.resizeWithBrowserSize(true);
Copy the code
Load the scene
cc.LoaderScene.preload(g_resources, function () {
if(window.sideIndexBar && typeof sideIndexBar.start === 'function'){
sideIndexBar.start();
}else{
var scene = new cc.Scene();
//TestController is a Layer controller implemented manually
// Is essentially a Layer
scene.addChild(newTestController()); cc.director.runScene(scene); }},this);
Copy the code
Finally launch the game
cc.game.run();
Copy the code
Resource to load
Run () is implemented in ccboot.js. If you start the TestController Layer, you will find that the TestController Layer is undefined. This is because the project script file has not been loaded. CCBoot has two loadConfig and loadText methods to load and parse the project.json text, respectively.
{
"debugMode" : 1."showFPS" : true."frameRate" : 60."noCache" : false."id" : "gameCanvas"."renderMode" : 0."engineDir" : ".. /.. /web/"."modules" : ["cocos2d"."extensions"."external"]."plugin": {
"facebook": {
"appId" : "1426774790893461"."xfbml" : true."version" : "v2.0"}},"jsList" : [
"src/BaseTestLayer/BaseTestLayer.js"."src/tests_resources.js"]},Copy the code
During CCBoot, the jsList file was loaded in the final prepare method
// Load game scripts
var jsList = config[CONFIG_KEY.jsList];
if (jsList) {
cc.loader.loadJsWithImg(jsList, function (err) {
if (err) throw new Error(err);
self._prepared = true;
if (cb) cb();
});
}
else {
if (cb) cb();
}
Copy the code
Add “SRC/testtests-main. js” to jsList to load the TestController. At the same time, it is observed that cc.loader is a singleton. All types of external resources can be registered to cc.loader through the mode of plug-in mechanism
- Load Entry API for loading resources
- LoadJS /loadJsWithImg Loads js files.
- LoadXXX (Txt, Img, Binary, BinarySync) loads the XXX file
- The register register loader
- GetRes retrieves cached data
- Release Releases cached data
CCLoader registered
cc.loader.register(["font"."eot"."ttf"."woff"."svg"."ttc"], cc._fontLoader);
Copy the code
The SRC directory contains the ressource.js resource configuration file. In cocos2dx V3, the configuration format is as follows (font is used as an example), and the loader type is font, and the resource cache key is Thonburi
var g_fonts = [
//@face-font for WebFonts
{
type:"font".name:"Thonburi".srcs: [".. /cpp-tests/Resources/fonts/Thonburi.eot".".. /cpp-tests/Resources/fonts/Thonburi.ttf"]]},Copy the code
The scene and resource file bindings are bound in a handler
{
title:"Font Test",
resource:g_fonts,
platforms: PLATFORM_ALL,
linksrc:"src/FontTest/FontTest.js", testScene:function () { return new FontTestScene(); }},Copy the code
Finally, load the related scene resources in the callback function of scene switch
cc.LoaderScene.preload(res, function () {
var scene = testCase.testScene();
if(scene) { scene.runThisTest(); }},this);
Copy the code
The image processing
Cocos mainly uses texture cache to process image resources. Generally, texture objects are cached in TextCache during the preload stage of scene switching. When Sprite objects are created, the program will request data from TextCache by default. The texture object calls the underlying drawing interface to complete the filling of pixel information
var tex = cc.textureCache.getTextureForKey(filename);
if(! tex) { tex = cc.textureCache.addImage(filename); }Copy the code
Voice processing
- Music: long playing time, usually used as background music
- Sound effects: Temporary sound effects
Cocos2djs’ AudioEngine encapsulates music and sound effects and provides a variety of API call methods
An AudioId is returned when a sound file is played, and the sound can be processed each time using this ID
aId = audioEngine.playEffect("music/sound.mp3", _loop);
Copy the code
AudioEngine plays sound resources in two ways
- WebAudio: Good compatibility, but too much cache usage
cc.Audio.WebAudio = function (buffer) {
this.buffer = buffer;
}
Copy the code
- DomAudio (auto-play-audio) : caches only elements, but has poor compatibility
Initialize the renderer process
Apply colours to a drawing
Rendering process
- Activation:
Execute _runMainLoop to start the main loop, and director to draw the scene. Before drawing the scene, all nodes will be looped through before rendering.
- Traversal and rearrangement
The rendering order of each node depends on three orders: priority globalZOrder>localZOrder>arrivalOrder. The higher the order value of the same priority, the later the rendering
- GlobalZOrder: is set via Node::setGlobalZorder
- LocalZOrder :set with Node::setLocalZorder
- OrderOfArrival: Set when a node is added to a parent node, resulting from a global quantity summation
Before drawing, cocos will traverse all children nodes starting from ccScene, and the visit function is implemented as follows
Cocos2dx Cocos3.0 does not immediately render the calculated rendering data after executing visit() to collect data of each node. Instead, it first encapsulates the data with RenderCommand as the base class to generate the drawing command of openGL. Push into a CommandQuere after waiting after performing the render sequentially
rej::RenderCommand()
: _type(RenderCommand::Type::UNKNOWN_COMMAND)
, _globalOrder(0)
{
}
Copy the code
Each CMD saves the data required by webGL rendering. LabelTTFWebGLRenderCmd is shown as follows
The queue holds webGL drawing function commands for different types of base elements
- ReorderChildDirty is set to true every time a new chLID or child is reordered
- Nodes with localZOrder less than 0 are traversed first each time, followed by push of the current node, and finally nodes greater than 0 (middle order).
Cocos2dx-js actually uses the Node localZOrder to adjust the structure of the node tree. Nodes with localZOrder less than 0 are placed in the left subtree, and those greater than 0 are placed in the right subtree. The recursive method is adopted to conduct middle-order traversal of the nodes in the UI tree. First, the localZorder of the node is compared, and the arrivalOrder of the node is compared. In the same case, the arrivalOrder of the node is compared, and the node of the top is rendered first
- draw
Renderer. Render (cc._renderContext) starts loop rendering of all elements, passing the calculated node data to opeGL’s buffer object, and then performing batch rendering
The collision
Anchor points and coordinates
Cocos2dx uses the OpenGL coordinate system. The starting position (0, 0) is located at the lower left corner of the screen. The archorPoint anchor point is a two-dimensional coordinate, and the value is taken from 0 to 1
The node position is determined by position and Archor point. The default anchor point of Layer is (0,0), namely the lower left corner, and the default anchor point of Sprite is (0.5, 0.5), namely the center. The anchor point is actually the origin of the image. It can be understood that there is a picture anchor point (0.5,0.5), which is the center of the picture. We regard this point as the origin of the picture, and the picture starts from this point, and then combines it with position
Physics engine
Cocos2d integrates the open source physics engine with PhysicsSprite, a subclass of Sprite bound to a physical Body, Multiple physical engines cannot be started at the same time (only one of the preprocessing macros CC_ENABLE_CHIPMUNK_INTEGRATION, CC_ENABLE_CHIPMUNK_INTEGRATION, and CC_ENABLE_BOX2D_INTEGRATION can be defined) :
- Chipmunk
- Objective-Chipmunk
- Box2d
When the cocos2D Sprite object is bound to the physics engine, parameters such as mass density and friction coefficient can be assigned to make the Sprite behave more like the real physical world
Here are some of the methods used in collision detection
Axisymmetric bounding box collision detection
The familiar collision detection method is rectangular collision detection
Cc.rect () to create a regular rectangle
cc.rect = function (x, y, w, h) {
return {x: x, y: y, width: w, height: h };
};
Copy the code
The x and y here are the bottom left corner of the rectangle, which is origin, or minX, minY.
getBoundingBox(); Methods The range of collision objects to be detected was obtained, and then rectIntersectsRect() was selected. Method to judge whether there is overlap between the range of touch points and detection points, if there is collision
// Determine whether to intersect
cc.rectIntersectsRect = function (rect1, rect2) {
var max1x = rect1.x + rect1.width,
max1y = rect1.y + rect1.height,
max2x = rect2.x + rect2.width,
max2y = rect2.y + rect2.height;
return! (max1x < rect2.x || max2x < rect1.x || max1y < rect2.y || max2y < rect1.y); };Copy the code
In the same principle, we can also judge whether a certain rectangular element is hit by obtaining position of a click. However, we can only detect regular graphics or objects here, if complex graphics are unfriendly
SAT collision detection
In addition to the irregular graphics outer package once again after boundingRect axisymmetric case detection, in order to improve accuracy, sometimes we need to test the irregular graphics, general testing convex polygon collision can use separation axis theorem, the two convex polygons, when there is a straight line, make two polygons projection don’t want to pay on the line, So these two polygons do not want to cross, this line is called the separation axis, so writing code generally consider: determine the projection axis, projection to the projection axis, determine the overlap
To find the projection axis is to find the axis on which the normal vectors of each side of the polygon are parallel
A simple way to calculate the projection of a polygon on a line is to calculate the projection of all vertices on a line and obtain coordinates with a minimum maximum value
// let p; / / p to the original point = k * x + y/b/projection axis x1 = (k * (p) [1] - b + p [0])/(k + 1) * k; y1=k*x1+b; M=[x1 y1] // Projection point coordinatesCopy the code
Finally, we compare the difference values of the corresponding coordinate values and judge whether there is projection intersection to obtain the collision detection results
Ball collision detection
function isCollision(a, b) {
// To find the distance between the point and the center of the circle, we use the Pythagorean theorem
var dis = Math.sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
return dis < (a.r + b.r)
}
Copy the code
Spherical and rectangular collision detection
function isCollision(a, closePoint) {
// To find the distance between the point and the center of the circle, we use the Pythagorean theorem
var dis = Math.sqrt((a.x -closePoint.x) * (a.x -closePoint.x) + (a.y - closePoint.y) * (a.y - closePoint.y));
return dis < a.r
}
Copy the code
quadtree
According to the above method, if every two objects need to calculate a collision, usually the performance overhead is very high. Therefore, the method of space partition is generally used to group different graphics to ensure that only the graphics objects in the group may have collisions, which greatly reduces the calculation times
This structure is called a quadtree, quadtree can along with the increase in number of image objects and deeper level, this recursive constructed tree will set up a hierarchy number or the number of architectural elements as the threshold value, to provide the termination conditions, if lead to three-dimensional space, and octree structure is constructed, in principle is similar
BVH surrounded by body
Sometimes what we need to calculate is not the collision between objects, but whether a line intersects a graph object at a certain moment, which is usually used in the case of bounding body (BVH). If you are familiar with clustering algorithm, you should know that if we have a two-dimensional space, first we randomly specify a center point, And then we compute the distance from the center of the object set to the center, and then we recalculate the center and after repeated iterations, we get n clusters.
In BVH, each cluster is a rectangular bounding body, which is regarded as a complete tree structure constructed by a single node according to certain rules
It has the following characteristics:
- Leaf nodes represent graphic objects, and non-leaf nodes represent graphic objects
- Number of leaf nodes = number of non-leaf nodes -1
- The order of leaf nodes is optional
animation
Cocos2d-js animation is divided into frame by frame play and PLIST play (cut map frame play) two ways
Frame by frame to play
Cocos2d-js implements frame-by-frame playback by Animation. First, instantiate an Animation instance and a Sprite object, set the playback time, and call addSpriteFrameWithFile to add pictures of all frames and set the interval of each frame. RepeatForever encapsulates the action and assigns it to sprite.runAction().
Plist play
Plist playback is a large image that contains all the frames of the animation process. It is generally divided into different arrays according to the direction of the large image, and added to an AnimationCache instance to cache the animation. Take the corresponding frame animation in different directions and wrap it as an Action like a frame by frame animation and pass it to sprite.runAction()
reference
- Cocos2d-x Api documentation docs.cocos2d-x.org/api-ref/cpl…