This article discusses how to build checkerboard blocks and multiple selectable piece objects in a WebGL scenario based on Babiel.js, show the piece’s range of movement when clicking on it, and move the piece to the target block when clicking on an empty block within the range of movement. In this process, the different moving power and influence range of different pieces should be considered, as well as the different moving power consumption of different blocks.

First, display effect:

1, visit https://ljzc002.github.io/CardSimulate/HTML/TEST3tiled.html to view the board test page “:

The scene is a 20-by-20 chessboard with random fields of grass, land, and snow, and four “pieces” (guest card grid objects) in the center of the board. Use the mouse and WASD, Shift, and space keys to control the camera mesh objects to wander through the scene, and the four pieces will try to make themselves face the camera.

2. Click a chess piece or the plot where the chess piece is located, and the chess piece will be selected and its mobile range and influence range will be displayed:

Blocks that pieces can reach are covered with blue translucent masks, and blocks that pieces cannot reach but can influence are covered with red translucent masks. It is stipulated here that the moving power of “copper card” is 10 and the influence range is 2, while that of “silver card” is 15 and the influence range is 3. The consumption of moving power of grass, mud and snow is 2, 3 and 4 respectively.

3. Click on the blue block, the path of the pieces to the target block will be marked with a yellow semi-transparent mask. Click on the red block, the path to the blue block nearest to the red block will be marked:

4. Click the yellow plot, then the chess piece will move slowly to the yellow plot of the target. After reaching the target, the scope of influence of the chess piece will be displayed around the chess piece:

In the updated version of the program, the red mask under the pieces has been removed

5. Click the chess piece to zoom the camera close to the chess piece; Click the plot outside the influence range, you can cancel the selection of chess pieces; Click on another piece or a block containing other pieces to change the selected piece.

Two, code implementation:

This board is set in a card on the scene (www.cnblogs.com/ljzc002/p/9…

Create checkerboard in initArena

1 mesh_tiledGround=new BABYLON.Mesh("mesh_tiledGround", scene); Y =-7; // This is the parent grid of all blocks and the grandfather grid of all pieces on the board. // set the height of the board to 3 MakeTileds2(0,20,20); // Generate a square checkerboard grid, 20 by 20.Copy the code

The MakeTileds2 method looks like this:

1 arr_tilednodes=[]; Mesh_tiledCard =null; mesh_tiledCard=null; Function MakeTileds2(type,sizex,sizez) function MakeTileds2(sizex,sizez) If you ignore the possible performance advantages 4 {5 // Create textures for several mask layers: 5 var mat_alpha_blue=new BABYLON.StandardMaterial("mat_alpha_blue", scene); 6 var mat_alpha_blue=new BABYLON. 7 mat_alpha_blue.diffuseTexture = new BABYLON.Texture(".. /ASSETS/IMAGE/LANDTYPE/alpha_blue.png",scene); 8 mat_alpha_blue.diffuseTexture.hasAlpha=true; / / declare diffuse texture picture which has 9 mat_alpha_blue transparency. The useAlphaFromDiffuseTexture = true; // Enable diffuse texture transparency 10 //mat_alpha_blue. HasVertexAlpha =true; 5 //mat_alpha_blue. DiffuseColor = new BABYLON.Color3(0, 0,1); 12 / / mat_alpha_blue. Alpha = 0.2; // Opacity 13 mat_alpha_blue.uselogdepth =true; // In order to display properly between cards, it must also be set to this depth. 14 MyGame.materials.mat_alpha_blue=mat_alpha_blue; 15 var mat_alpha_red=new BABYLON.StandardMaterial("mat_alpha_red", scene); 16 mat_alpha_red.diffuseTexture = new BABYLON.Texture(".. /ASSETS/IMAGE/LANDTYPE/alpha_red.png",scene); 17 mat_alpha_red.diffuseTexture.hasAlpha=true; 18 mat_alpha_red.useAlphaFromDiffuseTexture=true; 5 //mat_alpha_red. DiffuseColor = new BABYLON.Color3(1, 0,0); 20 / / mat_alpha_red. Alpha = 0.2; // Opacity 21 mat_alpha_red.uselogDEPTH =true; 22 MyGame.materials.mat_alpha_red=mat_alpha_red; 23 var mat_alpha_green=new BABYLON.StandardMaterial("mat_alpha_green", scene); 24 mat_alpha_green.diffuseTexture = new BABYLON.Texture(".. /ASSETS/IMAGE/LANDTYPE/alpha_green.png",scene); 25 mat_alpha_green.diffuseTexture.hasAlpha=true; 26 mat_alpha_green.useAlphaFromDiffuseTexture=true; 5 // mat_alpha_green.diffusecolor = new BABYLON.Color3(0, 1,0); 28 / / mat_alpha_green. Alpha = 0.2; / / opacity 29 mat_alpha_green useLogarithmicDepth = true; 30 MyGame.materials.mat_alpha_green=mat_alpha_green; 31 var mat_alpha_yellow=new BABYLON.StandardMaterial("mat_alpha_yellow", scene); 32 mat_alpha_yellow.diffuseTexture = new BABYLON.Texture(".. /ASSETS/IMAGE/LANDTYPE/alpha_yellow.png",scene); 33 mat_alpha_yellow.diffuseTexture.hasAlpha=true; 34 mat_alpha_yellow.useAlphaFromDiffuseTexture=true; 35 //mat_alpha_yellow. DiffuseColor = new BABYLON.Color3(1, 1,0); 36 / / mat_alpha_yellow. Alpha = 0.2; / / the opacity 37 mat_alpha_yellow useLogarithmicDepth = true; 38 MyGame.materials.mat_alpha_yellow=mat_alpha_yellow; 39 var mat_alpha_null=new BABYLON.StandardMaterial("mat_alpha_null", scene); // Or just make the mask invisible? 5 mat_alpha_null.diffusecolor = new BABYLON. Color5 (1, 1,1); 41 mat_alpha_null.alpha=0; // Opacity 42 mat_alpha_null.useLogarithmicDepth=true; 43 MyGame.materials.mat_alpha_null=mat_alpha_null; 44 45 mesh_tiledCard=new BABYLON.Mesh("mesh_tiledCard",scene); Mesh_tiledcard. parent=mesh_tiledGround; Var obj_p={xmin:-30,xmax:30,zmin:-30,zmax:30,precision :{"w" : 2,"h" : 2},subdivisions:{"w" : sizex,"h" : sizez} 50 }; 51 var heightp=(obj_p.zmax-obj_p.zmin)/sizez; Var widthp=(obj_p.xmax-obj_p.xmin)/sizex; var widthp=(obj_p.xmax-obj_p.xmin)/sizex; 53 obj_p.heightp=heightp; 54 obj_p.widthp=widthp; 55 mesh_tiledGround.obj_p=obj_p; 58 for(var I =0; var I =0; i<sizez; I++)// start at 0 or 1? ? 59 {// For each column? 60 var z=obj_p.zmax-(heightp* I +0.5*heightp); 61 var arr_rownodes=[]; 62 for(var j=0; j<sizex; J ++) 64 {64 var x=obj_p.xmin+(widthp*j+0.5*widthp); 65 // To create a plot showing ground textures, do I need to make the plot a class as well? 66 var mesh_tiled=new BABYLON.MeshBuilder.CreateGround("mesh_tiled_"+i+"_"+j 67 ,{width:widthp,height:heightp,subdivisionsX : 2,subdivisionsY : 2,updatable:false},scene); 68 mesh_tiled.index_row=i; 69 mesh_tiled.index_col=j; 70 mesh_tiled.heightp=heightp; 71 mesh_tiled.widthp=widthp; 72 mesh_tiled.position.z=z; 73 mesh_tiled.position.x=x; 74 mesh_tiled.position.y=-1; Parent =mesh_tiledGround; 76 mesh_tiled.renderingGroupId=2; 77 // Randomly assign a terrain to this plot, referring to DataWar?? 78 var landtype=newland.RandomChooseFromObj(arr_landtypes); 79 mesh_tiled.landType =landtype.name; Mesh_tiled.cost =arr_landtypes[landtype.name].cost; 81 if(mygame. materials["mat_"+landtype.name])// If this type of material has been created, Material =MyGame. Materials ["mat_"+landtype.name]; mesh_tiled.material=MyGame. 86 var mat_tiled = new BABYLON.StandardMaterial("mat_"+landtype.name,scene); 88 mat_tiled.diffuseTexture = new BABYLON.Texture(landtype.Url,scene); 89 mat_tiled.useLogarithmicDepth=true; 90 MyGame.materials["mat_"+landtype.name]=mat_tiled; 91 mesh_tiled.material=mat_tiled; 92 } 93 var mesh_mask=new BABYLON.MeshBuilder.CreatePlane("mesh_mask_"+i+"_"+j 94 , {width: widthp - 0.1, height: heightp - 0.1}, scene); Mesh_mask. material= mygame.materials.mat_alpha_null; 96 mesh_mask.parent=mesh_tiled; 97 mesh_tiled.mask=mesh_mask; 98 mesh_mask. Rotation. X = Math. PI * 0.5; 99 mesh_mask. Position. Y = 0.1; 100 mesh_mask.renderingGroupId=2; 101 mesh_mask.isPickable=false; Push (mesh_tiled); 102 arr_rownodes.push(mesh_tiled); 103 } 104 arr_tilednodes.push(arr_rownodes); 105} 106} 107}Copy the code

This code first designs the dependency relationship between the board, the plot, and the chess grid. Then, in lines 5 to 43, several mask materials representing different states of the plot were created, which were initially used

Solid color material with transparency (annotated part), later found that the translucent mask of solid color is easy to confuse with the color of the block, and the boundaries between different blocks are not clear enough, so changed to using a translucent image as the texture of the mask. The code for generating a translucent image using canvas is as follows:

1 <! DOCTYPE HTML > 2 < HTML lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title> Generate a semi-transparent PNG image with canvas </title> 6 </head> 7 <body> 8 <div id="div_allbase"> 9 <canvas style="width: 512px; height: 512px" width="512" height="512" id="can_pic"> 10 11 </canvas> 12 </div> 13 </body> 14 <script> 15 var canvas=document.getElementById("can_pic"); 16 window.onload=loadImage; 17 function loadImage() 18 { 19 var context=canvas.getContext("2d"); 20 context. The fillStyle = "RGB (0255, 0)"; 21 context. FillRect (0,0,512,512); 22 drawRoundRect(context, 16, 16, 480, 480, 32); Context. strokeStyle = "#ff0000"; 24 the context. The fillStyle = "RGB (255255255)"; 25 //context.stroke(); // Draw path 26 context.fill(); 27 // The overlay with transparency is not a replacement color, but a mixture of colors, such as 0, 0, 255, 255, and 0, 255, 0, 64. The result is 0, 64, 255, 255, The essence of this should be a sum weighted by opacity 28 // So to set the color accurately, it is better to set it pixel by pixel through ImageData. 29 and 30 var imagedata_temp = context. GetImageData (0,0,512,512); 31 var data=imagedata_temp.data; 32 var len=data.length; 33 for(var i=0; i<len; I + = 4) / / for each pixel 34 35 if {(data [I] = = 255 && data [I + 1) = = 255 && data [I + 2] = = 255) {/ / if it is pure white 36 37 data [I] = 0; 38 data[i+1]=255; 39 data[i+2]=0; 40 data[i+3]=64; 41 } 42 else 43 { 44 data[i+3]=192; PutImageData (imagedata_temp,0,0); Function drawRoundRect(CXT, x, y, width, height, radius){51 cxt.beginPath(); 53 cxt.arc(x + radius, y + radius, radius, Math.PI, Math.PI * 3 / 2); 54 cxt.lineTo(width - radius + x, y); 55 cxt.arc(width - radius + x, radius + y, radius, Math.PI * 3 / 2, Math.PI * 2); 56 cxt.lineTo(width + x, height + y - radius); 57 cxt.arc(width - radius + x, height - radius + y, radius, 0, Math.PI * 1 / 2); 58 cxt.lineTo(radius + x, height +y); 59 cxt.arc(radius + x, height - radius + y, radius, Math.PI * 1 / 2, Math.PI); 60 cxt.closePath(); 61 } 62 63 </script> 64 </html>Copy the code

View Code

The idea is to first mark an area in the Canvas with a specific color, then iterate over the pixels in the canvas and change the pixels that match the specific color to the rGBA color you want.

Next, a grid is generated for each block according to the set size, and then a mask grid is generated for each block grid. Different materials are set for the mask grid to represent the different states of the block grid, while the mask grid is slightly smaller than the block grid, which can make the boundaries between masks clearer. The row – and column-by-row algorithm used to calculate the location of elements in generating blocks is also common in image processing and table drawing programs and should be considered as a general method.

. It is important to note that Babylon, js internal also encapsulate a set up the “grid” board method, but I didn’t see where the advantages of this approach is, instead, because all the plot indiscriminate in a mesh object in object selection difficulties, at the same time, the built-in checkerboard grid square board cannot only support custom chessboard shape.

The RandomChooseFromObj method returns one of the object’s properties at random, with the following code:

1 newland. RandomChooseFromObj = function (obj) / / random from all the attributes of an object according to the probability to select an attribute 2 {3 var len=Object.getOwnPropertyNames(obj).length; Var count_rate=0; 5 var num=Math.random(); 6 var result=null; 7 for(var key in obj) 8 { 9 var ratep=1/len; 10 var pro=obj[key]; 11 if(pro.rate) 12 { 13 ratep=pro.rate; 14 } 15 count_rate+=ratep; 16 if(count_rate>num) 17 { 18 result=pro; 19 return result; // There is always a 20} 21} 22 return "fault"; 23}Copy the code

View Code

  

2, Refactor the mouse click event into the CameraClick method, remove the code from the CameraMesh class, and place it in the cameraclick.js file:

1 / / handle camera click event 2 function CameraClick (_this, evt) 3 {4 if (MyGame. Init_state = = 1 | | MyGame. Init_state = = 2) / / locking the cursor, click on the canvas, This listener does nothing if(mygame.flag_view! ="first_pick") 7 { 8 canvas.requestPointerLock = canvas.requestPointerLock || canvas.msRequestPointerLock || canvas.mozRequestPointerLock || canvas.webkitRequestPointerLock; 9 the if (canvas. RequestPointerLock) {/ / for mouse accident after leaving the browser to lock the cursor 10. Canvas requestPointerLock (); 11 12 MyGame.flag_view="first_lock"; 13 14 _this.centercursor.isVisible=true; 15 } 16 if(MyGame.init_state==1) 17 { 18 var width = engine.getRenderWidth(); 19 var height = engine.getRenderHeight(); 20 var pickInfo = scene.pick(width/2, height/2, null, false, MyGame.Cameras.camera0); 21 if (pickInfo. Hit & & pickInfo. PickedMesh. Name the substr (0, 5) = = "card_") / / judgment, according to the names of the grid 22 {/ / click on the board card, think not much choose at this moment, and can also click on the card of others, But you can only control your own cards. 23 cancelPropagation(evt); 24 cancelEvent(evt); 25 var mesh=pickInfo.pickedMesh; 26 var card=mesh.card; 27 PickCard2(card); / / click on the board card 28} 29 else if (pickInfo. Hit & & pickInfo. PickedMesh. Name. The substr (0, 6) = = "mesh_t") 30 {/ / if you click on the plot, if is the first time click show paths, With particle effects? If the path has already been calculated, the path is confirmed. Move the path by animation 31 PickTiled(pickInfo); 32} 33} 34} 35 else// The click listener does not appear to be blocked by the camera when the cursor is not locked (first_pick), 36 {37 if(mygame. flag_view=="first_ani")// To control camera animation time 38 {39 cancelPropagation(evt); 40 cancelEvent(evt); 41 return; 42 } 43 //var width = engine.getRenderWidth(); 44 //var height = engine.getRenderHeight(); 45 var pickInfo = scene.pick(scene.pointerX, scene.pointerY, null, false, MyGame.Cameras.camera0); // Click info to get screen center info instead of mouse info!! 46 if(MyGame.init_state==1&&MyGame.flag_view=="first_pick" 47 && pickInfo. Hit && pickInfo. PickedMesh. Name the substr (0, 5) = = "card_ && pickInfo. PickedMesh. Card. Belongto = = MyGame. WhoAmI) / / on a card 48 {// Click on a card in hand 49 cancelPropagation(EVT); 50 cancelEvent(evt); 51 //releaseKeyState(); 52 var mesh=pickInfo.pickedMesh; 53 var card=mesh.card; 54 PickCard(card); 55} 56 57} 58} 59}Copy the code

3. Click on the processing of chess pieces:

1 function PickCard2(card)// Click select, highlight edge, use 2D view to follow in unselected state, or 3D view to follow? , click it again to zoom in. Do you want to adjust the Angle of view following mode? 2 {6 GetCardClose2(card) 2 {7 GetCardClose2(card) 3 {7 GetCardClose2(card); 7 //DisposeRange(); //getPicked(card); //getPicked(card); // Card. isPicked=true; // Card. isPicked=true; // set to the selected card and calculate range 13 for it //card_Closed2=card; //card_Closed2 is the global variable that holds the currently selected piece. 16 15}}Copy the code

When the piece is already selected, clicking the piece again will move the camera to the piece with the following code:

1 function GetCardClose2(card) ?? 2 { 3 MyGame.flag_view="first_ani"; 4 MyGame.anicount=2; 5 var pos_card=card.mesh._absolutePosition.clone(); / / get the camera object world coordinate position 6 var pos_camera = MyGame. Player. Mesh. The position. The clone (); 7 var pos=pos_card.clone().add(pos_camera.clone().subtract(pos_card).normalize().scale(3)); 8 var animation3=new BABYLON.Animation("animation3","position",30,BABYLON.Animation.ANIMATIONTYPE_VECTOR3,BABYLON.Animation.ANIMATIONLOOPMODE _CONSTANT); 9 var keys1=[{frame:0,value:MyGame.player.mesh.position.clone()},{frame:30,value:pos}]; 10 animation3.setKeys(keys1); 11 12 13 var rot_camera=MyGame.player.mesh.rotation.clone(); 14 var tran_temp=new BABYLON.Mesh("tran_temp",scene); // The "transform Node" class object of babylu.js might be more suitable for 15 tran_temp.position=pos; // create a "temporary grid" in front of the piece, Orient the grid towards the piece, and get the attitude of the grid 16 tran_temp.lookAt(pos_card, math.pi,0,0); //,Math.PI,Math.PI); YXZ? 17 var rot=tran_temp.rotation.clone(); // It looks like this rot is reversed. How do I reverse it? 18 rot.x=-rot.x; 19 //MyGame.PI2=Math.PI*2; 20 //rot.x=(rot.x-Math.PI)%MyGame.PI2; 21 //rot.y=(rot.y-Math.PI)%MyGame.PI2; 22 //rot.z=0; Dispose (); // Dispose (); 24 var animation4=new BABYLON.Animation("animation4","rotation",30,BABYLON.Animation.ANIMATIONTYPE_VECTOR3,BABYLON.Animation.ANIMATIONLOOPMODE _CONSTANT); 25 var keys2=[{frame:0,value:rot_camera},{frame:30,value:rot}]; 26 animation4.setKeys(keys2); 27 MyGame.player.mesh.animations.push(animation3); // The mesh and camera must use the same animation. 28 //MyGame.Cameras.camera0.animations.push(animation3); 29 MyGame.Cameras.camera0.animations.push(animation4); 30 //MyGame.player.mesh.animations.push(animation4); 31 scene.beginAnimation(MyGame.player.mesh, 0, 30, false,1,function(){ 32 MyGame.anicount--; 33 if(MyGame.anicount==0) 34 { 35 MyGame.flag_view="first_lock"; 36}} 37); 38 scene.beginAnimation(MyGame.Cameras.camera0, 0, 30, false,1,function(){ 39 MyGame.anicount--; 40 if(MyGame.anicount==0) 41 { 42 MyGame.flag_view="first_lock"; 44}} 43); 45}Copy the code

The seventh line of the code calculates the position “in front of the piece,” which is “move 3 units from the piece position to the camera position.” The next step is to calculate the orientation of the camera when moving in front of the chess piece. The lookAt method in Babybabe.js can Orient the grid to a specified world coordinate position, but the actual effect seems to be different from the document. The schematic diagram of the above transformation is as follows:

Then it is the key frame of the grid position animation of the camera after the camera moves as “camera grid class”, the key frame of the camera posture animation of the camera after the camera moves as the camera posture animation of the camera grid class, and executes the animation. In the render loop, the camera grid object’s camera will apply the grid’s position, and the grid will apply the camera’s attitude. The reason for this is the introduction of the CameraMesh class in the previous article.

4. Calculate and display the movement range and influence range of the chess pieces:

A. Preparations:

1 arr_nodepath={}; // Use it to save the consumption value and movement path of each node within the movement range. Is this variable redundant? - "does not have 2 arr_DisplayedMasks=[]; Arr_noderange ={}; // Save each node that may be affected (red material), Function DisplayRange(card)// Display the range of this card 5 {6 // First check to see if there is a mask 7 already displayed if(arr_DisplayedMasks.length>0) 8 { 9 HideAllMask(); Card_Closed2 10} 11 card_Closed2=card; // Since HideAllMask will empty selected pieces, the checker selection code for switching pieces should be placed here. 12 getPicked(card_Closed2); 13 card.isPicked=true; 14 if(card.workstate! ="wait") 15 { 16 return; 17} 18 var node_start=FindNode(card.mesh. Position); Var STR =node_start.name; 20 arr_nodepath={}; // Empty the moving range data, then put the first block (node) into 21 arr_noderange={}; 22 ARR_nodepath [node_start.name]={cost:0,path:[node_start.name],node:node_start}; 23 //arr_nodepath={str:{cost:0,path:[node_start.name]}}; 24 //node_start.open=true; 25 var list_node=[]; // list_node.push(node_start); 27 var power=card.speed; Var costg=0; 29 //var path=[node_start.name]; //var path=[node_start.name]; // Save the name only in the path, so that you can use concat??Copy the code

FindNode initializes all the data required for range calculation. The FindNode method is used to find the plot where the piece is located according to the position of the piece. The code is as follows:

1 function FindNode(pos) {2 var obj_p=mesh_tiledGround. 4 var num_row=Math.floor((obj_p.zmax-pos.z)/obj_p.heightp); Var num_col= math.floor ((pos.x-obj_p.xmin)/obj_p.widthp); var num_col= math.floor ((pos.x-obj_p.xmin)/obj_p.widthp); 6 var node=arr_tilednodes[num_row][num_col]; 7 return node; 8}Copy the code

B. Calculate the movement range of chess pieces:

During the programming process, I found that the data stored in list_node and ARR_nodepath overlaps, but on the one hand, I need to traverse the nodes through list_node in order, and on the other hand, I need to access arr_nodepath data by name in the subsequent code. So you decided to use both arrays and objects.

1 for(var i=0; i<list_node.length; I++)// this variable length sequential traversal requires the use of arrays, followed by the use of object properties - "so keep two sets of variables ???? 2 {// For each node in the list, call it "central node"; Var len=arr_node_neighbor.length; // 5 for(var j=0; j<len; Var nextNode =arr_node_neighbor[J]; 8 costg=arr_nodepath[list_node[i].name].cost; Cost +=nextnode.cost; cost +=nextnode.cost; cost +=nextnode.cost; cost +=nextnode.cost; cost +=nextnode.cost; // The cost of reaching the neighbor is: the cost of reaching the central node + the cost of the neighbor 12 //path.push(nextNode); 13 var path2=arr_nodepath[list_node[i].name].path.concat(); // Path to the central node 14 path2.push(nextNode.name); If (costg>power)// If (costg>power) 16 {17 if(arr_nodepath[nextnode.name])// If (arr_nodepath[nextnode.name])// If (arr_nodepath[nextnode.name]) 18 {19 continue; // Consider the next neighbor 20} 21 else Will this moving boundary node as a starting point for considering influence extent 22 {23 arr_noderange [nextnode. Name] = {cost: 1, path: [nextnode. Name], path0: path2, node: nextnode}; If (arr_nodepath[nextnode.name]) if(arr_nodepath[nextnode.name]) if(arr_nodepath[nextnode.name]) 31 {32 if(arr_nodepath[nextnode.name].cost>costg)// Found a better way to get to this point 33 {// Replace the previously recorded path to this node and cost 34 arr_nodepath[nextnode.name]={cost:costg,path:path2,node:nextnode}; // The new way to reach this node is not better. 41 {42 if(arr_noderange[nextnode.name])// If (arr_noderange[nextnode.name])// If (arr_noderange[nextnode.name]) {44 delete arr_noderange[nextnode.name]; 45 } 46 arr_nodepath[nextnode.name]={cost:costg,path:path2,node:nextnode}; 47 list_node.push(nextnode); // Add this node to the node list for the first time, increase the length of the node list by one, then use these newly added nodes as the central node to calculate the range 48} 49} 50} 51} Arr_nodepath holds the path and consumption to each node within the movement rangeCopy the code

The FindNeighbor method is used to find four “neighbor nodes” around the central node:

1 function FindNeighbor(node)// FindNeighbor(node) 2 {3 var arr_node_neighbor=[] 4 var total_row=arr_tilednodes.length; Var total_col= arr_tiledNodes [0].length; Var index_row=node.index_row; 7 var index_col=node.index_col; Var I =index_row-1; 10 if(I >=0)// If not beyond the checkerboard range 11 {12 arr_node_neighbor.push(arr_tilednodes[I][index_col]); 15 I = 13} 14 / / right index_col + 1; 16 if(i<total_col) 17 { 18 arr_node_neighbor.push(arr_tilednodes[index_row][i]); I =index_row+1; 22 if(i<total_row) 23 { 24 arr_node_neighbor.push(arr_tilednodes[i][index_col]); } 27 I =index_col-1; 28 if(i>=0) 29 { 30 arr_node_neighbor.push(arr_tilednodes[index_row][i]); 31 } 32 return arr_node_neighbor; 33}Copy the code

C. Calculate the scope of influence of chess pieces:

The calculation method is similar to the previous algorithm for calculating the range of movement, with only a small difference.

Var range=card.range; Var list_noderange=[]; 4 for(var key in arr_noderange) 5 {// List_noderange.push (arr_noderange[key].node) 7} 8 for(var i=0; i<list_noderange.length; Var arr_node_neighbor=FindNeighbor(list_noderange[I]); 11 var len=arr_node_neighbor.length; 12 for(var j=0; j<len; Costg =arr_noderange[list_noderange[I].name].cost; 15 costg+=1; 16 if(costg>range) 17 {18 break; Var nextNode = arr_node_neighbor[j]; var nextnode = arr_node_neighbor[j]; 22 if(arr_nodepath[nextnode.name])// If (arr_nodepath[nextNode.name])// If (arr_nodepath[nextnode.name])// If (arr_nodepath[nextnode.name]) 25 } 26 else 27 { 28 var path2=arr_noderange[list_noderange[i].name].path.concat(); // From the start point to the central node path 29 path2.push(nextNode.name); 30 If (arr_noderange[nextnode.name])// If this node has been reached before 31 {32 if(arr_noderange[nextnode.name].cost>costg) 33 {34 arr_noderange[nextnode.name]={cost:costg,path:path2,node:nextnode,path0:arr_noderange[list_noderange[i].name].path0}; 35 } 36 else 37 { 38 continue; 39 } 40 } 41 else 42 { 43 arr_noderange[nextnode.name]={cost:costg,path:path2,node:nextnode,path0:arr_noderange[list_noderange[i].name].path0}; 44 list_noderange.push(nextnode); When the traversal is complete, arr_NOderange contains the information of each node in the range of influence, where path0 is the path to the nearest (one of) boundary node, path2 is the path to the influence node. 49 DisplayAllMask() 50 51 }Copy the code

After calculation, use the DisplayAllMask method to display the moving range and influence range:

Function DisplayAllMask() 2 {3 for(var key in arr_nodepath) 4 {5 if(arr_nodepath[key].cost>0) 6 {7  arr_nodepath[key].node.mask.material=MyGame.materials.mat_alpha_blue; Arr_DisplayedMasks. Push (arr_nodepath[key].node.mask); 10 } 11 for(var key in arr_noderange) 12 { 13 arr_noderange[key].node.mask.material=MyGame.materials.mat_alpha_red; Arr_DisplayedMasks. Push (arr_noderange[key].node.mask); 16 15}}Copy the code

5. Click the processing of the plot:

Considering that it may be difficult to click the chess pieces, it is set here to click the pieces in the plot can also select the chess pieces; In addition, the mask grid only plays a display role, after the selected pieces, but also by listening to the plot click events to determine the moving target of the pieces.

1 function PickTiled(pickInfo)// Click on block 2 {3 // Click on block properties regardless of whether there is a range mask. Add 4 var mesh= pickinfo.pickedMesh; If (arr_DisplayedMasks. Length >0&&card_Closed2)// if(arr_DisplayedMasks. Length >0&&card_Closed2)// If (arr_DisplayedMasks. Length >0&&card_Closed2)// If (arr_DisplayedMasks. Length >0&&card_Closed2)// If (arr_DisplayedMasks. 8 var mesh_unit=TiledHasCard(mesh); If (mesh_unit) {11 if(mesh_unit.name! =card_Closed2.mesh.name) 12 { 13 PickCard2(mesh_unit.card); // Replace selected pieces 14} 15 else// If you click on your own piece!! Close card 16 {17 GetCardClose2(mesh_unit.card); 18 } 19 return; 20} 21 // If you do not click on the plot of another unit 22 // click on the area of influence also automatically find the way to the past? 23 //if(arr_noderange[mesh.name])// If in the range of influence 24 if(mesh.mask.material. Name =="mat_alpha_red")// If click on the red block 25 {26 27 for(var key in arr_noderange) 28 {29 var node=arr_noderange[key]. Node; 30 if(node.mask.material.name=="mat_alpha_yellow") 31 { 32 node.mask.material=MyGame.materials.mat_alpha_blue; If (card_closed2.workState =="wait") 36 {37 var path=arr_noderange[mesh.name].path0; Var len=path.length; 39 for(var i=0; i<len; i++) 40 { 41 if(arr_nodepath[path[i]]&&! TiledHasCard(arr_nodepath[path[i]].node)) 42 { 43 arr_nodepath[path[i]].node.mask.material=MyGame.materials.mat_alpha_yellow; 44} 45} 46} 47 else if(card_closed2.workState ==" Moved ") Else if(mesh.mask.material. Name =="mat_alpha_blue")// If the clicked plot is within the movement range of the selected unit 53 55 for(var key in arr_noderange) 56 {57 var node=arr_noderange[key]. 58 if(node.mask.material.name=="mat_alpha_yellow") 59 { 60 node.mask.material=MyGame.materials.mat_alpha_blue; 61 } 62 } 63 var path=arr_nodepath[mesh.name].path; Var len=path.length; 65 for(var i=0; i<len; i++) 66 { 67 if(arr_nodepath[path[i]]&&! TiledHasCard(arr_nodepath[path[I]].node))// No yellow 68 {69 arr_nodepath[path[i]].node.mask.material=MyGame.materials.mat_alpha_yellow; 70} 71} 72} 73 else if(mesh.mask.material. Name =="mat_alpha_yellow") Var path=arr_nodepath[mesh.name]. Path; // Take the path to this point, the path to the yellow plot must be passable. 76 CardMove2Tiled(path); 77} 78 else// click on the point 79 {80 HideAllMask(); 85 var mesh_unit=TiledHasCard(mesh); 86 if(mesh_ununit.card) 89 {90 PickCard2(mesh_ununit.card); } 92 return; 93} 94} 95}Copy the code

This code defines the handling of each click case through a series of criteria, and the specific rules refer to the code comments.

The TiledHasCard method is used to find possible pieces in the plot:

Function TiledHasCard(node) {3 var units= mesh_tiledcard._children; Var len=units.length; 5 var xmin=node.position.x-node.widthp/2; Var xmax=node.position.x+node.widthp/2; var xmax=node.position.x+node.widthp/2; 7 var zmin=node.position.z-node.heightp/2; 8 var zmax=node.position.z+node.heightp/2; 9 for(var i=0; i<len; i++) 10 { 11 var unit=units[i]; 12 var pos=unit.position; X >xmin&&pos.z>zmin&&pos.z<zmax) {15 return unit 16} 17} 18 return false; 19}Copy the code

The HideAllMask method hides all masks and unchecks the current checker:

1 function HideAllMask() {1 var mask =arr_DisplayedMasks. Length; 4 for(var i=0; i<len; i++) 5 { 6 arr_DisplayedMasks[i].material=MyGame.materials.mat_alpha_null; 7 } 8 arr_DisplayedMasks=[]; 9 arr_nodepath={}; 10 arr_noderange={}; 11 noPicked(card_Closed2); 12 card_Closed2=null; 13}Copy the code

The CardMove2Tiled method is used to move pieces along the yellow path:

1 function CardMove2Tiled(path) 2 { 3 MyGame.flag_view="first_ani"; 4 var len=path.length; 6 var frame_total=len*15; 7 var animation3=new BABYLON.Animation("animation3","position",30,BABYLON.Animation.ANIMATIONTYPE_VECTOR3,BABYLON.Animation.ANIMATIONLOOPMODE _CONSTANT); 8 var keys1=[]; 9 for(var i=0; i<len; I ++)// For each node in the path 10 {11 var pos=arr_nodepath[path[I]].node.position.clone(); 12 pos.y=0; 13 keys1.push({frame:i*15,value:pos}); Add the corresponding key frames 15 / / var keys1 = 14} [{frame: 0, the value: MyGame. Player. Mesh. The position. The clone ()}, {frame: 30, value: pos}]. 16 animation3.setKeys(keys1); 17 card_Closed2.mesh.animations.push(animation3); 18 MyGame.anicount=1; 19 var len=arr_DisplayedMasks.length; 20 for(var i=0; i<len; 1 {22 arr_DisplayedMasks[I]. Material = mygame.materials. Mat_alpha_null; Arr_DisplayedMasks =[]; // Clearing it does not affect moving and saving the range !!!! 25 scene.beginAnimation(card_Closed2.mesh, 0, frame_total, false,1,function(){ 26 MyGame.anicount--; 27 if(MyGame.anicount==0) 28 { 29 MyGame.flag_view="first_lock"; 30 //HideAllMask(); 31 32 card_Closed2.workstate="moved"; 33 DisplayRange2(card_Closed2, card_closed2.range); // The same unit using different skills may have different areas of influence 34} 35}) 36}Copy the code

The animation is performed in much the same way as before, with the only difference being that the keyframes are generated based on the movement path of the pieces. After the animation is complete, execute the DisplayRange2 method to display the influence range of the piece after the move. The code is as follows:

1 var arr_noderange2={} 2 function DisplayRange2(card,range) 3 {4 var node_start=FindNode(card.mesh.position); 5 arr_noderange2={}; 6 arr_noderange2[node_start.name]={cost:0,path:[node_start.name],node:node_start}; 7 var costg=0; 8 var range=card.range; 9 var list_noderange=[node_start]; 10 for(var i=0; i<list_noderange.length; i++) 11 { 12 var arr_node_neighbor=FindNeighbor(list_noderange[i]); 13 var len=arr_node_neighbor.length; 14 for(var j=0; j<len; j++) 15 { 16 costg=arr_noderange2[list_noderange[i].name].cost; 17 costg+=1; 18 if(costg>range) 19 { 20 break; 21} 22 var nextNode = arr_node_neighbor[j]; 24 var path2=arr_noderange2[list_noderange[i].name].path.concat(); 25 path2.push(nextnode.name); 26 If (arr_noderange2[nextnode.name])// If this node has been reached before 27 {28 if(arr_noderange2[nextnode.name].cost>costg)// Is it necessary to calculate the path? 29 { 30 arr_noderange2[nextnode.name]={cost:costg,path:path2,node:nextnode}; 31 } 32 else 33 { 34 continue; 35 } 36 } 37 else 38 { 39 arr_noderange2[nextnode.name]={cost:costg,path:path2,node:nextnode}; 40 list_noderange.push(nextnode); 41 } 42 } 43 } 44 for(var key in arr_noderange2) 45 { 46 if(arr_noderange2[key].cost>0) 47 { 48 arr_noderange2[key].node.mask.material=MyGame.materials.mat_alpha_red; 49 } 50 51 arr_DisplayedMasks.push(arr_noderange2[key].node.mask); 52}} 53Copy the code

Is a simplified version of the previous range algorithm.

This completes the checkerboard scenario.

Iii. Next Step

Next, I plan to try to write a real-time calculation skill module using the eval function and add simple rules to the scene, and then refer to the Babyl.js document to try to optimize the rendering to increase the frame count. The next step is to introduce the previously written WebSocket component to add multiplayer interaction control to the scene.