Plan to do a spacecraft simulation program, do some technical preparations first.
Can access ljzc002. Making. IO/test/Spacet… To view the test scenario, hold down the QE key to tilt the camera left and right. Can the github.com/ljzc002/ljz… Take a look at the code. Due to time constraints, the code on Github may differ slightly from the code in this article.
Main Contents:
1. Program infrastructure
2. Scene initialization
Terrain initialization
4. Event initialization
5. UI initialization
Unit initialization
7. Main loop initialization
Eight, summary
I. Program infrastructure:
The entry file spacetestwp2.html looks like this:
1 <! DOCTYPE HTML > 2 < HTML lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title> 6 <link href=".. /.. /CSS/newland.css" rel="stylesheet"> 7 <script src=".. /.. /JS/LIB/babylon.max.js"></script><! "> 8 <script SRC ="... /.. /JS/LIB/babylon.gui.min.js"></script><! --> 9 <script SRC =".. /.. /JS/LIB/babylonjs.loaders.min.js"></script><! <script SRC ="... /.. /JS/LIB/babylonjs.materials.min.js"></script><! > 11 <script SRC =".. /.. /JS/LIB/earcut.min.js"></script><! <script SRC =".. /.. /JS/LIB/babylonjs.proceduralTextures.min.js"></script><! <script SRC =".. /.. /JS/LIB/oimo.min.js"></script><! -- Oimo Physics library --> 14 <script SRC =".. /.. /JS/LIB/ammo.js"></script><! Ammo <script SRC =".. /.. /JS/LIB/cannon.js"></script><! -- Cannon Physics library --> 16 <! --script src=".. /.. /JS/LIB/dat.gui.min.js"></script--><! > 17 <script SRC =".. /.. /JS/MYLIB/newland.js"></script><! <script SRC =".. /.. /JS/MYLIB/CREATE_XHR.js"></script><! <canvas id="renderCanvas"></canvas> 24 <div id="fps" style="z-index: 302;" ></div> 25 </div> 26 </body> 27 <script> 28 var VERSION=1.0,AUTHOR="[email protected]"; 26 Var machine/* Device info */,canvas/* HTML5 canvas tag */,engine/*Babylon. Js engine */,scene/*Babylon scene */,gl/* underlying WebGL object */,MyGame/* used to store various variables */; 30 canvas = document.getElementById("renderCanvas"); 31 engine = new BABYLON.Engine(canvas, true); 32 engine.displayLoadingUI(); 33 gl=engine._gl; 34 scene = new BABYLON.Scene(engine); 35 var divFps = document.getElementById("fps"); /* Label used to display frames per second */ 36 37 window.onload=beforewebGL; 38 function beforewebGL() 39 { 40 MyGame=new Game(0,"first_pick","","","",""); 41 initWs(webGLStart,"no"); // Test offline without using WebSocket 42 //webGLStart(); Function webGLStart() 45 {// Is it necessary to strictly control the synchronization of the initialization process? 46 initScene(); // Initialize the base scene, including illumination and camera object 47 initArena(); // Initialize the terrain, including birth point, placable area (6*9) // initialize event 49 initUI(); // Init scene UI 50 initObj(); InitLoop (); // initLoop(); // Initialize render loop 52 initAI(); // Initialize AI computing task 53 mygame. init_state=1; 54 engine.hideLoadingUI(); 55 } 56 </script> 57 <script src=".. /.. /JS/PAGE/SpaceTest/WsHandler.js"></script> 58 <script src=".. /.. /JS/PAGE/SpaceTest/SpaceTest2.js"></script> 59 <script src=".. /.. /JS/MYLIB/Game.js"></script> 60 <script src=".. /.. /JS/PAGE/SpaceTest/Control.js"></script> 61 <script src=".. /.. /JS/PAGE/SpaceTest/FullUI.js"></script> 62 <script src=".. /.. /JS/PAGE/SpaceTest/Campass.js"></script> 63 <script src=".. /.. /JS/PAGE/CHARACTER/Rocket2.js"></script> 64 </html>Copy the code
1. Download the Babylon. Js library
Before version 4.0, Babylon. Js provided a graphical packaging tool that can easily package various libraries into a JS file according to user needs. However, this packaging tool is no longer available after the revision of the official website. The last version generated using this packaging tool can be found here:
Github.com/ljzc002/ljz… This commemorative version is packaged in beta 4.0 and includes most of the features except for the physics engine.
Back in the present, the official recommendation for using the package using CDN or NPM is github.com/BabylonJS/B… . But I personally prefer explicit calls to local files, so I’ve put together a relatively new package here: github.com/ljzc002/ljz… , you can also do it yourself at github.com/BabylonJS/B… Select the latest version of the package to download.
2. Program initialization process:
Lines a, 28-35 define some global variables that may be used in a program.
B, line 40 create an instance of the Game class to manage variables in the scene. The code for the Game class is as follows:
1 Game=function(init_state,flag_view,wsUri,h2Uri,userid,wsToken) 2 { 3 var _this = this; 4 this.scene=scene; 5 this.loader = new BABYLON.AssetsManager(scene);; // Resource manager 6 // Controller array 7 this. arr_allPlayers =null; 8 this.arr_myplayers={}; 9 this.arr_webplayers={}; 10 this.arr_npcs={}; 11 //this.player={}; // Are these equal for world users? 12 //this.player.arr_units=[]; // These are not set here. In initScene set 13 this.world={}; 14 this.world.arr_units=[]; 15 //this.arr_ 16 this.count={}; 17 this.count.count_name_npcs=0; 18 this.Cameras={}; 19 this.ws=null; 20 this.lights={}; 21 this.fsUI=BABYLON.GUI.AdvancedDynamicTexture.CreateFullscreenUI("ui1"); /* Fullscreen UI*/ 22 this.hl=new BABYLON.HighlightLayer(" hL1 ", scene); 23 enclosing the hl. BlurVerticalSize = 1.0; / / the influence degree of thickness is not high, but to divide it into multiple fuzzy effect, value of the gap between multiple size 24 enclosing the hl blurHorizontalSize = 1.0; 25 this.hl.innerGlow = false; 26 this.hl.alphaBlendingMode=3; 27 //this.hl.isStroke=true; 28 //this.hl.blurTextureSizeRatio=2; 29 //this.hl.mainTextureFixedSize=100; 30 //this.hl.renderingGroupId=3; 31 //this.hl._options.mainTextureRatio=1000; 32 33 this.wsUri=wsUri; 34 this.wsConnected=false; 35 this.init_state=init_state; /*0-startWebGL 37 1-WebGLStarted 38 2-PlanetDrawed 39 * */ 40 this.h2uri =h2Uri; 42 this.WhoAmI=userid; //newland.randomString(8); 43 this.userid=userid; 44 this.wsToken=wsToken; 45 //this.arr_webplayers 46 47 this.materials={}; /* Default material */ 48 var mat_frame = new BABYLON.StandardMaterial("mat_frame", scene); 49 mat_frame.wireframe = true; 50 //mat_frame.useLogarithmicDepth = true; 51 mat_frame.freeze(); 52 this.materials.mat_frame=mat_frame; 53 var mat_red=new BABYLON.StandardMaterial("mat_red", scene); 54 mat_red.diffuseColor = new BABYLON.Color3(1, 0, 0); 55 //mat_red.useLogarithmicDepth = true; 56 mat_red.freeze(); 57 var mat_green=new BABYLON.StandardMaterial("mat_green", scene); 58 mat_green.diffuseColor = new BABYLON.Color3(0, 1, 0); 59 //mat_green.useLogarithmicDepth = true; 60 mat_green.freeze(); 61 var mat_blue=new BABYLON.StandardMaterial("mat_blue", scene); 62 mat_blue.diffuseColor = new BABYLON.Color3(0, 0, 1); 63 mat_blue.freeze(); 64 var mat_black=new BABYLON.StandardMaterial("mat_black", scene); 65 mat_black.diffuseColor = new BABYLON.Color3(0, 0, 0); 66 //mat_black.useLogarithmicDepth = true; 67 mat_black.freeze(); 68 var mat_orange=new BABYLON.StandardMaterial("mat_orange", scene); 62 mat_orange. DiffuseColor = new BABYLON. Color5 (1, 0.5, 0); 70 //mat_orange.useLogarithmicDepth = true; 71 mat_orange.freeze(); 72 var mat_yellow=new BABYLON.StandardMaterial("mat_yellow", scene); 73 mat_yellow.diffuseColor = new BABYLON.Color3(1, 1, 0); 74 //mat_yellow.useLogarithmicDepth = true; 75 mat_yellow.freeze(); 76 var mat_gray=new BABYLON.StandardMaterial("mat_gray", scene); 77 mat_gray. DiffuseColor = new BABYLON.Color3(0.5, 0.5, 0.5); 78 //mat_gray.useLogarithmicDepth = true; 79 mat_gray.freeze(); 80 this.materials.mat_red=mat_red; 81 this.materials.mat_green=mat_green; 82 this.materials.mat_blue=mat_blue; 83 this.materials.mat_black=mat_black; 84 this.materials.mat_orange=mat_orange; 85 this.materials.mat_yellow=mat_yellow; 86 this.materials.mat_gray=mat_gray; 87 88 this.models={}; /* Textures ={}; Textures ["grained_uv"]=new BABYLON.Texture(".. /.. /ASSETS/IMAGE/grained_uv.png", scene); This.texts ={}; 92 93 this.flag_startr=0; This. Flag_starta =0; 95 this.list_nohurry=[]; 96 this.nohurry=0; // a timer to make some calculations less frequent 97 this.flag_online=false; 98 this.flag_view=flag_view; //first/third/input/free 99 this.flag_controlEnabled = false; 100 this.arr_keystate=[]; 101 this.obj_keystate={}; 102 this.SpriteManager=new BABYLON.SpriteManager("treesManagr", ".. /.. /ASSETS/IMAGE/CURSOR/palm.png", 2000, 100, scene); / * the default particle generator * / 103 this. SpriteManager. RenderingGroupId = 2; 104 this.obj_ground={}; This. Arr_startpoint =[]; Currentarea =null; currentarea=null; 107}Copy the code
Some variables are predefined for later invocation through the MyGame object, some of which have no effect on the spacecraft simulation, and can be added or subtracted as needed.
Need to consider is 47 to 86 lines set up the default material code, including mat_frame. UseLogarithmicDepth = true; Indicates that the depth calculation of the material is changed to a logarithmic form. This setting can effectively avoid the flicker of the surface against each other and the overflow of the depth calculation of objects that are too far away. However, some features (such as procedural textures and particle systems) in Babibs.js do not support this setting. Non-logarithmic depth materials in the same render group will always appear next to logarithmic depth materials, so it is up to the scene to decide whether to use a logarithmic depth material.
C, lines 46-52 initialize each aspect of the simulation program in turn. (Refer to the Initialization process from Windows Game Programming Master Tips and the Getting Started Guide to WebGL)
Ii. Scene initialization:
The initScene method code is as follows :(in the spacetest2.js file)
1 function initScene() 2 {3 console.log(" initialize universe scene "); 4 var light1 = new BABYLON.HemisphericLight("hemi", new BABYLON.Vector3(0, 50, 100), scene); // Light 5 light1.diffuseColor = new BABYLON.Color3(0, 10, 10); 6 7 var camera0= new BABYLON.UniversalCamera("FreeCamera", new BABYLON.Vector3(0, 0, -10), scene); // The new version of "universal camera" is changed from FreeCamera. It is said that it can support various operating devices by default. 8 camera0. MinZ = 0.001; Camera0. attachControl(canvas,true); 10 //camera0.speed=50; 11 scene.activeCameras.push(camera0); 12 13 MyGame. Player = {/ / to save some can use variables to the player attributes of MyGame object name: 14 MyGame. Userid, / / display name 15 id: MyGame. Userid, / / the WebSocket Sessionid 16 camera:camera0, 17 methodofmove:"free", 18 mesh:new BABYLON.Mesh("mesh_"+MyGame.userid,scene), 19 cardinhand:[], 20 arr_units:[], 21 handpoint:new BABYLON.Mesh("mesh_handpoint_"+MyGame.userid,scene), 22 scal:5, 23}; 24 MyGame. Player. Handpoint. Position = new BABYLON. Vector3 (0, 14, 31); 25 MyGame.player.handpoint.parent=MyGame.player.mesh; 26 MyGame.Cameras.camera0=camera0; 27 // Enable the physical engine 28 //var physicsPlugin =new BABYLON.CannonJSPlugin(false); 29 //var physicsPlugin = new BABYLON.OimoJSPlugin(false); 30 var physicsPlugin = new BABYLON.AmmoJSPlugin(); 31 physicsPlugin.setTimeStep(1/120); 32 var physicsEngine = scene.enablePhysics(new BABYLON.Vector3(0, 0.1, 0.2), physicsPlugin); // Gravity new BABYLON.Vector3(0, 0.1, 0.2) 33}Copy the code
Babyl.js supports three physics engines cannon.js, Oimo. Js, and Ammo. Js by default, as well as binding custom physics engines. Here’s a quick comparison of the three physics engines supported by default:
You are advised to select a physical engine based on actual requirements.
Three, terrain initialization:
The initArena method code is as follows:
1 function initArena() 2 {3 console.log(" initialize terrain "); 5 var skybox = BABYLON.Mesh.CreateBox(" skybox ", 1500.0, scene); 5 var skyboxMaterial = new BABYLON.StandardMaterial("skyBox", scene); 6 skyboxMaterial.backFaceCulling = false; 7 skyboxMaterial.reflectionTexture = new BABYLON.CubeTexture(".. /.. /ASSETS/IMAGE/SKYBOX/nebula", scene); 8 skyboxMaterial.reflectionTexture.coordinatesMode = BABYLON.Texture.SKYBOX_MODE; 9 skyboxMaterial.diffuseColor = new BABYLON.Color3(0, 0, 0); 10 skyboxMaterial.specularColor = new BABYLON.Color3(0, 0, 0); 11 skyboxMaterial.disableLighting = true; 12 skybox.material = skyboxMaterial; 13 skybox.renderingGroupId = 1; 14 skybox.isPickable=false; 15 skybox.infiniteDistance = true; 16 17 18 var / / three reference mesh_base = new BABYLON. MeshBuilder. CreateSphere (" mesh_base ", {10} diameter:, scene). 19 mesh_base.material=MyGame.materials.mat_frame; 20 mesh_base.position.x=0; 21 mesh_base.renderingGroupId=2; 22 //mesh_base.layerMask=2; 23 var mesh_base1=new BABYLON.MeshBuilder.CreateSphere("mesh_base1",{diameter:10},scene); 24 mesh_base1.position.y=100; 25 mesh_base1.position.x=0; 26 mesh_base1.material=MyGame.materials.mat_frame; 27 mesh_base1.renderingGroupId=2; 28 //mesh_base1.layerMask=2; 29 var mesh_base2=new BABYLON.MeshBuilder.CreateSphere("mesh_base2",{diameter:10},scene); 30 mesh_base2.position.y=-100; 31 mesh_base2.position.x=0; 32 mesh_base2.material=MyGame.materials.mat_frame; 33 mesh_base2.renderingGroupId=2; 34}Copy the code
It was an empty space with nothing but skyboxes and references
4. Event initialization
The event initialization code is as follows:
1 function initEvent() 2 {3 console.log(" Initialization control event "); 4 InitMouse(); 5 window.addEventListener("resize", function () { 6 if (engine) { 7 engine.resize(); 8 } 9 },false); 10 window.addEventListener("keydown", onKeyDown, false); // Press 11 window.addEventListener("keyup", onKeyUp, false); // button lift 12}Copy the code
// control.js 1 function InitMouse() 2 {3 canvas.addeventListener ("blur",function(evt){// Listener loses focus 4 releaseKeyState(); 5}) 6 canvas.addEventListener("focus",function(evt){// Listen to focus 7 releaseKeyState(); // Note that considering the number of mobile phones, 12 function onKeyDown(event) 13 {14 var key=event.key 15 mygame.obj_keyState [key]=1; 16 } 17 function onKeyUp(event) 18 { 19 var key=event.key 20 MyGame.obj_keystate[key]=0; 21 } 22 function releaseKeyState() 23 { 24 for(key in MyGame.obj_keystate) 25 { 26 MyGame.obj_keystate[key]=0; 28 27}}Copy the code
Considering the users may use touchscreen devices, there is no add to “lock” cursor (canvas. RequestPointerLock) support, and plan the future of the GUI buttons on the keyboard monitor window instead.
5. UI initialization
1. The UI initialization code is as follows:
1 function initUI() 2 {3 console.log(" initialize global UI"); 4 MakeFullUI(MyGame.Cameras.camera0); 5}Copy the code
1 //FullUI.js 2 function MakeFullUI(camera0) 3 { 4 var node_z=new BABYLON.TransformNode("node_z",scene); 5 node_z.position.z=32; 6 node_z.parent=camera0; 7 var node_y=new BABYLON.TransformNode("node_y",scene); 8 node_y.position.z=32; 9 node_y.position.y=13; 10 node_y.parent=camera0; 11 var node_x=new BABYLON.TransformNode("node_x",scene); 12 node_x.position.z=32; 13 node_x.position.x=28; 14 node_x.parent=camera0; 17 var compassz = campass.makeringz (12,36,0,0.5,node_z); 18 var compassy = campass.makeringy (28,36,0,1,node_y); 19 var compassx = campass.makeringx (12,36,0,1,node_x); 20 21 camera0.node_z=node_z; 22 camera0.node_y=node_y; 23 camera0.node_x=node_x; 24 camera0.compassz=compassz; 25 camera0.compassy=compassy; 26 camera0.compassx=compassx; 27 28 camera0.arr_myship=[]; 29 camera0.arr_friendship=[]; 30 camera0.arr_enemyship=[]; 31 32 33}Copy the code
2. A problem that needs to be solved in UI stage is how to display the posture of the camera in 3D space. After thinking about it, we decided to establish a 3D compass in front of the camera that moves synchronically with the camera:
Var Campass={}; var Campass={}; 3 Campass.MakeRingX=function(radius,sumpoint,posx,sizec,parent){ 4 var lines_x=[]; 5 var arr_point=[]; 6 var radp=Math.PI*2/sumpoint; 7 for (var I = 0.0; i<sumpoint; i++) 8 { 9 var x=posx||0; 10 var rad=radp*i; 11 var y=radius*Math.sin(rad); 12 var z=radius*Math.cos(rad); 13 var pos=new BABYLON.Vector3(x,y,z) 14 arr_point.push(pos); 15 var pos2=pos.clone(); 16 pos2.x-=sizec; 17 lines_x.push([pos,pos2]); 18 var node=new BABYLON.Mesh("node_X"+rad,scene); 19 node.parent=parent; 20 node.position=pos2; 21 var label = new BABYLON.GUI.Rectangle("label_X"+rad); 22 label.background = "black"; 23 label.height = "14px"; 24 label. Alpha = 0.5; 25 label.width = "36px"; 26 //label.cornerRadius = 20; 27 label.thickness = 0; 28 //label.linkOffsetX = 30; // Position offset?? 29 MyGame.fsUI.addControl(label); 30 label.linkWithMesh(node); 31 var text1 = new BABYLON.GUI.TextBlock(); 32 text1.text = Math.round((rad/Math.PI)*180)+""; 33 text1.color = "white"; 34 label.addControl(text1); 35 label.isVisible=true; 36 label.text=text1; 37 38 } 39 arr_point.push(arr_point[0].clone()); // end to end, 40 lines_x.push(arr_point); 41 var compassx = new BABYLON.MeshBuilder.CreateLineSystem("compassx",{lines:lines_x,updatable:false},scene); 42 compassx.renderingGroupId=2; 43 compassx.color=new BABYLON.Color3(0, 1, 0); 44 compassx.useLogarithmicDepth = true; //compassx.position=node_x.position.clone(); 46 compassx.parent=parent; 47 compassx.mainpath=arr_point; 48 compassx.sumpoint=sumpoint; 49 compassx.radius=radius; 50 return compassx; 51 } 52 53 Campass.MakeRingY=function(radius,sumpoint,posy,sizec,parent){ 54 var lines_y=[]; 55 var arr_point=[]; 56 var radp=Math.PI*2/sumpoint; 57 the for (var I = 0.0; i<sumpoint; i++) 58 { 59 var y=posy||0; 60 var rad=radp*i; 61 var z=radius*Math.sin(rad); 62 var x=radius*Math.cos(rad); 63 var pos=new BABYLON.Vector3(x,y,z) 64 arr_point.push(pos); 65 var pos2=pos.clone(); 66 pos2.y-=sizec; 67 lines_y.push([pos,pos2]); 68 var node=new BABYLON.Mesh("node_Y"+rad,scene); 69 node.parent=parent; 70 node.position=pos2; 71 var label = new BABYLON.GUI.Rectangle("label_Y"+rad); 72 label.background = "black"; 73 label.height = "14px"; 74 label. Alpha = 0.5; 75 label.width = "36px"; 76 //label.cornerRadius = 20; 77 label.thickness = 0; 78 //label.linkOffsetX = 30; // Position offset?? 79 MyGame.fsUI.addControl(label); 80 label.linkWithMesh(node); 81 var text1 = new babibing.gui.textBlock (); 82 var num=Math.round((rad/Math.PI)*180); 83 if(num>=90) 84 { 85 num-=90; 86 } 87 else 88 { 89 num+=270; 90 } 91 text1.text = num+""; 92 text1.color = "white"; 93 label.addControl(text1); 94 label.isVisible=true; 95 label.text=text1; 96 } 97 arr_point.push(arr_point[0].clone()); // end to end, 98 lines_y.push(arr_point); 99 var compassy = new BABYLON.MeshBuilder.CreateLineSystem("compassy",{lines:lines_y,updatable:false},scene); 100 compassy.renderingGroupId=2; 101 compassy.color=new BABYLON.Color3(0, 1, 0); 102 compassy.useLogarithmicDepth = true; 103 //compassy.position=node_y.position.clone(); 104 compassy.parent=parent; 105 compassy.mainpath=arr_point; 106 compassy.sumpoint=sumpoint; 107 compassy.radius=radius; 108 return compassy; 109 } 110 111 Campass.MakeRingZ=function(radius,sumpoint,posz,sizec,parent){ 112 var lines_z=[]; 113 var arr_point=[]; 114 var radp=Math.PI*2/sumpoint; 115 parent.arr_node=[]; For 116 (var I = 0.0; i<sumpoint; i++) 117 { 118 var z=posz||0; 119 var rad=radp*i; 120 var x=radius*Math.sin(rad); 121 var y=radius*Math.cos(rad); 122 var pos=new BABYLON.Vector3(x,y,z); 123 arr_point.push(pos); 124 var pos2=pos.clone(); 125 pos2.normalizeFromLength(radius/(radius-sizec)); Lines_z. push([pos,pos2]); lines_z.push([pos,pos2]); 127 var node=new BABYLON.Mesh("node_Z"+rad,scene); 128 node.parent=parent; 129 node.position=pos2; 130 parent.arr_node.push(node); 131 var label = new BABYLON.GUI.Rectangle("label_Z"+rad); 132 label.background = "black"; 133 label.height = "14px"; 134 label. Alpha = 0.5; 135 label.width = "36px"; 136 //label.cornerRadius = 20; 137 label.thickness = 0; 138 label.rotation=rad; 139 label.startrot=rad; 140 //label.linkOffsetX = 30; // Position offset?? 141 MyGame.fsUI.addControl(label); 142 label.linkWithMesh(node); 143 var text1 = new BABYLON.GUI.TextBlock(); 144 text1.text = Math.round((rad/Math.PI)*180)+""; Text1. color = "white"; 146 label.addControl(text1); 147 label.isVisible=true; 148 label.text=text1; 149 node.label=label; 150 } 151 arr_point.push(arr_point[0].clone()); // end to end, 152 lines_z.push(arr_point); 153 var compassz = new BABYLON.MeshBuilder.CreateLineSystem("compassz",{lines:lines_z,updatable:false},scene); 154 compassz.renderingGroupId=2; 155 compassz.color=new BABYLON.Color3(0, 1, 0); 156 compassz.useLogarithmicDepth = true; 157 compassz.parent=parent; 158 compassz.mainpath=arr_point; 159 compassz.sumpoint=sumpoint; 160 compassz.radius=radius; 161 return compassz; 162}Copy the code
The main body of the compass is made up of three rings, on which there are scales and numbers indicating angles, and its structure is shown as follows:
FIG. 1
Figure 2
The white quadrilateral pyramid represents the camera’s visual cone. The semicircles of CompassX and Compassy closest to the camera are just outside the visual cone, so they are not visible. How the compass moves when the camera attitude changes is covered in the initialization loop. Another possibility is to set the compassx and Compassy circles to 720 degrees so you can see all angles in the field of view, or to use a GUI like an HTML revolving light instead of a stereo compass. Time didn’t test these ideas.
Two Pointers should be added at the top of the screen and in the middle of the right. This will more accurately indicate the current Angle, which is planned for the next version.
3, Here’s a bit more about the babybaby.js viewcone. The official documentation of Babybaby.js does not mention the properties and Settings of the viewcone (it seems to be wrapped in the projection matrix method of the camera), so I wrote my own code to test the properties of the viewcone:
1 <!DOCTYPE html>
2 <html lang="en">
3 <head>
4 <meta charset="UTF-8">
5 <title>改为直接用顶点构造视锥体</title>
6 <link href="../../CSS/newland.css" rel="stylesheet">
7 <script src="../../JS/LIB/babylon.min.js"></script><!--这里包含了babylon格式的模型导入,但不包含gltf等其他格式,包含了后期处理-->
8 <script src="../../JS/LIB/babylon.gui.min.js"></script>
9 <script src="../../JS/LIB/babylonjs.loaders.min.js"></script>
10 <script src="../../JS/LIB/babylonjs.materials.min.js"></script>
11 <script src="../../JS/LIB/earcut.min.js"></script>
12 <script src="../../JS/LIB/babylonjs.proceduralTextures.min.js"></script>
13 <script src="../../JS/LIB/oimo.min.js"></script>
14 <script src="../../JS/LIB/ammo.js"></script>
15 <script src="../../JS/LIB/cannon.js"></script>
16 <script src="../../JS/LIB/dat.gui.min.js"></script>
17 <script src="../../JS/MYLIB/newland.js"></script>
18 <script src="../../JS/MYLIB/CREATE_XHR.js"></script>
19 </head>
20 <body>
21 <div id="div_allbase">
22 <canvas id="renderCanvas"></canvas>
23 <div id="fps" style="z-index: 302;"></div>
24 </div>
25 </body>
26 <script>
27 var VERSION=1.0,AUTHOR="[email protected]";
28 var machine,canvas,engine,scene,gl,MyGame;
29 canvas = document.getElementById("renderCanvas");
30 engine = new BABYLON.Engine(canvas, true);
31 engine.displayLoadingUI();
32 gl=engine._gl;
33 scene = new BABYLON.Scene(engine);
34 var divFps = document.getElementById("fps");
35
36 window.onload=beforewebGL;
37 function beforewebGL()
38 {
39 webGLStart();
40 }
41 function webGLStart()
42 {
43 createScene();
44 //scene.debugLayer.show();
45 MyBeforeRender();
46 }
47 var createScene = function () {
48 camera0= new BABYLON.UniversalCamera("FreeCamera", new BABYLON.Vector3(0, 0, 0), scene);//FreeCamera
49 camera0.minZ=0.001;
50 camera0.attachControl(canvas,true);
51 scene.activeCameras.push(camera0);
52
53 var light1 = new BABYLON.HemisphericLight("hemi", new BABYLON.Vector3(0, 1, 0), scene);
54 light1.diffuse = new BABYLON.Color3(1,1,1);//这道“颜色”是从上向下的,底部收到100%,侧方收到50%,顶部没有
55 light1.specular = new BABYLON.Color3(0,0,0);
56 light1.groundColor = new BABYLON.Color3(1,1,1);//这个与第一道正相反
57
58 var skybox = BABYLON.Mesh.CreateBox("skyBox", 1500.0, scene);
59 var skyboxMaterial = new BABYLON.StandardMaterial("skyBox", scene);
60 skyboxMaterial.backFaceCulling = false;
61 skyboxMaterial.reflectionTexture = new BABYLON.CubeTexture("../../ASSETS/IMAGE/SKYBOX/nebula", scene);
62 skyboxMaterial.reflectionTexture.coordinatesMode = BABYLON.Texture.SKYBOX_MODE;
63 skyboxMaterial.diffuseColor = new BABYLON.Color3(0, 0, 0);
64 skyboxMaterial.specularColor = new BABYLON.Color3(0, 0, 0);
65 skyboxMaterial.disableLighting = true;
66 skybox.material = skyboxMaterial;
67 skybox.renderingGroupId = 1;
68 skybox.isPickable=false;
69 skybox.infiniteDistance = true;
70
71 var mat_frame = new BABYLON.StandardMaterial("mat_frame", scene);
72 mat_frame.wireframe = true;
73 //测试视锥体
74 var vertexData= new BABYLON.VertexData();
75 var w=50;//锥体底部矩形宽度的一半
76 var h=60;//锥体底部到视点的距离
77 var r=0.5;//锥体底部矩形的高宽比
78 var positions=[0,0,0,-w,w*r,h,-w,-w*r,h,w,-w*r,h,w,w*r,h];
79 var uvs=[0.5,0.5,0,0,0,1,1,1,1,0];
80 var normals=[];
81 var indices=[0,1,2,0,2,3,0,3,4,0,4,1];
82 BABYLON.VertexData.ComputeNormals(positions, indices, normals);//计算法线
83 BABYLON.VertexData._ComputeSides(0, positions, indices, normals, uvs);
84 vertexData.indices = indices.concat();//索引
85 vertexData.positions = positions.concat();
86 vertexData.normals = normals.concat();//position改变法线也要改变!!!!
87 vertexData.uvs = uvs.concat();
88
89 var mesh=new BABYLON.Mesh(name,scene);
90 vertexData.applyToMesh(mesh, true);
91 mesh.vertexData=vertexData;
92 mesh.renderingGroupId=2;
93 mesh.material=mat_frame;
94
95 var node_z=new BABYLON.TransformNode("node_z",scene);
96 node_z.position.z=32;
97 //node_z.parent=camera0;
98 var node_y=new BABYLON.TransformNode("node_y",scene);
99 node_y.position.z=32;
100 node_y.position.y=13;
101 //node_y.parent=camera0;
102 var node_x=new BABYLON.TransformNode("node_x",scene);
103 node_x.position.z=32;
104 node_x.position.x=28;
105 //node_x.parent=camera0;
106 //绘制罗盘
107 var compassz = Campass.MakeRingZ(12,36,0,0.5,node_z);
108 var compassy = Campass.MakeRingY(28,36,0,1,node_y);
109 var compassx = Campass.MakeRingX(12,36,0,1,node_x);
110
111 }
112 function MyBeforeRender()
113 {
114 scene.registerBeforeRender(
115 function(){
116 //camera0.position.x=0;
117 //camera0.position.y=0;
118 }
119 )
120 scene.registerAfterRender(
121 function() {
122 }
123 )
124 engine.runRenderLoop(function () {
125 engine.hideLoadingUI();
126 if (divFps) {
127 // Fps
128 divFps.innerHTML = engine.getFps().toFixed() + " fps";
129 }
130 //lastframe=new Date().getTime();
131 scene.render();
132 });
133 }
134 var Campass={};
135 Campass.MakeRingX=function(radius,sumpoint,posx,sizec,parent){
136 var lines_x=[];
137 var arr_point=[];
138 var radp=Math.PI*2/sumpoint;
139 for(var i=0.0;i<sumpoint;i++)
140 {
141 var x=posx||0;
142 var rad=radp*i;
143 var y=radius*Math.sin(rad);
144 var z=radius*Math.cos(rad);
145 var pos=new BABYLON.Vector3(x,y,z)
146 arr_point.push(pos);
147 var pos2=pos.clone();
148 pos2.x-=sizec;
149 lines_x.push([pos,pos2]);
150 var node=new BABYLON.Mesh("node_X"+rad,scene);
151 node.parent=parent;
152 node.position=pos2;
153 }
154 arr_point.push(arr_point[0].clone());//首尾相连,不能这样相连,否则变形时会多出一个顶点!!,看来这个多出的顶点无法去掉,只能在选取时额外处理它
155 lines_x.push(arr_point);
156 var compassx = new BABYLON.MeshBuilder.CreateLineSystem("compassx",{lines:lines_x,updatable:false},scene);
157 compassx.renderingGroupId=2;
158 compassx.color=new BABYLON.Color3(0, 1, 0);
159 compassx.useLogarithmicDepth = true;
160 //compassx.position=node_x.position.clone();
161 compassx.parent=parent;
162 compassx.mainpath=arr_point;
163 compassx.sumpoint=sumpoint;
164 compassx.radius=radius;
165 return compassx;
166 }
167
168 Campass.MakeRingY=function(radius,sumpoint,posy,sizec,parent){
169 var lines_y=[];
170 var arr_point=[];
171 var radp=Math.PI*2/sumpoint;
172 for(var i=0.0;i<sumpoint;i++)
173 {
174 var y=posy||0;
175 var rad=radp*i;
176 var z=radius*Math.sin(rad);
177 var x=radius*Math.cos(rad);
178 var pos=new BABYLON.Vector3(x,y,z)
179 arr_point.push(pos);
180 var pos2=pos.clone();
181 pos2.y-=sizec;
182 lines_y.push([pos,pos2]);
183 var node=new BABYLON.Mesh("node_Y"+rad,scene);
184 node.parent=parent;
185 node.position=pos2;
186 }
187 arr_point.push(arr_point[0].clone());//首尾相连,不能这样相连,否则变形时会多出一个顶点!!,看来这个多出的顶点无法去掉,只能在选取时额外处理它
188 lines_y.push(arr_point);
189 var compassy = new BABYLON.MeshBuilder.CreateLineSystem("compassy",{lines:lines_y,updatable:false},scene);
190 compassy.renderingGroupId=2;
191 compassy.color=new BABYLON.Color3(0, 1, 0);
192 compassy.useLogarithmicDepth = true;
193 //compassy.position=node_y.position.clone();
194 compassy.parent=parent;
195 compassy.mainpath=arr_point;
196 compassy.sumpoint=sumpoint;
197 compassy.radius=radius;
198 return compassy;
199 }
200
201 Campass.MakeRingZ=function(radius,sumpoint,posz,sizec,parent){
202 var lines_z=[];
203 var arr_point=[];
204 var radp=Math.PI*2/sumpoint;
205 parent.arr_node=[];
206 for(var i=0.0;i<sumpoint;i++)
207 {
208 var z=posz||0;
209 var rad=radp*i;
210 var x=radius*Math.sin(rad);
211 var y=radius*Math.cos(rad);
212 var pos=new BABYLON.Vector3(x,y,z);
213 arr_point.push(pos);
214 var pos2=pos.clone();
215 pos2.normalizeFromLength(radius/(radius-sizec));//里面的数字表示坐标值除以几
216 lines_z.push([pos,pos2]);
217 var node=new BABYLON.Mesh("node_Z"+rad,scene);
218 node.parent=parent;
219 node.position=pos2;
220 parent.arr_node.push(node);
221 }
222 arr_point.push(arr_point[0].clone());//首尾相连,不能这样相连,否则变形时会多出一个顶点!!,看来这个多出的顶点无法去掉,只能在选取时额外处理它
223 lines_z.push(arr_point);
224 var compassz = new BABYLON.MeshBuilder.CreateLineSystem("compassz",{lines:lines_z,updatable:false},scene);
225 compassz.renderingGroupId=2;
226 compassz.color=new BABYLON.Color3(0, 1, 0);
227 compassz.useLogarithmicDepth = true;
228 compassz.parent=parent;
229 compassz.mainpath=arr_point;
230 compassz.sumpoint=sumpoint;
231 compassz.radius=radius;
232 return compassz;
233 }
234 </script>
235 </html>
Copy the code
Starting at line 73, adjust the h parameter so that when the white boundary in Figure 1 disappears exactly, the cone mesh in the scene is the same shape as the visual cone. By default, babybaby.js has an aspect ratio of 0.5, a cone width to a cone height ratio of about 100 to 59, and a water * view Angle of about 80.56 degrees ((math.atan (50/59)*2/ math.pi)*180). Since these attributes are not needed for the time being, there is no research on how to modify them. Can be in ljzc002. Making. IO/test/Spacet… Check out this test page.
In the world of 3D programming, length has no actual physical meaning, and an object 50 times the size of 100 from the viewpoint will look the same as an object 100 times the size of 200 from the viewpoint, but that doesn’t mean we can set the size of the object arbitrarily, When setting the size, we need to consider whether the object is between the ** plane and the far * plane of the visual cone, the mutual occlusion relationship between objects, whether too large or too small value will lead to calculation overflow, and the support of various libraries for size. For example, if the skybox size of Babylu.js is set too large (such as 15000), the sky texture display will be abnormal, or if a physics engine only supports the size range of 0.1 to 10 by default, the size limits of such libraries are often poorly documented and require testing.
Unit initialization:
The initObj method code is as follows:
1 function initObj() 2 {// Suppose one unit of length corresponds to 100m 3 console.log(" initialization unit "); 4 var ship=new BABYLON.MeshBuilder.CreateBox("ship_target",{size:5},scene); Position =new BABYLON.Vector3(-5,0,0); // Create a cube as a ship. 6 ship.material=MyGame.materials.mat_green; 7 ship.renderingGroupId=2; 8 //ship.v={x:0,y:0,z:0} 9 ship.physicsImpostor = new BABYLON.PhysicsImpostor(ship, BABYLON.PhysicsImpostor.BoxImpostor//SphereImpostor// 10 , { mass: 1, restitution: 0.0005, friction: 0, damping: 0, linearDamping: "a"}, scene); / / physical simulators 11 ship. PhysicsImpostor. Damping = 0; 12 MyGame.player.ship=ship; Camera0 = mygame.camera0; camera0= camera0; 15 Campass.AddShip(camera0,"my",ship); 16 / * scene. OnReadyObservable. Add (function () {/ / this should earlier out!! 17 ship.physicsImpostor.physicsBody.linearDamping=0; 18 ship.physicsImpostor.physicsBody.angularDamping=0; 19 })*/ 20 newland.DisposeDamping(ship); 22 var advancedTexture = mygame.fsui; 23 var UiPanel = new BABYLON.GUI.StackPanel(); 24 UiPanel.width = "220px"; 25 UiPanel.fontSize = "14px"; 26 UiPanel.horizontalAlignment = BABYLON.GUI.Control.HORIZONTAL_ALIGNMENT_LEFT; 27 UiPanel.verticalAlignment = BABYLON.GUI.Control.VERTICAL_ALIGNMENT_BOTTOM; 28 UiPanel.color = "white"; 29 advancedTexture.addControl(UiPanel); 30 MyGame.player.ship.label_pos=UiPanel; 31 var text1 = new babb.gui.textBlock (); 32 text1.text = "" 33 text1.color = "white"; 34 text1.paddingTop = "0px"; 35 text1.width = "220px"; 36 text1.height = "20px"; 37 UiPanel.addControl(text1); 38 UiPanel.text1=text1; 39 var text2 = new BABYLON.GUI.TextBlock(); 40 text2.text = "" 41 text2.color = "white"; 42 text2.paddingTop = "0px"; 43 text2.width = "220px"; 44 text2.height = "20px"; 45 UiPanel.addControl(text2); 46 UiPanel.text2=text2; 47 var text3 = new BABYLON.GUI.TextBlock(); 48 text3.text = "" 49 text3.color = "white"; 50 text3.paddingTop = "0px"; 51 text3.width = "220px"; 52 text3.height = "20px"; 53 UiPanel.addControl(text3); 54 UiPanel.text3=text3; 55 and 56 var mesh_rocket = new BABYLON. MeshBuilder. CreateCylinder (" mesh_rocket "/ / for the spacecraft to add a conical rocket boosters 57 , {height: 2, diameterTop: 0.1, diameterBottom: 1}, scene); 58 mesh_rocket.renderingGroupId = 2; 59 mesh_rocket.material=MyGame.materials.mat_gray; 60 mesh_rocket. Rotation = new BABYLON. Vector3 (Math) PI, 0, 0); 61 mesh_rocket. Position = new BABYLON. Vector3 (0, 1, 0). 62 var rocket=new Rocket(); 63 ship.rocket=rocket; 64 var obj_p={ship:ship,mesh:mesh_rocket,name:"testrocket1" 65 ,mass:1000,cost2power:function(cost){return cost*1; } 66,pos:new BABYLON.Vector3(0,0,-3.5),rot:new BABYLON.Vector3(-math. 67 rocket.init(obj_p); / / object initialization rocket 68 rocket. The fire ({firescaling firebasewidth: 0.5, cost: 1:1}); / / rocket 69 70 var shipb = new BABYLON. MeshBuilder. CreateBox (" ship_targetb, "{size: 5}, scene). 71 shipb.position=new BABYLON.Vector3(5,0,0); 72 shipb.material=MyGame.materials.mat_green; 73 shipb.renderingGroupId=2; 74 //ship.v={x:0,y:0,z:0} 75 shipb.physicsImpostor = new BABYLON.PhysicsImpostor(shipb, BABYLON. PhysicsImpostor. BoxImpostor / / SphereImpostor / / 76, {mass: 1, restitution: 0.0005, friction: 0}, scene); // Shipb. mass=1000000000; 78 MyGame.player.shipb=shipb; 80 var camera0= mygame.camera0. Camera0; 81 Campass.AddShip(camera0,"my",shipb); 82 newland.DisposeDamping(shipb); 83 84 var mesh_rocketb=new BABYLON.MeshBuilder.CreateCylinder("mesh_rocketb" 85 , {height: 2, diameterTop: 0.1, diameterBottom: 1}, scene); 86 mesh_rocketb.renderingGroupId = 2; 87 mesh_rocketb.material=MyGame.materials.mat_gray; 88 mesh_rocketb. Rotation = new BABYLON. Vector3 (Math. PI, 0, 0); 89 mesh_rocketb. Position = new BABYLON. Vector3 (0, 1, 0). 90 var rocketb=new Rocket(); 91 shipb.rocket=rocketb; 92 var obj_pb={ship:shipb,mesh:mesh_rocketb,name:"testrocket1b" 93 ,mass:1000,cost2power:function(cost){return cost*1; } 62,pos:new BABYLON.Vector3(0,0,-3.5),rot:new BABYLON.Vector3(-math.pi /2,0,0)}; 95 rocketb.init(obj_pb); 96 rocketb. The fire ({firebasewidth: 0.5, cost: 1, firescaling: 1}); 97}Copy the code
1. First, a cubic grid was built to represent the spacecraft, and then the physics simulator was set up in the ninth spacecraft. Attention should be paid to the damping parameter, which represents the unconditional obstruction of acceleration by the physical engine. The default value is 0.1. Different from the friction parameter, which represents the friction coefficient, the emulator will always be subjected to this reduction even if it does not contact with any other object. This means that a force with an acceleration of less than 0.1 has no effect on the object. As planned, this property should be set by the PhysicsImpostor constructor. Unfortunately, as the physics engine is updated, this parameter does not work in the constructor. The user must go to the underlying physics engine to modify this parameter (actually two parameters: Linear velocity attenuation and angular velocity attenuation) :
1 // Remove the default acceleration attenuation of the mesh's physical overlay 2 newland.DisposeDamping=function(mesh) 3 {4 // Cannon uses 5 mesh.physicsImpostor.physicsBody.linearDamping=0; 6 mesh.physicsImpostor.physicsBody.angularDamping=0; 7 / / ammo use 8 if (mesh) physicsImpostor. PhysicsBody. SetDamping) 9 {10. Mesh physicsImpostor. PhysicsBody. SetDamping (0, 0); 12 11}}Copy the code
These are cannon and Ammo’s attenuation removal methods. Oimo seems to lack this limitation.
PhysicsImpostor is the object created by physicsImpostor, which allows you to manipulate various physics engines in a similar way. A physicsBody is a pointer to the underlying data of a specific physics engine, and the physicsBody structure is different for each type of physics engine. A detailed explanation of the relationship can be found on the official forum forum.babylonjs.com/t/a-questio… .
2. Next you need to indicate the direction of the spacecraft in the compass, in campass.js
1 campass. AddShip=function(Camera0,type,ship) 2 {3 // render group 3 vec_ship=ship.position.clone().subtract(camera0.position); Vec_ship =newland.VecTo2Local(vec_ship,camera0); vec_ship=newland.VecTo2Local(vec_ship,camera0); / / into a local coordinate system coordinates, 6 var pointerz = new BABYLON. MeshBuilder. CreateSphere (" pointerz_ "+ ship. Name, {diameter: 1}, scene). Parent =camera0.compassz.parent; 8 pointerz.position=new BABYLON.Vector3(vec_ship.x,vec_ship.y,0).normalize().scale(camera0.compassz.radius); 9 pointerz.renderingGroupId=3; 10 ship.pointerz=pointerz; 11 If (type=="my")// CamerA0.arr_myship.push (ship); 14 pointerz.material=MyGame.materials.mat_green; 7} 7 else if(type=="friend")// camera0.arr_friend.push (ship); 19 pointerz.material=MyGame.materials.mat_blue; 20} 21 else if(type=="enemy")// camerA0.arr_enemyship.push (ship); 24 pointerz.material=MyGame.materials.mat_red; 25 } 26 var label = new BABYLON.GUI.Rectangle("label_pointerz_"+ship.name); // Text box 27 label.background = "black"; 28 label.height = "14px"; 29 label. Alpha = 0.5; 30 label.width = "120px"; 31 label.thickness = 0; 32 //label.linkOffsetX = 30; // Position offset?? 33 MyGame.fsUI.addControl(label); 34 label.linkWithMesh(pointerz); 35 var text1 = new BABYLON.GUI.TextBlock(); 36 text1.text = ship.name; 37 text1.color = "white"; 38 label.addControl(text1); 39 label.isVisible=true; 40 label.text=text1; 41 pointerz.label=label; 42} Campass.Com putePointerPos 43 = function (ship) / / refresh the orientation of the spacecraft 44 {45 var camera0 = MyGame. Cameras. Camera0; 46 var pointerz=ship.pointerz; 47 var vec_ship=ship.position.clone().subtract(camera0.position); 48 /*var v=new BABYLON.Vector3(vec_ship.x,vec_ship.y,0) 49 var m = camera0.getWorldMatrix(); 50 var v = BABYLON.Vector3.TransformCoordinates(vector, m); */ 51 vec_ship=newland.VecTo2Local(vec_ship,camera0); 52 pointerz.position=(new BABYLON.Vector3(vec_ship.x,vec_ship.y,0)).normalize().scale(camera0.compassz.radius); 53, 54}Copy the code
Lines 21 to 54 create three text boxes in the lower left corner of the screen to show the position of the ship.
4, 56 to 68 behavior ship added a Rocket booster, Rocket class in rocket2.js:
1 // Working medium engine (Particle System Version, Low particle volume, low intensity, low flash) 2 Rocket = function () {4, 5} 3 6 Rocket. The prototype. The init = function (param) 7 {8 param = param | | {}; 9 this.name=param.name; 10 this.ship=param.ship; 11 this.node=new BABYLON.TransformNode("node_rocket_"+this.name,scene); 12 this.node.position=param.pos; 13 this.node.rotation=param.rot; 14 this.node.parent=this.ship; 15 this.mesh=param.mesh; Instance 16 this.mesh. Parent =this.node; 17 this.mass=param.mass; 18 this.ship.mass+=this.mass 19 this.cost2power=param.cost2power; This.cost2demage =param.cost2demage; // This. HP =param.hp; 22 this.cost=null; // This. Power =null; // Current thrust 24 this.failurerate=param.failurerate; 25, 26 and 27 / / this / / failure rate parameters. The scaling = param. Scaling | | 1; 28 29 this.rotxl=param.rotxl; // This. Rotyl =param.rotyl; 31 this.rotzl=param.rotzl; 32 33 34 } 35 Rocket.prototype.fire=function(param) 36 { 37 this.cost=param.cost; 38 this.power=this.cost2power(this.cost); 39 this.firebasewidth=param.firebasewidth||1; / / flame width at the bottom of this. 40 firescaling = param. Firescaling | | 1; Var particleSystem; 43 particleSystem = new BABYLON.GPUParticleSystem("particles", { capacity:50000 }, scene); / / particle system, particles available for 50000 44 particleSystem. ActiveParticleCount = 50000; // Active particle number 50000 45 particleSystem. EmitRate = 10000; / / launch 10000 46 per second particleSystem. ParticleTexture = new BABYLON. Texture (".. /.. /ASSETS/IMAGE/TEXTURES/fire/flare.png", scene); / / particle texture 47 particleSystem maxLifeTime = 10; MinSize = 0.01//*this. Firescaling; 49 particleSystem. MaxSize = 0.1 / / * this firescaling; 50 particleSystem.emitter = this.node; 51 52 var radius = this.firebasewidth; 53 var angle = Math.PI; 54 var coneEmitter = new BABYLON.ConeParticleEmitter(radius, angle); // coneEmitter. RadiusRange = 1; 56 coneEmitter.heightRange = 0; 57 particleSystem.particleEmitterType = coneEmitter; 58 59 particleSystem.renderingGroupId=2; 60 particleSystem.start(); // Var force=new BABYLON.Vector3(0,-this.power*100000/this.ship.mass,0); 62 var force = new BABYLON. Vector3 (0, 1, 0). 63 force=newland.vecToGlobal(force,this.node); 64 force=force.subtract(this.node.getAbsolutePosition()).scale(this.power); 65 //this.ship.physicsImpostor.applyForce(force,this.node.position)// 66 / / this. Ship. PhysicsImpostor. ApplyImpulse (force, new BABYLON, Vector3 (0, 0, 3.5)) / / this is equivalent to accelerate a seconds 67 / / this. Ship. PhysicsImpostor. ApplyForce (force, new BABYLON, Vector3 (0, 0, 3.5)) / / Oimo doesn 't support applying force. Using impule instead. 68 69 var rocket=this; 70 / / this. Ship. PhysicsImpostor. ApplyImpulse (new BABYLON. Vector3 (0, 1), new BABYLON. Vector3 (0, 0, 2.5)); 71 function(){73 var force=new 71 function(){73 var force=new BABYLON. Vector3 (0, 1, 0). 74 force=newland.vecToGlobal(force,rocket.node); 75 force=force.subtract(rocket.node.getAbsolutePosition()).scale(rocket.power); 76 rocket.ship.physicsImpostor.applyForce(force,rocket.node.getAbsolutePosition()); 77}, 0) * / 78 scene. RegisterAfterRender (function () {/ / per frame render executed after 79 var force = new BABYLON. Vector3 (0, 1, 0); 80 force=newland.vecToGlobal(force,rocket.node); 81 force=force.subtract(rocket.node.getAbsolutePosition()).scale(rocket.power); 82 var pos=rocket.node.getAbsolutePosition(); 83 rocket.ship.physicsImpostor.applyForce(force,pos); 84 console.log(rocket.ship.physicsImpostor.getLinearVelocity()); 85 }) 86 87 //this.ship.physicsImpostor.applyForce(force,this.node.getAbsolutePosition()); // execute only once 88}Copy the code
Rocket boosters consist of a conical nozzle and particles ejected from it. Note line 50: particleSystem. And 57 rows particleSystem. ParticleEmitterType = coneEmitter; The former represents the entire particle system moving with the rocket, while the latter represents the shape of the region from which the particles are emitted. Using transform node instead of empty grid can refer to the cause of www.cnblogs.com/ljzc002/p/1… , the use of the particle system can view: ljzc002. Making. IO/BABYLON101 /… . No. 43 uses GPU particle system to improve rendering efficiency. In practical use, it can be considered to further reduce the number of available particles and particle emittance.
Another idea is to use a flame material or flame texture instead of particles to represent a Rocket’s tail flame, which works quite well in some cases (rocke.js) :
The dancing flames made a wonderful contrast to the stillness of space
However, this mask texture was not used because it caused a number of problems in shifting viewing angles and could not simulate the tail effect of a ship turning.
Apply thrust to the rocket from line 62:
A. Because the cone built by babylu.js by default faces downward, it turns into the posture pointing to the rear of the spaceship after being rotated and inherits the posture of the parent object, so lines 62 to 64 first establish a vertical downward force, and then apply the rocket world matrix to this force. And because the position change contained in the rocket world matrix will influence vector, wrong change the size and direction of the force, so the force vector minus the absolute position of the rocket, so we get the direction of the rocket jet force in the global coordinate system, and then multiplied by the size of the jet force can get the rocket jet force vector.
B. Then apply force to the ship’s physics simulator. Babylon. Js provides users with two ways to apply force (doc.babylonjs.com/how_to/forc…). — applyImpulse and applyForce, both of which are force vectors and points in the global coordinate system. Each execution of applyImpulse accelerates the simulator by 1 second, while each execution of applyForce accelerates the current frame of the object by 1 second. So use the applyForce method. Because the Oimo engine does not support applyForce (applyImpulse is automatically replaced internally), THE Ammo engine is used. In addition, the force vector parameter of applyImpulse and applyForce is “force”, and different masses of the spaceship will produce different accelerations.
For the sake of convenience, the mass of the rocket itself is not added to the mass of the spacecraft, nor is the amount of working mass consumed by rocket propulsion.
5. Then build a similar shipb for comparison
Main loop initialization:
1 var posz_temp1=0; Var posz_temp2=0; Var posz_temp1b=0; Var posz_temp2b=0; 5 function initLoop() 6 {7 console.log(" initialize the main loop "); 8 var _this=MyGame; 9 MyGame. AddNohurry (" task_logpos ", 1000, 0, the function () {/ / per second output some information and update the position of the spacecraft showed 10. Var posz = MyGame player. The ship. The position, z; 11 var poszb=MyGame.player.shipb.position.z; 12 //console.log("---"+(new Date().getTime())+"\n"+posz+"_"+(posz-posz_temp1)+"_"+(posz-posz_temp1-posz_temp2)+"@"+MyGame.player.ship.physicsImposto r.getLinearVelocity() 13 // +"\n"+poszb+"_"+(poszb-posz_temp1b)+"_"+(poszb-posz_temp1b-posz_temp2b)+"@"+MyGame.player.shipb.physicsImpostor.getLinea rVelocity()); 14 //console.log(MyGame.player.ship.physicsImpostor.getLinearVelocity()); 15 posz_temp2=posz-posz_temp1; 16 posz_temp1=posz; 17 posz_temp2b=poszb-posz_temp1b; 18 posz_temp1b=poszb; 19 var ship_main=MyGame.player.ship; 20 var UiPanel=ship_main.label_pos; 21 UiPanel.text1.text="x:"+ship_main.position.x; 22 UiPanel.text2.text="y:"+ship_main.position.y; 23 UiPanel.text3.text="z:"+ship_main.position.z; 24 },0)//name,delay,lastt,todo,count 25 26 scene.registerBeforeRender( 27 function(){ 28 29 var camera0=MyGame.Cameras.camera0; 30 var node_z=camera0.node_z; 31 var node_y=camera0.node_y; 32 var node_x=camera0.node_x; 33 34 node_z.rotation.z=-camera0.rotation.z; Var len=node_z.arr_node.length; 36 for(var i=0; i<len; i++) 37 { 38 var label=node_z.arr_node[i].label; 39 label.rotation=label.startrot+camera0.rotation.z; 40 } 41 node_y.rotation.y=-camera0.rotation.y; 42 node_x.rotation.x=-camera0.rotation.x; 43 // Do ship logo updates occur every frame or once per second? 44 var len1=camera0.arr_myship.length; 45 for(var i=0; i<len1; i++) 46 { 47 var ship=camera0.arr_myship[i]; 48 Campass.ComputePointerPos(ship); 49 } 50 var len1=camera0.arr_friendship.length; 51 for(var i=0; i<len1; i++) 52 { 53 var ship=camera0.arr_friendship[i]; 54 Campass.ComputePointerPos(ship); 55 } 56 var len1=camera0.arr_enemyship.length; 57 for(var i=0; i<len1; i++) 58 { 59 var ship=camera0.arr_enemyship[i]; 60 Campass.ComputePointerPos(ship); 61 } 62 63 64 } 65 ) 66 scene.registerAfterRender( 67 function() { 68 MyGame.HandleNoHurry(); // Put it here for compatibility with the physics engine. 69 var camera0=MyGame.Cameras.camera0; 70 if(mygame.obj_keystate.q ==1) 71 {72 camerA0.rotation. Z +=0.01; If (mygame.obj_keystate.e ==1) {76 CamerA0.rotation. Z -=0.01; Engine. RunRenderLoop (function () {81 engine.hideloadingui (); 82 if (divFps) { 83 // Fps 84 divFps.innerHTML = engine.getFps().toFixed() + " fps"; 85 } 86 //MyGame.HandleNoHurry(); 87 //lastframe=new Date().getTime(); 88 scene.render(); 89}); 90 91}Copy the code
1. Three cycles
There are three types of things that need to be done periodically in an interactive 3D scene:
One is the essential rendering loop, and bobabylon. Js is ready for us in this regard. In Babylu.js, each render is divided into BeforeRender (26-65), Render (80-89), and AfterRender (66-79) stages. You can add code to the in-line function of each stage that needs to be executed at the corresponding stage of each frame. The main thread executes the render loop at a certain frequency (typically 60HZ). Since the engine will only execute the next frame after all code execution in one frame is completed, if the code execution time in one frame + graphics card rendering time exceeds 1/60s, the execution of the next frame will be delayed, resulting in a lower frame rate of the scene. It is also worth noting that registerBeforeRender and registerAfterRender do not have to be written together with engine. RunRenderLoop. You can register multiple registers in your favorite places if you want, as in Rocket2.js.
The second is low-cost tasks that are done once in a while, such as displaying the current position of the ship or output the current linear velocity of the ship. We don’t need to do these tasks every frame, but once every second is a good choice. To do this I wrote the Nohurry method:
1 / / Game. Js 2 Game. The prototype = {3 AddNohurry: function (name, delay, lastt, todo, count) / / add a periodic task 4 {5 var _this = this; 6 7 var len=_this.list_nohurry.length; 8 if(len==0) 9 { 10 _this.list_nohurry.push({delay:delay,lastt:lastt,todo:todo,name:name 11 ,count:count}) 12 } 13 else { 14 for(var i=0; i<len; i++) 15 { 16 var obj_nohurry=_this.list_nohurry[i]; 17 if(obj_nohurry.name==name)// If there is already a task with the same name 18 {19 return; 20} 21 if(delay> obj_nohurts. delay)// If (delay> obj_nohurts. delay) 24 } 25 else { 26 _this.list_nohurry.splice(i,0,{delay:delay,lastt:lastt,todo:todo,name:name 27 ,count:count}); 28 break; 29 } 30 } 31 } 32 33 }, 34 RemoveNohurry:function(name) 35 { 36 //delete this.list_nohurry[name]; }, 38 HandleNoHurry:function() {40 var this=this; 41 If (_this.flag_startr==0)// Start rendering and terrain initialization complete!! 42 { 43 engine.hideLoadingUI(); 44 _this.flag_startr=1; 45 _this.lastframet=new Date().getTime(); 46 _this.firstframet=_this.lastframet; 47 _this.DeltaTime=0; 48 } 49 else 50 { 51 _this.currentframet=new Date().getTime(); 52 _this.DeltaTime=_this.currentframet-_this.lastframet; 53 _this. lastFramet =_this. currentFramet; 54 /*_this.nohurry+=_this.DeltaTime; 55 56 if(MyGame&&_this.nohurry>1000)// A navigation fix is made every second 57 {58 _this.nohurry=0; 59 60 }*/ 61 //var time_start=_this.currentframet-_this.firstframet; 62 for(var I =0; i<_this.list_nohurry.length; i++) 63 { 64 var obj_nohurry=_this.list_nohurry[i]; 65 if(obj_nohurry.lastt==0) 66 { 67 obj_nohurry.lastt=new Date().getTime(); 68 } 69 else 70 { 71 var time_start=_this.currentframet-obj_nohurry.lastt; 72 if(time_start>obj_nohurry.delay)// If the elapsed time exceeds each execution period multiplied by the number of executions plus one, the command is executed once. 73 {74 obj_nohurry.todo(); 75 obj_nohurry.count++; 76 obj_nohurry.lastt=_this.currentframet; 78 // Change the policy and put the time-consuming operation on the work thread, then execute all tasks on the main thread, including calling the work thread 78 //break; If (_this.flag_starta==1) {// If (_this.flag_starta==1) {// If (_this.flag_starta==1) {// If (_this.flag_starta==1) { Otherwise, only the content related to the basic UI will be processed.Copy the code
In MyGame, we maintain a task array list_NoHurry and the current time, and maintain a last execution time for each task in the array. In each frame of the rendering cycle, we check if the current time – last execution time >= task execution period, then execute the corresponding task.
The html5 Workers thread can be used to handle tasks that are sometimes time-consuming and may slow down the main thread (such as complex AI calculations) :
2 {3 mygame. worker=new worker (" aithread.js "); 2 {3 mygame. worker=new worker (" aithread.js "); 4 MyGame.worker.postMessage("start"); 5 6 MyGame.worker.onmessage=function(event) 7 { 8 console.log(event.data); 9} 10 11}Copy the code
Aithread.js :(in the same directory as the HTML)
1 var flag_thinking=false; 2 var time_now=0; 3 var time_last=0; 4 5 onmessage=function(event) 6 { 7 var data=event.data; 8 if(data=="start"&&! flag_thinking) 9 { 10 flag_thinking=true; 11 //console.log(" Start thinking "); 12 Think() 13 } 14 else if(data=="stop"){ 15 flag_thinking=false; 16 close(); 17} 18} 19 function Think() 20 {21 if(flag_thinking) 22 {// 23 time_now=new Date().gettime (); 24 if(time_last! =0) 25 {26 if(time_last>1000) 27 {28 time_last=time_now; 29 //console.log(time_now); 30 postMessage(time_now); 31 } 32 } 33 else{ 34 time_last=time_now; 35 } 36 37 requestAnimationFrame(Think); RequestAnimationFrame is available instead of window! 38} 39 40}Copy the code
Longer tasks can be executed separately from the main thread in this thread to take full advantage of the computational power of multithreaded computers and reduce the main thread rendering latency.
Note that threads are conceptually supposed to be able to access each other’s memory, but For thread-safety purposes Chrome restricts memory calls between the main thread and the Workers thread, which operates
The self-built objects in the main thread namespace are undefiend and users can only send messages to each other through the postMessage method or share data with the Navigator object. Of course, data can also be shared through the network or session or localStorage, but the speed may be slower.
There are no long-running tasks in this demo, so the Workers thread does nothing.
2, in the scene. RegisterBeforeRender set each frame according to the camera’s stance in inversion of compass, enables the compass to indicate the current camera right direction (33-42), at the same time to refresh the spacecraft on the compass.
In the scene. The registerAfterRender set of q and e keys corresponding, if this frame status to press the keys on the tilt the camera, the tilt has two ideas: First, as in the demo, each frame changes a fixed Angle, the disadvantage is that the number of frames will lead to changes in the manipulation effect, and second, the Angle change speed multiplied by the frame time, the disadvantage is not conducive to breakpoint debugging procedures.
Conclusion: The above has completed some technical verification of spacecraft simulation, the next step will be to use the grid to assemble “more like” spacecraft, and write some internal processing logic of spacecraft (such as energy distribution, module durability), write a set of dedicated rocket control UI, and add WebSocket networking function. The plan is to eventually write a spaceship simulation similar to World of Warships.