在WebGL場景中管理多個卡牌對象的實驗

来源:https://www.cnblogs.com/ljzc002/archive/2018/09/19/9660676.html
-Advertisement-
Play Games

這篇文章討論如何在基於Babylon.js的WebGL場景中,實現多個簡單卡牌類對象的顯示、選擇、分組、排序,同時建立一套實用的3D場景代碼框架。由於作者美工能力有限,所以示例場景視覺效果可能欠佳,本文的重點在於對相關技術的探討。 因為文章比較長,讀者可以考慮將網頁導出為mhtml格式,使用Word ...


  這篇文章討論如何在基於Babylon.js的WebGL場景中,實現多個簡單卡牌類對象的顯示、選擇、分組、排序,同時建立一套實用的3D場景代碼框架。由於作者美工能力有限,所以示例場景視覺效果可能欠佳,本文的重點在於對相關技術的探討。

  因為文章比較長,讀者可以考慮將網頁導出為mhtml格式,使用Word瀏覽。Chrome瀏覽器導出mhtml文件的方法見末尾。

一、顯示效果:

1、訪問https://ljzc002.github.io/CardSimulate/HTML/TEST2.html查看“卡牌模擬頁面”:

  場景中間是三個作為參照物的小球,視口平面的中間是一個用Babylon.js GUI製作的準星,預設滑鼠與準星鎖定在一起,直接移動滑鼠即可改變相機視角,使用WASD Shift 空格鍵可以控制相機前、左、後、右、下、上運動(可能將Ctrl鍵設為向下更符合傳統,但是沒有找到禁用瀏覽器Ctrl+s快捷鍵的方法,只好用Shift代替)。因為游標被鎖定,將這種瀏覽狀態命名為“first_lock”。

2、按下Alt鍵,75張卡片通過動畫移入相機視野,同時相機的位置被固定(但仍可以通過拖動滑鼠改變視角):

  點擊右側的“向上兩行”和“向下兩行”按鈕可以上下滾動卡片,再次按下Alt鍵將隱藏卡片,同時恢復相機的移動和游標的鎖定。因為這種瀏覽狀態主要用來點選場景中的物體,將它命名為“first_pick”。

3、滑鼠左鍵單擊一張卡片,卡片將處於“選中狀態”(綠色邊緣),再次左鍵單擊處於選中狀態的卡片,卡片將被放大拉近顯示,再左鍵單擊將恢複原位:

  執行動畫時會禁用用戶的控制,完全由動畫控制視角,所以將這種瀏覽狀態命名為“first_ani”。

4、模仿Windows的文件多選編寫了卡片多選功能,按下Ctrl時可以點選多個卡片,按下Shift時可以選取首尾之間的所有卡片:

5、選中若幹張卡片後,按1-5鍵可以將被選中的卡片編為1-5隊,被編隊的卡片將按編隊順序顯示在最高處,同時編隊的前面會顯示隊號標記:

6、在first_pick狀態可以使用上下左右方向鍵進行場景漫游,可以看到場景中的所有對象:

 二、代碼實現:

1、文件結構:

CardSimulate工程的文件結構如下圖所示:

其中LIB目錄下是從網上下載的代碼庫

  babylon.32.all.maxs.js是Babylon.js引擎庫

  earcut.dev.js是一個Babylon.js擴展,其功能是在網格上挖洞

  stat.js是用來顯示幀數的代碼

MYLIB是自己編寫的代碼庫

  Events.js是一些用來處理事件的方法

  FileText.js是與文件處理相關的代碼

  newland.js是自己編寫的一些Babylon.js輔助類

  View.js是html視圖的一些相關方法

PAGE是直接操縱這個頁面(WebGL場景)的代碼庫

  Character.js是場景中出現的各種對象的類(比如卡牌網格、相機網格)

  Control20180312.js是用來處理滑鼠鍵盤輸入的代碼

  DrawCard.js是用來繪製卡牌的代碼

  FullUI.js是用來繪製全局(全屏)UI的代碼

  Game.js是游戲類,存儲用來調度整個場景的信息

  HandleCard.js是用來處理已經繪製出的卡牌的代碼,後期考慮和DrawCard.js整合在一起

  HandleCard2.js是一個分枝修改版

  Moves.js是運動計算代碼

  tab_carddata.js里是卡牌種類信息

  tab_somedata.js里是其他輔助信息

2、代碼入口與場景初始化:

  A、代碼由TEST2.html開始執行,其中一部分和前面幾篇文章用到的相似:

 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     <link href="../CSS/stat.css" rel="stylesheet">
 8     <script src="../JS/LIB/babylon.32.all.maxs.js"></script>
 9     <script src="../JS/LIB/stat.js"></script>
10     <script src="../JS/MYLIB/Events.js"></script>
11     <script src="../JS/MYLIB/FileText.js"></script>
12     <script src="../JS/MYLIB/newland.js"></script>
13     <script src="../JS/MYLIB/View.js"></script>
14     <script src="../JS/PAGE/Game.js"></script>
15     <script src="../JS/PAGE/Character.js"></script>
16     <script src="../JS/PAGE/Control20180312.js"></script>
17     <script src="../JS/PAGE/Moves.js"></script>
18     <script src="../JS/PAGE/DrawCard.js"></script>
19     <script src="../JS/PAGE/tab_carddata.js"></script>
20     <script src="../JS/PAGE/tab_somedata.js"></script>
21     <script src="../JS/PAGE/HandleCard2.js"></script>
22     <script src="../JS/PAGE/FullUI.js"></script>
23 </head>
24 <body>
25 <div id="div_allbase">
26     <canvas id="renderCanvas"></canvas>
27     <div id="fps" style="z-index: 301;"></div>
28 </div>
29 </body>
30 <script>
31     var VERSION=1.0,AUTHOR="[email protected]";
32     var machine,canvas,engine,scene,gl,MyGame={};
33     canvas = document.getElementById("renderCanvas");
34     engine = new BABYLON.Engine(canvas, true);
35     engine.displayLoadingUI();
36     gl=engine._gl;//決定在這裡結合使用原生OpenGL和Babylon.js;
37     scene = new BABYLON.Scene(engine);
38     var divFps = document.getElementById("fps");
39 
40     var MyGame={};
41     window.onload=beforewebGL;
42     function beforewebGL()
43     {
44         if(engine._webGLVersion==2.0)//輸出ES版本
45         {
46             console.log("ES3.0");
47         }
48         else{
49             console.log("ES2.0");
50         }
51         MyGame=new Game(0,"first_pick","","http://127.0.0.1:8082/");//建立MyGame對象用來進行全局調度
52         /*0-startWebGL
53          * */
54         webGLStart();
55     }
。。。
56 </script> 57 </html>

  但與前面的簡單場景將主要代碼都寫在webGLStart方法中不同,對於較為複雜的流程最好將流程的每個階段寫在單獨的方法里,對於較多的對象則最好提取對象的共同點作為一個“類”,將每個對象作為類的實例。這樣可以將程式的複雜度分解,每次只關註其中的一小部分,降低編程難度。(設計模式的本質是對變數名進行管理,理論上講,如果編程者的記憶力足夠強、編程者之間的溝通效率足夠高,則這些所謂的“設計模式”都可以省略)

  B、在webGLStart方法中對場景初始化流程進行了劃分,各個流程如註釋所示:

 1 //對象框架架構
 2     function webGLStart()
 3     {
 4         //initWebSocket();//如何確保上一環結成功才開啟下一環節?
 5         initScene();//初始化場景,包括最初入門教程里的那些東西
 6         initArena();//初始化地形,包括天空盒,參照物等
 7         initEvent();//初始化事件
 8         initUI();//初始化場景UI
 9         initObj();//初始化一開始存在的可交互的物體
10         initLoop();//初始化渲染迴圈
11         MyGame.init_state=1;//更新初始化狀態
12         engine.hideLoadingUI();//隱藏載入UI
13         //MyGame.flag_startr=1;//這個是通過nohurry計時器自動啟動的,不需要手動啟動
14     }

  C、初始化場景

 1 function initScene()
 2     {//光照
 3         var light0 = new BABYLON.HemisphericLight("light0", new BABYLON.Vector3(0, 1, 0), scene);
 4         light0.diffuse = new BABYLON.Color3(1,1,1);//這道“顏色”是從上向下的,底部收到100%,側方收到50%,頂部沒有
 5         light0.specular = new BABYLON.Color3(0,0,0);
 6         light0.groundColor = new BABYLON.Color3(1,1,1);//這個與第一道正相反
 7         MyGame.lights.light0=light0;//將光照變數交給MyGame對象管理
 8         mesh_arr_cards=new BABYLON.Mesh("mesh_arr_cards", scene);
 9         //相機對象
10         var camera0= new BABYLON.FreeCamera("FreeCamera", new BABYLON.Vector3(0, 0, 0), scene);
11         //camera0.layerMask = 2;
12         //camera0.position=new BABYLON.Vector3(0, 0, -20);
13         camera0.minZ=0.001;
14         scene.activeCameras.push(camera0);
15         
16         //用BallMan作為CameraMesh的mesh
17         var player = new BallMan();
18         var obj_p={};//初始化參數
19         //計劃不使用物理引擎
20         var mesh_ballman=new BABYLON.Mesh("mesh_ballman",scene);
21         obj_p.mesh=mesh_ballman;
22         obj_p.name="本機";//顯示的名字
23         obj_p.id="本機";//WebSocket Sessionid
24         obj_p.image="../ASSETS/IMAGE/Rainbow.jpg";
25         player.init(
26                 obj_p,scene
27         );
28 
29         var cameramesh=new CameraMesh();
30         var obj_p={};//初始化參數
31         obj_p.mesh=mesh_ballman;
32         obj_p.mesh.isVisible=false;
33         obj_p.mesh.position=new BABYLON.Vector3(0,0,-20);
34         if(obj_p.mesh.ballman)
35         {
36             obj_p.mesh.ballman.head.position=obj_p.mesh.position.clone();
37         }
38         obj_p.methodofmove="host20171018";
39         obj_p.name="FreeCamera";//顯示的名字
40         obj_p.id="FreeCamera";//WebSocket Sessionid
41         obj_p.camera=camera0;
42         //obj_p.image="assets/image/play.png";
43         obj_p.flag_objfast=5;
44         cameramesh.init(
45                 obj_p,scene
46         );
47         MyGame.arr_myplayers[obj_p.name]=cameramesh;
48         MyGame.player=cameramesh;
49         MyGame.Cameras.camera0=camera0;
50         camera0.position=cameramesh.mesh.position.clone();
51         cameramesh.mesh.rotation=camera0.rotation.clone();
52         mesh_arr_cards.position=MyGame.player.mesh.ballman.backview._absolutePosition.clone();
53     }

  其中mesh_arr_cards是所有手牌的父網格,用來對手牌進行定位,事實上這個對象放在initArena或者initObj階段更加合理,但是因為相機對象的一些事件和這個網格有關,只好放在場景初始化階段。BallMan的外觀是一個球體網格,用來代表場景中的玩家,其用法可以參考https://www.cnblogs.com/ljzc002/p/7274455.html;CameraMesh是一個網格與相機的結合體,在第三人稱時用戶將能看見自己操縱的單位(關於BallMan和CameraMesh類的參數將在後面詳細介紹)。最後把各種對象都交給MyGame統一管理。

  D、初始化環境

 1 function initArena()
 2     {
 3         var mesh_base=new BABYLON.MeshBuilder.CreateSphere("mesh_base",{diameter:1},scene);
 4         mesh_base.material=MyGame.materials.mat_green;
 5         mesh_base.position.x=0;
 6         mesh_base.renderingGroupId=2;
 7         //mesh_base.layerMask=2;
 8         var mesh_base1=new BABYLON.MeshBuilder.CreateSphere("mesh_base1",{diameter:1},scene);
 9         mesh_base1.position.y=10;
10         mesh_base1.position.x=0;
11         mesh_base1.material=MyGame.materials.mat_green;
12         mesh_base1.renderingGroupId=2;
13         //mesh_base1.layerMask=2;
14         var mesh_base2=new BABYLON.MeshBuilder.CreateSphere("mesh_base2",{diameter:1},scene);
15         mesh_base2.position.y=-10;
16         mesh_base2.position.x=0;
17         mesh_base2.material=MyGame.materials.mat_green;
18         mesh_base2.renderingGroupId=2;
19         //mesh_base2.layerMask=2;
20         for(var i=0;i<5;i++)//建立五個標示組號的標記網格,標記從一(而不是零)開始
21         {
22             var plane=new BABYLON.MeshBuilder.CreatePlane("mesh_groupicon"+(i+1),{size:5},scene);
23             var mat_plane = new BABYLON.StandardMaterial("mat_plane"+(i+1), scene);
24             var texture_plane= new BABYLON.DynamicTexture("texture_plane"+(i+1), {width:100, height:100}, scene);
25             mat_plane.diffuseTexture =texture_plane;
26             plane.material=mat_plane;
27             var font = "bold 60px monospace";
28             texture_plane.drawText((i+1), 40, 70, font, "white", "green", true, true);//第一個是文字顏色,第二個則是完全填充的背景色
29             plane.position.x=-16;
30             plane.position.z=-2;
31             plane.renderingGroupId=2;
32             //plane.rotation.x=-Math.PI/2;//這會導致自由相機的視角發生bug??Y與Z軸混淆?
33             plane.isPickable=false;
34             plane.isVisible=false;
35             arr_mesh_groupicon.push(plane);
36             //plane.parent=mesh_arr_cards;
37         }
38     }

  建立了三個小綠球作為場景的參照物,建立了五個小平面作為分組標記,這五個標記暫時不可見(在調試分組標記的過程中Babylon.js發生了bug,相機輸入的Y軸和Z軸發生混淆,但沒有深入分析原因)。

  E、初始化事件

 1 function initEvent()
 2     {
 3         InitMouse();
 4         window.addEventListener("keydown", onKeyDown, false);//按鍵按下
 5         window.addEventListener("keyup", onKeyUp, false);//按鍵抬起
 6         window.addEventListener("resize", function () {
 7             if (engine) {
 8                 engine.resize();
 9             }
10         },false);
11     }

  InitMouse中是對滑鼠的四種事件監聽,具體代碼在Control20180312.js文件中,接下來監聽了按鍵按下、按鍵抬起、視窗尺寸變化。

  F、初始化UI

1 function initUI()
2     {
3         MakeFullUI();
4         //var advancedTexture = MyGame.fsUI;
5 
6     }

  代碼主體在FullUI.js文件中

  G、初始化對象

1 function initObj()
2     {//添加75個(?)實驗對象
3 
4         DrawCard4();
5         SortCard();
6     }

  具體代碼在DrawCard.js中

  H、初始化渲染迴圈(也是邏輯迴圈)

 1 function initLoop()
 2     {
 3         var _this=MyGame;
 4         scene.registerBeforeRender(function() {     //比runRenderLoop更早
 5         });
 6         scene.registerAfterRender(
 7                 function() {
 8                     if(MyGame.flag_startr==1)//如果開始渲染了
 9                     {//如果正在使用相機網格進行漫游
10                         if(MyGame.player.prototype=CameraMesh&&MyGame.flag_view=="first_lock")
11                         {
12                             host20171018(MyGame.player);
13                         }
14                     }
15                 }
16         );
17 
18         engine.runRenderLoop(function ()        //場景邏輯和AI也從這裡引入
19         {
20             if (divFps) {
21                 // Fps
22                 divFps.innerHTML = engine.getFps().toFixed() + " fps";
23             }
24             MyGame.HandleNoHurry();//這裡包含了運動使用的計時器
25             if(_this.flag_startr==1||_this.flag_view!="first_pick")
26             {
27                 //主相機和小地圖相機都隨著玩家的位置變化
28                 CamerasFollowActor(_this.player);
29             }
30 
31             _this.scene.render();
32 
33         });
34     }

   其中registerBeforeRender是在每一幀渲染之前執行的代碼,registerAfterRender是在每一幀渲染之後執行的代碼,除了scene之外mesh類對象也可以使用這樣的方法,這也意味著可以將渲染前後的代碼分散寫在多個地方,但這裡為了方便管理統一寫在一處。host20171018是根據按鍵狀態和視角計算player運動的方法,具體代碼在Moves.js文件中。

  runRenderLoop里是每一幀渲染時執行的代碼,這裡首先更新了當前幀數顯示,然後通過HandleNoHurry(代碼在Game類中)執行一些“需要周期性執行,但沒有必要每一幀都執行的代碼”,接下來通過CamerasFollowActor(Moves.js文件中)讓“和player關聯但不是player子元素”的其他對象跟隨player運動,最後調用場景的渲染方法。

 

  關於運動,上述代碼的計算流程是這樣的:

  player的position-》計算出player的_absolutePosition(?)-》registerBeforeRender-》根據player的_absolutePosition計算關聯對象的新位置-》渲染-》registerAfterRender-》host20171018更新player的position。

  考慮到player也許是其他元素的子元素,其position(位置)和_absolutePosition(絕對位置)可能不同(相差物體的世界矩陣),需要使用_absolutePosition來定位關聯對象;而Babylon.js根據position計算_absolutePosition的操作發生在registerBeforeRender之前,所以如果我們把host20171018放在registerBeforeRender中則會導致position更新而_absolutePosition仍為舊的,表現的效果就是相機運動時對象抖動。所以我們把host20171018放在registerAfterRender中,當然如果position和_absolutePosition完全相同,則從理論上講不存在這種限制,但並未測試過。

3、Game類

  A、初始化方法:

 1 Game=function(init_state,flag_view,wsUri,h2Uri)
 2 {//參數:初始化時的狀態代號,初始化時的瀏覽模式,webSocket的伺服器地址,h2資料庫地址
 3     var _this = this;
 4     this.scene=scene;
 5     this.loader =  new BABYLON.AssetsManager(scene);//資源管理器,用於預先載入資源
 6     //控制者數組
 7     this.arr_myplayers={};
 8     this.arr_npcs={};//NPC數組
 9     this.count={};//綜合計數器對象
10     this.count.count_name_npcs=0;//NPC命名計數器,每產生一個NPC則加一,避免NPCID重覆
11     this.Cameras={};//scene里也有?,綜合相機對象
12     this.websocket;
13     this.lights={};//綜合光源對象
14     this.fsUI=BABYLON.GUI.AdvancedDynamicTexture.CreateFullscreenUI("ui1");//全屏GUI對象
15     this.hl=new BABYLON.HighlightLayer("hl1", scene);//高光層對象,下麵是高光層的一些參數
16     this.hl.blurVerticalSize = 1.0;//這個影響的並不是高光的粗細程度,而是將它分成 多條以產生模糊效果,數值表示多條間的間隙尺寸
17     this.hl.blurHorizontalSize =1.0;
18     this.hl.innerGlow = false;//取消內部光暈
19     this.hl.alphaBlendingMode=3;
20     //this.hl.isStroke=true;
21     //this.hl.blurTextureSizeRatio=2;
22     //this.hl.mainTextureFixedSize=100;
23     //this.hl.renderingGroupId=3;
24     //this.hl._options.mainTextureRatio=1000;
25 
26     this.wsUri=wsUri;
27     this.init_state=init_state;//當前運行狀態
28     /*0-startWebGL
29     1-WebGLStarted
30     2-PlanetDrawed
31      * */
32     this.h2Uri=h2Uri;
33     //我是誰
34     this.WhoAmI=newland.randomString(8);
35 
36     this.materials={};//綜合材質對象,下麵初始化了幾種常用的材質
37     var mat_frame = new BABYLON.StandardMaterial("mat_frame", scene);
38     mat_frame.wireframe = true;
39     this.materials.mat_frame=mat_frame;
40     var mat_red=new BABYLON.StandardMaterial("mat_red", scene);
41     mat_red.diffuseColor = new BABYLON.Color3(1, 0, 0);
42     var mat_green=new BABYLON.StandardMaterial("mat_green", scene);
43     mat_green.diffuseColor = new BABYLON.Color3(0, 1, 0);
44     var mat_blue=new BABYLON.StandardMaterial("mat_blue", scene);
45     mat_blue.diffuseColor = new BABYLON.Color3(0, 0, 1);
46     this.materials.mat_red=mat_red;
47     this.materials.mat_green=mat_green;
48     this.materials.mat_blue=mat_blue;
49 
50     this.models={};//綜合模型對象
51     this.textures={};//綜合紋理對象
52     this.texts={};//綜合文本對象
53 
54     this.flag_startr=0;//開始渲染並且地形初始化完畢
55     this.flag_starta=0;//開始執行NPC的AI邏輯
56     this.list_nohurry=[];//需要周期性進行的工作
57     this.nohurry=0;//一個計時器,讓一些計算不要太頻繁
58     this.flag_online=false;//是否是線上場景
59     this.flag_view=flag_view;//first/third/input/free
60     this.flag_controlEnabled = false;
61     this.arr_keystate=[];//按鍵狀態數組
62 }

  這段代碼中初始化了一些場景中可能會用到的變數,最整潔的情況是把所有的全局變數都作為MyGame的屬性加以管理,但很難做到。

  B、原型方法:

    每個Game類的實例都會繼承這些方法:

 1 Game.prototype={
 2     AddNohurry:function(name,delay,lastt,todo,count)
 3     {//名字,每次執行之間的間隔(最小間隔),上一次執行時間,要執行的函數名,已經執行的次數
 4         if(this.list_nohurry[name])//如果已經有叫做這個名字的任務
 5         {
 6             return;
 7         }
 8         this.list_nohurry[name]={delay:delay,lastt:lastt,todo:todo
 9             ,count:count};
10     },
11     RemoveNohurry:function(name)
12     {
13         delete this.list_nohurry[name];
14     },
15     HandleNoHurry:function()
16     {
17         var _this=this;
18         if( _this.flag_startr==0)//開始渲染並且地形初始化完畢!!
19         {
20             engine.hideLoadingUI();//隱藏載入UI
21             _this.flag_startr=1;//標誌開始渲染
22             _this.lastframet=new Date().getTime();
23             _this.firstframet=_this.lastframet;
24             _this.DeltaTime=0;
25         }
26         else
27         {//如果已經開始渲染
28             _this.currentframet=new Date().getTime();//當前幀的時間
29             _this.DeltaTime=_this.currentframet-_this.lastframet;//取得兩幀之間的時間
30             _this.lastframet=_this.currentframet;
31             /*_this.nohurry+=_this.DeltaTime;//這個代碼用於只執行一個定時任務的情況
32 
33             if(MyGame&&_this.nohurry>1000)//每一秒進行一次
34             {
35                 _this.nohurry=0;
36 
37             }*/
38             //var time_start=_this.currentframet-_this.firstframet;//當前時間到最初過了多久
39             for(var i=0;i<_this.list_nohurry.length;i++
              
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Cookie主要屬性 Cookie主要屬性: path domain max age expires:是expires的補充,現階段有相容性問題:IE低版本不支持,所以一般不單獨使用 secure httponly JS不能讀寫HttpOnly Cookie 屬性之間使用英文分號和空格("; ")連 ...
  • 在互聯網高速發現的今天,可以說每天都在變化著一不留神你就會錯過一個億,所以你不得不時刻的保持著高度的專註。 互聯網的信息是多元的,大量的,在海量的信息中很容易就會迷失自己,沉浸在互聯網給我們帶來的快樂當中。 所以不管你做的什麼行業,時刻都要保持關註,因為我是做技術的,所以我們今天就來說說技術把。 談 ...
  • 算術運算符:+,-,*,/字元運算符 :+(字元串連接),+=(字元串連接複合);布爾運算符:!,&&,||;一元操作符:++,—,+,-;關係比較運算符: ,=,=,!=,==,====,!==;按位運算符: ~,&,,|,^,>,>>>賦值運算符:=,複合賦值(+=,-=,*=,/=)複合按位賦... ...
  • 老鐵們,我胡漢三又回來了,最近掃黃比較嚴,然後我就出去避了避風頭, 今天我們來總結總結對前端進行性能優化的方法吧,這篇隨筆沒啥代碼,但是我總結了總結,看一下總歸是有點用的 1.減少http請求 a.CSS sprites(精靈圖),即合併圖片,減少圖片請求次數 b.CSS,JS源碼壓縮。 c.cdn ...
  • 前端開發 前端開發前端概述一、什麼是前端二、前端開發技術棧HTMLCSSJavaScript前端三劍客一、HTML1、標記語言2、html 為前端頁面的主體,有標簽、指令與轉義字元等組成。3、html 發展史代表版本4、文檔類型二、CSS三、JavaScript1.編程語言2.js為前端頁面的腳步, ...
  • 一丶盒模型的屬性(重要) 1.padding padding是標準文檔流,父子之間調整位置 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>padding</title> <style> *{ padding: 0; margi ...
  • 微信發支付寶紅包(花唄) 原理很簡單,就是利用支付寶花唄的分享紅包到微信,微信用戶通過掃碼跳轉到支付寶領取。 第一步,獲得紅包地址 進入自己的支付寶紅包“天天領紅包”,分享二維碼到微信,然後用網上的識別二維碼的工具或網頁將支付寶二維碼解析一下,解析結果是一個url,例如: 這個就是支付寶天天領紅包的 ...
  • 使用 Array.includes 替代 Array.indexOf “如果需要在數組中查找某個元素,請使用 Array.indexOf。” 我記得在我學習 JavaScript 的課程中有類似的這麼一句話。毫無疑問,這完全正確! 在 MDN 文檔中,對 Array.indexOf 的描述是:返回在 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...