This article will discuss how to do simple 2D pathfinding on a custom ground grid and how to use physics engine based motion to get objects along the path to the destination once the path is determined. Readers need to have some prior knowledge of WebGL and Babylonjs, refer to the WebGL introductory video tutorial I recorded and the translated official introductory documentation, or learn in any other way you like.

The article is mainly divided into the following parts:

1. Customize ground grid and pathfinding matrix

2. Generate 3D model in Babylon format

3. Use the PathFinding library for 2D pathfinding

4. Move objects along paths based on the cannon.js physics engine

Scene can be accessed through http://ljzc002.github.io/FPS3/index.html, complete code can look up on the https://github.com/ljzc002/ljzc002.github.io.

The scene is as follows:

Use WASD to control the free camera position, move the mouse to control the perspective, right click on the ground will place a “target square” on the ground, and then a small ball marked with the “farmer” logo will move towards the target square.

The 2DA* pathfinding algorithm is used in the scenario, as shown below:

When the target square is on the other side of the obstacle, the farmer tries to find the shortest way around the obstacle to the target square.

1. Customized ground grid and pathfinding matrix:

A. Customize the ground grid in the Babylon. Js rendering engine

1 var vdata_ground=new BABYLON.VertexData.CreateGround({width:198,height:198,subdivisionsX:99,subdivisionsY:99}); Var arr_vposition=vdata_ground.positions; 3 var map=[]; 4 var len=arr_vposition.length/3; 5 for(var I =20; i<81; I++)// for(var j=20; j<23; J++) / / for this range of column 8 {9 arr_vposition (I) (j + 100 * * 3 + 1) = 1; 10 } 11 } 12 for(var i=20; i<81; I++)// for(var j=40; j<43; J++) / / for this column within the scope of 15 {16 arr_vposition (I) (j + 100 * * 3 + 1) = 2; 17 } 18 } 19 for(var i=20; i<81; I++)// for(var j=60; j<63; J++) / / for the columns within the scope of 22 {23 arr_vposition (I) (j + 100 * * 3 + 1) = 4. 24 } 25 } 26 BABYLON.VertexData._ComputeSides(0, arr_vposition, vdata_ground.indices, vdata_ground.normals 27 , vdata_ground.uvs); 28 var mesh_ground=new BABYLON.Mesh("mesh_ground",scene); 29 mesh_ground.renderingGroupId=2; 30 vdata_ground.applyToMesh(mesh_ground, true);Copy the code

The first line creates a Babylonjs “vertex data” object of type Ground. This object contains the vertex positions, normals, texture coordinates, and vertex index data needed to build the Ground grid. The two 198 in the constructor indicate that the Ground is 198 in length and width. The two 99s indicate that each edge is divided into 99 segments (consisting of 100 vertices with a distance of 2 between each vertex). Why this is set to 99 segments will be explained later, when the ground grid is rendered as a flat surface.

There are two ways to make the ground uneven: for a large proportion of height changes, try traversing each vertex and changing the height of the vertex according to some rule; When the ratio is small, it is recommended to directly find these vertices in the cache array to change, which is obviously faster.

Line 26 evaluates the front and back of each face based on the vertex data. This statement is usually executed after a modification of the vertex information in the grid. Another statement that usually precedes it is:

BABYLON.VertexData.ComputeNormals(positions, indices, normals);
Copy the code

“, its function is to recalculate the normal direction after the vertex data changes. The reason for not executing this statement here is that the ground grid in Babylonjs is a “simplified” grid

As shown in the figure:

The simplified method uses six vertices to represent two squares. The slice metadata between vertices is interpolated by vertex data. Since the square on the left and the square on the right share two vertices, the normal direction and texture coordinates of the two squares must be continuous. Representing the two squares in a non-simplified way requires eight vertices, which has the disadvantage of increasing the performance cost. The advantage is that the normal direction and texture coordinates do not have to be continuous and can vary radically.

Because of the simplified method, the same vertex of the ground grid is in multiple different planes, so it is meaningless to use ComputeNormals to calculate the normal direction of the vertices of the ground grid. In fact, Babylonjs default the normal direction of the vertices of the ground grid is vertical upward.

The following code creates an empty grid, sets the render group of the grid to 2, and hands the vertex data to the empty grid object.

The resulting grid is shown below:

 

B. Establish 2D pathfinding matrix

The PathFinding library used in pathfinding scenarios requires a matrix (a two-dimensional array) to define the location of obstacles, where a zero element means the block is passable and a non-zero element means the block is not passable. Here’s how to build this array:

1 mesh_ground.mydata={}; 2 mesh_ground. Mydata. WalkabilityMatrix = MakewalkabilityMatrix (,99,2 arr_vposition, 99); 3 mesh_ground.mydata.len_x=99; 4 mesh_ground.mydata.len_y=99; 5 mesh_ground.mydata.len_s=2; June 7. 8. 9. 10 11 // Calculate the tilt degree of each square block to determine whether it is passable 12 // vertex data, pathfinding space width, pathfinding space height, Function MakewalkabilityMatrix(arr,len_x,len_y,len_s) 14 {15 var arr_Matrix=numeric. Rep ([len_y,len_x],0); 16 var len_s2 = len_s * 0.707; Var len_s2a=len_s2; var len_s2a=len_s2; 18 //var len_s2b=len_s2; 19 //var len_s2c=len_s2; 20 for(var i=0; i<len_y; For (var j=0; var j=0; j<len_x; {24 var int1=j+ I *(len_x+1); 25 var int2=j+i*(len_x+1)+1; 26 var int3=j+(i+1)*(len_x+1); 27 var int4=j+(i+1)*(len_x+1)+1; 28 var y1=arr[int1*3+1]; 29 var y2=arr[int2*3+1]; 30 var y3=arr[int3*3+1]; 31 var y4=arr[int4*3+1]; 32 var ya=(y1+y2+y3+y4)/4; 33 var yb=Math.max(Math.abs(y1-ya),Math.abs(y2-ya),Math.abs(y3-ya),Math.abs(y4-ya)); 34 arr_Matrix [I] [j] = parseInt (yb)/(0.707); 35} 36} 37 return arr_Matrix; 38}Copy the code

The idea behind this code is: Ground grid vertical orthogonal projection as pathfinding cell, take every pathfinding cell’s four vertices, work out the four vertices of the maximum difference of average height and the height of each vertex and pathfinding cell center to the vertices of the horizontal distance ratio, this ratio as “obstacle” (is simply the high change, the more severe, The harder the obstacles are). Then add a myData attribute to the grid (a JavaScript advantage) and put the pathfinding matrix information into this attribute.

Here with the help of numeric math library, can be in http://www.numericjs.com/ to check the document.

2. Generate 3D model in Babylon format:

After the mesh is generated, it needs to be used in other programs. Here, I choose to save it as a 3D model in the format of Babylon, which is a JSON string model file with its advantages of simple structure and comprehensive functions.

Babylon. Js the format of the website has a complete explanation and example: doc.babylonjs.com/generals/fi…

Here is the code to generate the corresponding JSON:

  1 /**
  2  * Created by Administrator on 2017/7/14.
  3  */
  4 function Export_mesh(arr_mesh,PngName)//用Babylon格式导出模型
  5 {
  6     //场景对象
  7     var obj_scene=
  8     {
  9         'autoClear': true,
 10         'clearColor': [0,0,0],
 11         'ambientColor': [0,0,0],
 12         'gravity': [0,-9.81,0],
 13         'cameras': [{
 14             'name': 'Camera',
 15             'id': 'Camera',
 16             'position': [7.4811,5.3437,-6.5076],
 17             'target': [-0.3174,0.8953,0.3125],
 18             'fov': 0.8576,
 19             'minZ': 0.1,
 20             'maxZ': 100,
 21             'speed': 1,
 22             'inertia': 0.9,
 23             'checkCollisions': false,
 24             'applyGravity': false,
 25             'ellipsoid': [0.2,0.9,0.2]
 26         }],
 27         'activeCamera': 'Camera',
 28         'lights': [{
 29             'name': 'Sun',
 30             'id': 'Sun',
 31             'type': 1,
 32             'position': [0.926,7.3608,14.1829],
 33             'direction': [-0.347,-0.4916,-0.7987],
 34             'intensity': 1,
 35             'diffuse': [1,1,1],
 36             'specular': [1,1,1]
 37         }],
 38         'materials':[{
 39             'name': 'mball',
 40             'id': 'mball',
 41             'ambient': [1,1,1],
 42             'diffuse': [1,1,1],
 43             'specular': [1,1,1],
 44             'specularPower': 50,
 45             'emissive': [0,0,0],
 46             'alpha': 1,
 47             'backFaceCulling': true,
 48             'diffuseTexture': {
 49                 'name': PngName?PngName:'snow2.jpg',
 50                 'level': 1,
 51                 'hasAlpha': 1,
 52                 'coordinatesMode': 0,
 53                 'uOffset': 0,
 54                 'vOffset': 0,
 55                 'uScale': 1,
 56                 'vScale': 1,
 57                 'uAng': 0,
 58                 'vAng': 0,
 59                 'wAng': 0,
 60                 'wrapU': true,
 61                 'wrapV': true,
 62                 'coordinatesIndex': 0
 63             }
 64         }],
 65         'geometries': {},
 66         'meshes': [],
 67         'multiMaterials': [],
 68         'shadowGenerators': [],
 69         'skeletons': [],
 70         'sounds': [],
 71         'mydata':{'walkabilityMatrix':[]}
 72     };
 73     //所有模型组件的父物体
 74     var obj_allbase=
 75     {
 76         'name': 'allbase',
 77         'id': 'allbase',
 78         'materialId': 'mball',
 79         'position': [0,0,0],
 80         'rotation': [0,0,0],
 81         'scaling': [1,1,1],
 82         'isVisible': true,
 83         'isEnabled': true,
 84         'checkCollisions': false,
 85         'billboardMode': 0,
 86         'receiveShadows': true,
 87         'positions': [],
 88         'normals': [],
 89         'uvs': [],
 90         'indices': [],
 91         'subMeshes': [{
 92             'materialIndex': 0,
 93             'verticesStart': 0,
 94             'verticesCount': 0,
 95             'indexStart': 0,
 96             'indexCount': 0
 97         }]
 98     };
 99     obj_scene.meshes.push(obj_allbase);
100     var len=arr_mesh.length;
101     var all_x=0;
102     var all_y=0;
103     var all_z=0;
104     for(var i=0;i<len;i++)
105     {
106         var obj_child={};
107         if(arr_mesh[i].geometry._vertexBuffers!=null)
108         {
109             var child=arr_mesh[i];
110             if(!child.mydata)
111             {
112                 child.mydata={}
113             }
114             var vb=child.geometry._vertexBuffers;
115             all_x+=child.position.x;
116             all_y+=child.position.y;
117             all_z+=child.position.z;
118             obj_child=
119             {
120                 'name': child.name,
121                 'id': child.id,
122                 'parentID': 'allbase',
123                 'materialId': 'mball',
124                 'position': [child.position.x,child.position.y,child.position.z],
125                 'rotation': [child.rotation.x,child.rotation.y,child.rotation.z],
126                 'scaling': [child.scaling.x,child.scaling.y,child.scaling.z],
127                 'isVisible': true,
128                 'isEnabled': true,
129                 'checkCollisions': false,
130                 'billboardMode': 0,
131                 'receiveShadows': true,
132                 'positions': vb.position._buffer._data,
133                 'normals': vb.normal._buffer._data,
134                 'uvs': vb.uv._buffer._data,
135                 'indices': child.geometry._indices,
136                 'subMeshes': [{
137                     'materialIndex': 0,
138                     'verticesStart': 0,
139                     'verticesCount': vb.position._buffer._data.length,
140                     'indexStart': 0,
141                     'indexCount': child.geometry._indices.length
142                 }],
143                 'mydata':child.mydata
144             };
145             obj_scene.meshes.push(obj_child);
146         }
147     }
148     //不能让模型的主体过于偏离模型的中心
149     all_x=all_x/len;
150     all_y=all_y/len;
151     all_z=all_z/len;
152     for(var i=1;i<len+1;i++)
153     {
154         obj_scene.meshes[i].position[0]-=all_x;
155         obj_scene.meshes[i].position[1]-=all_y;
156         obj_scene.meshes[i].position[2]-=all_z;
157     }
158     var str_data=JSON.stringify(obj_scene);
159     DownloadText(MakeDateStr()+"testscene",str_data,".babylon");
160 }
Copy the code

As you can see, a Babylon file can contain multiple grid objects. In addition to grid objects, the model file can also store scene, lighting, camera, animation, skeleton, and other information, which can be optionally used. At the end of the method, I export the JSON text using the DownloadText method, which is a character download method written by referring to network materials. If I do not use DownloadText, To obtain the JSON string, enter console.log(str_data) on the cli in Debug mode of Chrome.

The DownloadText content is as follows:

1 /** 2 * Created by Administrator on 2015/3/2. 3 */ 4 /** 5 * Created by Administrator on 2015/3/2. 3 */ ** 2 * Created by Administrator on 2015/3/2. IE11 and Google browser compatibility 6 * / 7 function DownloadText (filename, content, filetype) 8 {9 if (filetype = = null) {10 11 filetype = ". TXT "; 12 } 13 if(document.createElement("a").download! Var aLink = document.createElement('a'); 16 var datatype="data:text/plain; charset=UTF-8,"; 17 if(filetype==".xml") 18 { 19 datatype="data:text/xml; charset=UTF-8,"; 20} 21 if(fileType ==".babylon") 22 {// The browser does not yet support the MIME type of Babylon!! 23 datatype="data:text/plain; charset=UTF-8,"; 24 } 25 if(filetype==".png"||filetype==".jpeg") 26 { 27 datatype=""; 28 } 29 if(content.length<1000000) 30 { 31 aLink.href = datatype+content; Dataurl "32} 33 else 34 {// For files that are too large, normal dataURL is not supported, 35 alink.href = url.createObjecturl (new Blob([content],{type:"text/plain"})); 36 } 37 aLink.download = filename; 38 aLink.innerHTML=filename; 39 //aLink.setAttribute("onclick",""); 40 aLink.onclick=function() 41 { 42 document.getElementById("div_choose").style.display="none"; 43 //delete_div('div_choose'); 44 delete_div('div_mask'); 45 } 46 //aLink.style.display="none"; 47 //document.body.appendChild(aLink); 48 /*var evt = document.createEvent("HTMLEvents"); // create an event 49 evt.initEvent("click", false, false); // This is a click event 50 evt.eventType = 'message'; 51 aLink.dispatchEvent(evt); Chrome considers clicking on a hyperlink to download a file to be the "default attribute" of the hyperlink tag. Google considers that the default attribute cannot be triggered from a script. 53 //window.open(datatype+content, "_blank"); 54 //document.write(datatype+content); 55 delete_div('div_choose'); 56 delete_div('div_mask'); 57 var evt=evt||window.event; 58 cancelPropagation(evt); 59 var obj=evt.currentTarget? evt.currentTarget:evt.srcElement; 60 61 Open_div (" ", "div_choose", 240, 180, 400, 80, ""," ", 1401); Var div_choose=$("#div_choose")[0]; 63 div_choose.style.border="1px solid"; 64 div_choose. InnerHTML ="<span> The Browser specific file has been generated. Please click the file name below to download the file. </span><br>" 65 div_choose.appendChild(aLink); 66 drag(div_choose); / / let the pop-up box can be drag and drop 67 aLink onmousedown = function (68) {var evt = 69 evt | | window. The event; 70 cancelPropagation(evt); 71 } 72 } 73 else//IE 74 { 75 var Folder=BrowseFolder(); 76 If (Folder=="false") 77 {78 alert(" Save failed!" ); 79 } 80 else 81 { 82 var fso, tf; 83 fso = new ActiveXObject("Scripting.FileSystemObject"); 84 tf = fso.createTextFile (Folder + filename+ fileType,true,true); // Create a file 85 tf.write(content); 86 tf.Close(); 87 alert(" Saved!" ); 88} 89} 90} 91 function BrowseFolder() 92 {// Use ActiveX controls 93 try 94 {95 var Message = "Please select save folder "; 96 var Shell = new ActiveXObject(" shell.application "); 97 var Folder = shell.browseForFolder (0,Message,0x0040,0x11); // The starting directory is: My computer 98 //var Folder = shell.browseForFolder (0,Message,0); // Start directory: desktop // Select desktop error!! 99 100 if(Folder ! = null) 101 { 102 Folder = Folder.items(); // Return FolderItems 103 Folder = Folder.item(); // Return Folderitem object 104 Folder = Folder.path; // Return path 105 if(folder.charat (folder.leng-1)! = "\\") 106 { 107 Folder = Folder + "\\"; 108 } 109 //document.all.savePath.value=Folder; 110 return Folder; 111 } 112 } 113 catch(e) 114 { 115 return "false"; 116 alert(e.message); 118 117}}Copy the code

View Code

Next, we’ll use the model file generated above in another program to load the grid using Babylonjs’s explorer:

1 this.loader = new BABYLON.AssetsManager(this.scene); // Resource manager 2 3 // Resource array 4 this.assets = {}; 5 // Assign a task to the resource Manager 6 var meshTask = this.loader.addmeshTask ("gun", "", "./assets/", "gun.babylon"); 7 meshtask.onsuccess = function(task) {8 _this._initmesh (task); 9}; // The first parameter indicates the name of the task, the second parameter indicates which grid is loaded in the model file, and the third parameter indicates the path. 10 var meshTask2 = this.loader.addMeshTask("mesh_ground", "", "./assets/arena/", "2017810_14_12_59testscene.babylon"); 11 meshTask2.onSuccess = function(task) { 12 _this._initMesh(task); 13}; This.loader. onFinish = function (tasks) 18} 19 20. 21. 22. 23 24 _initMesh : function(task) 25 { 26 this.assets[task.name] = task.loadedMeshes; 27 for (var i=0; i<task.loadedMeshes.length; i++ ){ 28 var mesh = task.loadedMeshes[i]; 29 mesh.isVisible = false; 30 // Load all resources in advance, but do not display, when needed to display it in the desired location, or in the desired location, create an instance of the resource (clone) 31} 32} 33Copy the code

At this point, a small problem occurs: Babylonjs does not support the MyData property we entrap in the mesh. The solution is to modify it near line 21272 of Babylon.30.all.max.js:

1 if (parsedMesh.metadata ! == undefined) { 2 mesh.metadata = parsedMesh.metadata; 3 } 4 if (parsedMesh.mydata ! == undefined) { 5 mesh.mydata = parsedMesh.mydata; 6}Copy the code

Write like metadata and add support for MyData, of course, you can also consider embedding MyData into other properties supported by the mesh.

3. Use the PathFinding library for 2D pathfinding

Pathfindingjs 2 d pathfinding library is an open source, can be downloaded at https://github.com/qiao/PathFinding.js complete code and documentation, In the http://qiao.github.io/PathFinding.js/visual/ online test can be all kinds of way to find the path

The basic uses of Pathfinding are as follows:

1 var Finder = new pf. AStarFinder({// "wayfinders" 2 diagonalMovement: 3 3}); 4, 5. 6 this.grid=new PF.Grid(this.len_x,this.len_y,this.walkabilityMatrix); // Generate search grid 7. 9 function FindWaytogo(pickResult) {11 var faceId=pickResult.faceId; PickedMesh = pickresult. pickedMesh; / / by click on the grid var px = 13 MyGame. Player. Mesh. Position. X; / / the object in the scene var p y = MyGame level 14 player. Mesh. Position. Z; 15 16 var len_x=MyGame.arena.len_x; Var len_y= mygame.arena.len_y; var len_y= mygame.arena.len_y; 18 var len_s=MyGame.arena.len_s; 19 if (px > - len_x * len_s / 2 && px < len_x * len_s / 2 && py > - len_y * && len_s / 2 p y < len_y * len_s / 2 && MyGame. Arena. The grid) / / if you use the pathfinder disorder matrix  20 { 21 var arr_matrix=MyGame.arena.walkabilityMatrix; Var count=parseInt(faceId/2); Count_y =parseInt(count/len_x); count_y= len_x; 26 var count_x=count%len_x; 28 var count_x0=parseInt(px/len_s+len_x/2); 29 var count_y0=parseInt(-py/len_s+len_y/2); 32 var path = finder.findPath(count_x0,count_y0, count_x,count_y, mygame.arena.grid.clone ()); 32 var path = finder.findPath(count_x0,count_y0, count_x,count_y, mygame.arena. Var len=path.length; 34 for(var i=0; i<len; Var obj=path[I]; var obj=path[I]; 37 obj[0]=(obj[0]-len_x/2)*len_s; 38 obj[1]=(-obj[1]+len_y/2)*len_s; 39 } 40 41 path.push([pickResult.pickedPoint.x,pickResult.pickedPoint.z]); 42 MyGame.player.path_goto=path; / / when use in generating highly 43 MyGame player. Positiontogo = [pickResult. PickedPoint. X, pickResult. PickedPoint. Z]; 44 path.shift(); Pickresult.pickedpoint.x +"," pickResult.pickedPoint.z+"]"); 47 46}}Copy the code

This allows us to map the locations in the scene to those in the road grid, and then use Pathfinding to generate 2D paths. Note that the Grid object in PathFinding can only be used once. You need to regenerate the grid or use a clone of the grid to find the path again.

 

4. The cannon.js based physics engine moves along the path

The next thing I need to do is move the object along the specified path. In order to keep the object close to the ground while moving through rough terrain, I use the CannonJS physics engine (see the previous article for a physics engine). After testing, this version of CannonJS supports up to 10,000 vertices in a single physical simulator, so the ground grid can’t be divided into more segments.

We listen for the “right click on the ground” event in this scenario as follows:

1 canvas.addEventListener("click", function(evt) { 2 var width = engine.getRenderWidth(); 3 var height = engine.getRenderHeight(); 4 var pickInfo = scene.pick(width/2, height/2, null, false, _this.camera); If (evt.button==2) {7 cancelEvent(evt); // Block the default response 8 if(pickinfo.hit && pickinfo.pickedmesh. Name =="mesh_ground")// click on the ground 9 {10 MyGame.player.mesh.physicsImpostor.setMass(70); FindWaytogo(pickInfo); // Assign mass to the object so that it can fall. 12 var mesh_togo=BABYLON.Mesh.CreateBox("box", 1, scene); / / target square 13 mesh_togo position = pickInfo. PickedPoint. Clone (); //pickResult.pickedPoint 14 mesh_togo.renderingGroupId=2; 15 MyGame.player.mesh_togo=mesh_togo; 16 } 17 } 18 19 20 21 }, false);Copy the code

Then execute the following motion methods before each render:

1 scene. RegisterBeforeRender (function () {2 if (MyGame. Flag_startr = = 1) / / if you begin to render the 3 {4 if(MyGame.flag_view=="first"||MyGame.flag_view=="third") 5 { 6 physics20170725(MyGame.player); 7 } 8 if(MyGame.flag_view=="free") 9 { 10 pathgoto20170808(MyGame.player); 11} 12} 13});Copy the code

 

1 function pathgoTo20170808 (obj)//obj is player 2 {3 if(true) 4 //if(obj. StandonTheGround ==1) 5 { 6 if(obj.path_goto! ="sleep"&&obj.path_goto! ="lose") 7 { 8 var len_x=MyGame.arena.len_x; 9 var len_y=MyGame.arena.len_y; 10 var len_s=MyGame.arena.len_s; 11 var vl_now=obj.mesh.physicsImpostor.getLinearVelocity(); 12 13 if(obj.path_goto.length>0) 14 { 15 var px=obj.mesh.position.x; // All scene coordinates!! 16 var py=obj.mesh.position.z; 17 var count_x0=px; 18 var count_y0=py; 19 var count_x=obj.path_goto[0][0]; 20 var count_y=obj.path_goto[0][1]; 21 var len=obj.path_goto.length; 22 var count_x2=obj.path_goto[len-1][0]; 23 var count_y2=obj.path_goto[len-1][1]; 24 var y_obj=obj.mesh.position.y; 25 if((math. pow(count_x0-count_x2,2)+ math. pow(count_y0-count_y2,2))<0.25*len_s*len_s) Go straight to the final point 27 console.log(" in the final grid "); If ((math. pow(count_x0-count_x2,2)+ math. pow(count_y0-count_y2,2))<0.01*len_s*len_s) 29 {30 31 obj.mesh.position.x=count_x; 32 obj.mesh.position.z=count_y; 33 obj.path_goto="sleep"; 34 the console. The log (" reach the ultimate goal: [+ obj. Mesh. The position. "x +", "+ obj. Mesh. Position. Y +", "+ obj. Mesh. Position. Z +"] "); 35 obj.mesh.physicsImpostor.setMass(0); Obj.mesh_togo.dispose (); dispose(); 37 obj. Mesh. PhysicsImpostor. SetAngularVelocity (new BABYLON. Vector3 (0, 0)); / / stop 38 obj. Mesh. PhysicsImpostor. SetLinearVelocity (new BABYLON. Vector3 (0, 0)); 41 else{42 var v_temp=new BABYLON.Vector3(count_x2,0,count_y2). Subtract (new BABYLON.Vector3(obj.mesh.position.x,0,obj.mesh.position.z)).normalize().scaleInPlace(obj.vm.forward); 43 v_temp.y=vl_now.y<=0? vl_now.y:0; / / this unit should be grounded smooth movement of the 44 obj. Mesh. PhysicsImpostor. SetLinearVelocity (v_temp); 45 / / obj. Mesh. PhysicsImpostor. SetAngularVelocity (new BABYLON. Vector3 (0, 0)); If (obj. Path_goto. Length >1) 47 {48 obj. Path_goto =[obj. Path_goto [len-1]]; Elseif ((math.pow (count_x0-count_x,2)+ math.pow (count_y0-count_y,2))>4*len_s*len_s) 53 {// In the process of moving, the distance is beyond the next target cell 2 for unknown reasons, so it needs to find the path again. This calculation may be time-consuming and cannot be performed every frame!! 54 obj.path_goto="lose"; 55 //obj.mesh.physicsImpostor.setMass(0); / / sticktite 56 obj. Mesh. PhysicsImpostor. SetLinearVelocity (new BABYLON. Vector3 (0, 0)); 57 obj. Mesh. PhysicsImpostor. SetAngularVelocity (new BABYLON. Vector3 (0, 0)); // stop 58 return false; 60 else if(obj.path_goto.length>1&&(math.pow (count_x0-count_x,2)+ math.pow (count_y0-count_y,2))<0.25*len_s*len_s) 61 {// Close enough to the next pathfinder to switch to the next pathfinder 62 63 obj.path_goto.shift(); 64 count_x=obj.path_goto[0][0]; 65 count_y=obj.path_goto[0][1]; 66 console. The log (" switch to the next pathfinding cell: [" + count_x + ", "+ count_y +"] "); 67 var v_temp=new BABYLON.Vector3(count_x,0,count_y).subtract(new BABYLON.Vector3(obj.mesh.position.x,0,obj.mesh.position.z)).normalize().scaleInPlace(obj.vm.forward); 68 v_temp.y=vl_now.y<=0? vl_now.y:0; 69 obj.mesh.physicsImpostor.setLinearVelocity(v_temp); 70 / / obj. Mesh. PhysicsImpostor. SetAngularVelocity (new BABYLON. Vector3 (0, 0)); // Stop 71} 72 else// Move normal to target pathfinder 73 {74 console.log(" normal pathfinding "); 75 var v_temp=new BABYLON.Vector3(count_x,0,count_y).subtract(new BABYLON.Vector3(obj.mesh.position.x,0,obj.mesh.position.z)).normalize().scaleInPlace(obj.vm.forward); 76 v_temp.y=vl_now.y<=0? vl_now.y:0; 77 obj.mesh.physicsImpostor.setLinearVelocity(v_temp); 78 / / obj. Mesh. PhysicsImpostor. SetAngularVelocity (new BABYLON. Vector3 (0, 0)); / / stop 79} 80 81} {82} 83 else 84/85 / obj. Mesh. PhysicsImpostor. SetMass (0); // do not drop 86} 87} 88 89 90 91}Copy the code

Here, the linear velocity of the controlled object is set according to several possible motion situations (the most common ones) to make the object move smoothly along the path. When the object reaches the target, it will enter the sleep state, and when the object deviates from the path, it will enter the LOSE state. The program will check whether the object loses every second. If lose refinds the path (untested) :

1 _this.currentframet=new Date().getTime(); 2 _this.DeltaTime=_this.currentframet-_this.lastframet; _this.lastframet=_this. currentFramet; 4 _this.nohurry+=_this.DeltaTime; 5 if(MyGame&&_this.nohurry>1000)// A navigation fix is made every second. 8 if(_this.player.path_goto=="lose")// find lost 9 {10 console.log(" find lost, replan path "); 11 var len_x=MyGame.arena.len_x; 12 var len_y=MyGame.arena.len_y; 13 var len_s=MyGame.arena.len_s; 14 15 var / / scene coordinates into grid count_x0 = parseInt (_this. Player. Mesh. Position. X/len_s + len_x / 2); 16 var count_y0=parseInt(-_this.player.mesh.position.z/len_s+len_y/2); 17 var count_x=parseInt(_this.player.positiontogo[0]/len_s+len_x/2); 18 var count_y=parseInt(-_this.player.positiontogo[1]/len_s+len_y/2); 19 var path = finder.findPath(count_x0,count_y0 , count_x,count_y, MyGame.arena.grid.clone()); Var len=path.length; 21 for(var i=0; i<len; Var obj=path[I]; var obj=path[I]; 24 obj[0]=(obj[0]-len_x/2)*len_s; 25 obj[1]=(-obj[1]+len_y/2)*len_s; 26 } 27 path.push(MyGame.player.positiontogo); 28 path.shift(); 29 mygame.player. path_goto=path; / / when use in generating height 30 console. The log (" generated path, starting point: [" + _this. Player. Mesh. Position. X + ", "+ _this. Player. Mesh. Position. Z +"] "+ 31", the finish: ["+_this.player.positiontogo[0]+","+_this.player.positiontogo[1]+"]"); 33 32}}Copy the code

 

Now that we’ve successfully accomplished our goal of pathfinding in a WebGL scenario, we can try to modify the PathFinding so that it can be weighted based on terrain and control the pathfinding behavior of multiple units.