結合WebSocket編寫WebGL綜合場景示例

来源:http://www.cnblogs.com/ljzc002/archive/2016/11/05/6029095.html
-Advertisement-
Play Games

在WebGL場景中導入多個Babylon骨骼模型,在區域網用WebSocket實現多用戶交互控制。 ...


在WebGL場景中導入多個Babylon骨骼模型,在區域網用WebSocket實現多用戶交互控制。

首先是場景截圖:

上圖在場景中導入一個Babylon骨骼模型,使用asdw、空格、滑鼠控制加速度移動,在移動時播放骨骼動畫。

上圖在場景中加入更多的骨骼模型(兔子),兔子感知到人類接近後會加速遠離人類。

上圖,一個區域網中的新玩家進入場景,(他們頭上的數字是WebSocket分配的session id),兔子們受到0和1的疊加影響。

 

具體實現:

一、工程結構:

 前臺WebStorm工程:

其中map.jpg是地形高度圖,tree.jpg不是樹而是地面泥土的紋理。。。

LIB文件夾里是引用的第三方庫(babylon.max.js是2.4版),MYLIB文件夾里是我自己編寫或整理修改的庫,PAGE里是專用於此網頁的腳本文件

  其中FileText.js是js前臺文件處理庫(這裡只用到了其中的產生日期字元串函數)

  MoveWeb.js是加速度計算庫

  Sdyq.js里是對物體對象的定義和操作監聽

  Player.js里是繼承了物體對象的玩家對象和動物對象的定義

  utils是一些其他工具

  View是頁面控制庫

MODEL文件夾里是人物和兔子的骨骼模型文件。

後臺MyEclipse工程:

使用JDK1.7,因為Tomcat v8.0里包含了WebSocket所用的庫,所以不需要引入額外jar包,只寫了一個類。

二、基本場景構建和骨骼模型導入:

html頁面文件:

 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>使用websocket聯網進行數據傳遞,這個節點應該既可以做主機也可以加入他人的主機</title>
 6 </head>
 7 <body>
 8 <div id="all_base" style="position:fixed;top:0px;left: 0px;">
 9     <div id="div_canvas" style="float: left;width: 75%;border: 1px solid">
10         <canvas id="renderCanvas" style="width: 100%;height: 100%"></canvas>
11     </div>
12     <div id="div_log" style="float: left;border: 1px solid;overflow-y: scroll">
13     </div>
14     <div id="div_bottom" style="float: left;width: 100%;height: 100px;padding-top: 10px;padding-left: 10px">
15         <input style="width: 200px" id="str_ip" value="localhost">
16         <input id="str_name">
17         <button id="btn_create" onclick="createScene()" disabled=true>啟動場景</button>
18         <button id="btn_connect" onclick="Connect()" >websocket連接</button>
19         <button id="btn_close" onclick="Close()" disabled=true>關閉連接</button>
20         <span id="str_id" style="display: inline-block"></span><br><br>
21         <input style="width: 400px" id="str_message">
22         <button id="btn_send" onclick="Send()">發送</button>
23     </div>
24 </div>
25 <script src="../JS/LIB/babylon.max.js"></script>
26 <script src="../JS/MYLIB/View.js"></script>
27 <script src="../JS/LIB/jquery-1.11.3.min.js"></script>
28 <script src="../JS/MYLIB/FileText.js"></script>
29 <script src="../JS/MYLIB/Sdyq.js"></script>
30 <script src="../JS/MYLIB/player.js"></script>
31 <script src="../JS/MYLIB/MoveWeb.js"></script>
32 <script src="../JS/MYLIB/utils.js"></script>
33 <script src="../JS/PAGE/scene_link.js"></script>
34 <script src="../JS/PAGE/WebSocket.js"></script>
35 </body>
36 <script>
37     var username="";
38     window.onload=BeforeLog;
39     window.onresize=Resize_Pllsselect;
40     function BeforeLog()
41     {
42         Resize_Pllsselect();
43         //DrawYzm();
44         //createScene();
45     }
46     var str_log=document.getElementById("div_log");
47     function Resize_Pllsselect()
48     {
49         var size=window_size();
50         document.getElementById("all_base").style.height=(size.height+"px");
51         document.getElementById("all_base").style.width=(size.width+"px");
52         document.getElementById("div_canvas").style.height=(size.height-100+"px");
53         str_log.style.height=(size.height-100+"px");
54         str_log.style.width=((size.width/4)-4+"px");
55         if(engine!=undefined)
56         {
57             engine.resize();
58         }
59     }
60 
61     var state="offline";
62 
63     var arr_myplayers=[];
64     var arr_webplayers=[];
65     var arr_animals=[];
66     var arr_tempobj=[];//暫存對象初始化信息
67     var tempobj;
68 
69     var canvas = document.getElementById("renderCanvas");
70     var ms0=0;//上一時刻毫秒數
71     var mst=0;//下一時刻毫秒數
72     var schange=0;//秒差
73 
74     var skybox,
75             scene,
76             sceneCharger = false,
77             meshOctree,
78             cameraArcRotative = [],//弧形旋轉相機列表
79             octree;
80     var engine;
81     var shadowGenerator ;
82 
83 </script>
84 </html>
View Code

其中包含對頁面尺寸大小變化的響應和一些全局變數的定義

scene_link.js文件中包含場景的構建和模型導入:

1、在createScene()方法的開頭部分建立了一個基本的PlayGround場景:

 1 engine = new BABYLON.Engine(canvas, true);
 2     engine.displayLoadingUI();
 3     scene = new BABYLON.Scene(engine);
 4 
 5     //在場景中啟用碰撞檢測
 6     scene.collisionsEnabled = true;
 7     //scene.workerCollisions = true;//啟動webworker進程處理碰撞,確實可以有效使用多核運算,加大幀數!!
 8     //但是worker是非同步運算的,其數據傳輸策略會導致movewithcollition執行順序與期望的順序不符
 9 
10     //定向光照
11     var LightDirectional = new BABYLON.DirectionalLight("dir01", new BABYLON.Vector3(-2, -4, 2), scene);
12     LightDirectional.diffuse = new BABYLON.Color3(1, 1, 1);//散射顏色
13     LightDirectional.specular = new BABYLON.Color3(0, 0, 0);//鏡面反射顏色
14     LightDirectional.position = new BABYLON.Vector3(250, 400, 0);
15     LightDirectional.intensity = 1.8;//強度
16     shadowGenerator = new BABYLON.ShadowGenerator(1024, LightDirectional);//為該光源建立陰影生成器,用在submesh上時一直在報錯,不知道為了什麼
17 
18     //弧形旋轉相機
19     cameraArcRotative[0] = new BABYLON.ArcRotateCamera("CameraBaseRotate", -Math.PI/2, Math.PI/2.2, 12, new BABYLON.Vector3(0, 5.0, 0), scene);
20     cameraArcRotative[0].wheelPrecision = 15;//滑鼠滾輪?
21     cameraArcRotative[0].lowerRadiusLimit = 2;
22     cameraArcRotative[0].upperRadiusLimit = 22;
23     cameraArcRotative[0].minZ = 0;
24     cameraArcRotative[0].minX = 4096;
25     scene.activeCamera = cameraArcRotative[0];
26     cameraArcRotative[0].attachControl(canvas);//控制關聯
27 
28     //地面
29     //name,url,width,height,subdivisions,minheight,maxheight,updateble,onready,scene
30     ground = BABYLON.Mesh.CreateGroundFromHeightMap("ground", "../IMAGE/map.jpg", 1000, 1000, 100, 0, 60, scene, true);//地面類型的網格
31     var groundMaterial = new BABYLON.StandardMaterial("groundMat", scene);//泥土材質
32     groundMaterial.diffuseTexture = new BABYLON.Texture("../IMAGE/tree.png", scene);//地面的紋理貼圖
33     groundMaterial.diffuseTexture.uScale = 50.0;//紋理重覆效果
34     groundMaterial.diffuseTexture.vScale = 50.0;
35     ground.material = groundMaterial;
36     ground.checkCollisions = true;//檢測碰撞
37     ground.receiveShadows = true;//接收影子
38 
39     //
40     var Mur = BABYLON.Mesh.CreateBox("Mur", 1, scene);
41     Mur.scaling = new BABYLON.Vector3(15, 6, 1);
42     Mur.position.y = 20;
43     Mur.position.z = 20;
44     Mur.checkCollisions = true;

其中各個方法的具體用法可以參考官方的基礎教程

2、接下來是在場景中導入第一個人物的骨骼模型:

 1 //角色導入,載入哪個mesh、文件目錄、文件名、加入場景、回調函數
 2     BABYLON.SceneLoader.ImportMesh("", "../MODEL/him/", "him.babylon", scene, function (newMeshes, particleSystems, skeletons)
 3     {//載入完成的回調函數
 4         var Tom=new Player;
 5         var obj_p={};//初始化參數對象
 6         obj_p.mesh=newMeshes[0];//網格數據
 7         obj_p.scaling=new BABYLON.Vector3(0.05, 0.05, 0.05);//縮放
 8         obj_p.position=new BABYLON.Vector3(-5.168, 30.392, -7.463);//位置
 9         obj_p.rotation=new BABYLON.Vector3(0, 3.9, 0);// 旋轉
10         obj_p.checkCollisions=true;//使用預設的碰撞檢測
11         obj_p.ellipsoid=new BABYLON.Vector3(0.5, 1, 0.5);//碰撞檢測橢球
12         obj_p.ellipsoidOffset=new BABYLON.Vector3(0, 2, 0);//碰撞檢測橢球位移
13         obj_p.skeletonsPlayer=skeletons;
14         obj_p.methodofmove="controlwitha";
15         obj_p.name=username;
16         obj_p.id=id;
17         obj_p.p1="";
18         obj_p.p2="../MODEL/him/";
19         obj_p.p3="him.babylon";
20         var len=newMeshes.length;//對於複雜的模型來說newMeshes的其他部分也必須保存下來
21         var arr=[];
22         for(var i=1;i<len;i++)
23         {
24             arr.push(newMeshes[i]);
25         }
26         obj_p.submeshs=arr;
27 
28         Tom.init(
29             obj_p
30         );
31         arr_myplayers[username]=Tom;
32 
33         if(state=="online")
34         {
35             var arr=[];
36             arr.push("addnewplayer");
37             arr.push(Tom.mesh.scaling.x);
38             arr.push(Tom.mesh.scaling.y);
39             arr.push(Tom.mesh.scaling.z);
40             arr.push(Tom.mesh.position.x);
41             arr.push(Tom.mesh.position.y);
42             arr.push(Tom.mesh.position.z);
43             arr.push(Tom.mesh.rotation.x);
44             arr.push(Tom.mesh.rotation.y);
45             arr.push(Tom.mesh.rotation.z);
46             arr.push(Tom.p1);
47             arr.push(Tom.p2);
48             arr.push(Tom.p3);
49             arr.push(Tom.meshname);
50             var dt=new Date();
51             console.log(dt.getTime()+"send addnewplayer"+id);
52             doSend(arr.join("@"));
53         }
54 
55         cameraArcRotative[0].alpha = -parseFloat(arr_myplayers[username].mesh.rotation.y) - 4.69;//初始化相機角度
56 
57     });

   其中BABYLON.SceneLoader.ImportMesh是一個非同步的把伺服器端場景文件導入本地記憶體的方法,第一個參數表示導入場景文件中的哪一個Mesh,為空表示都導入(一個場景文件里可能包含多個模型,但該示例中的場景文件里只有一個模型,所以也叫做模型文件),第二個參數是文件所在的相對路徑,第三個參數是文件名,第四個參數是文件加入的場景,第五個參數是導入完成後的回調函數。

  回調函數的newMeshes參數是所有導入的Mesh組成的數組,skeletons參數是所有導入的骨骼動畫數組。事實上一個模型可能由多個mesh組合而成,比如示例中的him模型的newMeshes[0]只是一個空殼,newMeshes[1]到newMeshes[5]才是模型各個部分的實際Mesh,後五個Mesh是newMeshes[0]的“submesh”,newMeshes[0]是後五個Mesh的parent,在理想情況下這些Mesh之間的關係和Mesh與骨骼動畫(skeleton)之間的關係由Babylon引擎自動管理。

  在回調函數中,定義Tom為一個Player“類”對象,第五行定義的obj_p對象是Player對象的初始化參數對象,Player.init()方法定義在player.js文件中:

 1 //玩家對象
 2 Player=function()
 3 {
 4     sdyq.object.call(this);
 5 }
 6 Player.prototype=new sdyq.object();
 7 Player.prototype.init=function(param)
 8 {
 9     param = param || {};
10     sdyq.object.prototype.init.call(this,param);//繼承原型的方法
11     this.flag_standonground=0;//是否接觸地面
12     this.keys={w:0,s:0,a:0,d:0,space:0,ctrl:0,shift:0};//按鍵是否保持按下,考慮到多客戶端並行,那麼勢必每個player都有自己的keys!!
13     this.flag_runfast=1;//加快速度
14     this.name=param.name;
15     this.id=param.id;
16     this.p1=param.p1;
17     this.p2=param.p2;
18     this.p3=param.p3;
19 。。。

  可以看到Player對象繼承自sdyq.object對象,Player對象的原型是sdyq.object對象,在Player對象的init方法中,先初始化屬於原型的屬性,再初始化自己這個“類”新添加的屬性。

  sdyq.object對象的定義在Sdyq.js文件中:

 1 //物體本身的屬性和初始化
 2 sdyq={};//3D引擎
 3 sdyq.object=function()
 4 {//在地面上加速度運動的物體
 5 
 6 }
 7 sdyq.object.prototype.init = function(param)
 8 {
 9     this.keys={w:0,s:0,a:0,d:0,space:0,ctrl:0,shift:0};//按鍵是否保持按下
10     this.witha0={forward:0,left:0,up:-9.82};//非鍵盤控制產生的加速度
11     this.witha={forward:0,left:0,up:-9.82};//環境加速度,包括地面阻力和重力,現在還沒有風力
12     this.witha2={forward:0,left:0,up:0};//鍵盤控制加速度與物體本身加速度和非鍵盤控制產生的加速度合併後的最終加速度
13     this.v0={forward:0,left:0,up:0};//上一時刻的速度
14     this.vt={forward:0,left:0,up:0};//下一時刻的速度
15     this.vm={forward:15,backwards:5,left:5,right:5,up:100,down:100};//各個方向的最大速度
16     //this.flag_song=0;//是否接觸地面
17     this.flag_runfast=1;//加快速度
18     this.ry0=0;//上一時刻的y軸轉角
19     this.ryt=0;//下一時刻的y軸轉角
20     this.rychange=0;//y軸轉角差
21     this.mchange={forward:0,left:0,up:0};//物體自身坐標繫上的位移
22     this.vmove=new BABYLON.Vector3(0,0,0);//世界坐標系中每一時刻的位移和量
23     this.py0=0;//記錄上一時刻的y軸位置,和下一時刻比較確定物體有沒有繼續向下運動!!
24 
25     param = param || {};
26     this.mesh=param.mesh;
27     this.mesh.scaling=param.scaling;
28     this.mesh.position=param.position;
29     this.mesh.rotation=param.rotation;
30     this.mesh.checkCollisions=param.checkCollisions;
31     this.mesh.ellipsoid=param.ellipsoid;
32     this.mesh.ellipsoidOffset=param.ellipsoidOffset;
33     this.meshname=this.mesh.name;
34     this.skeletonsPlayer=param.skeletonsPlayer||[];
35     this.submeshs=param.submeshs;
36     this.ry0=param.mesh.rotation.y;
37     this.py0=param.mesh.position.y;
38     this.countstop=0;//記錄物體靜止了幾次,如果物體一直靜止就停止發送運動信息
39 
40     this.PlayAnnimation = false;
41 
42     this.methodofmove=param.methodofmove||"";
43     switch(this.methodofmove)
44     {
45         case "controlwitha":
46         {
47             window.addEventListener("keydown", onKeyDown, false);//按鍵按下
48             window.addEventListener("keyup", onKeyUp, false);//按鍵抬起
49             break;
50         }
51         default :
52         {
53             break;
54         }
55     }
56 }

  sdyq.object對象的初始化方法中包含了對mesh姿態的詳細設定、對鍵盤操作的監聽設定和適用於加速度運動的各項參數設定,各種加速度運動的物體都可以用sdyq.object對象來擴展產生。

  在Player對象的初始化方法中還為每個玩家添加了id顯示(頭上的那個數字):

 1 //在玩家頭上顯示名字,clone時這個也會被clone過去,要處理一下!!!!
 2     var lab_texture=new BABYLON.Texture.CreateFromBase64String(texttoimg2(this.id),"datatexture"+this.id,scene);//使用canvas紋理!!
 3     var materialSphere1 = new BABYLON.StandardMaterial("texture1"+this.id, scene);
 4     materialSphere1.diffuseTexture = lab_texture;
 5     var plane = BABYLON.Mesh.CreatePlane("plane"+this.id, 2.0, scene, false, BABYLON.Mesh.FRONTSIDE);
 6     //You can also set the mesh side orientation with the values : BABYLON.Mesh.FRONTSIDE (default), BABYLON.Mesh.BACKSIDE or BABYLON.Mesh.DOUBLESIDE
 7     materialSphere1.diffuseTexture.hasAlpha = true;//應用紋理的透明度
 8 
 9     plane.position=new BABYLON.Vector3(0,75,0);//其父元素應用過0.05之縮放,故而這裡位移量要*20
10     plane.rotation.y = Math.PI;
11     plane.scaling.x=20;
12     plane.scaling.y=4;
13     plane.parent=this.mesh;
14 
15     plane.material=materialSphere1;
16     this.lab=plane;

  在這裡使用了canvas現場產生紋理(術語叫“程式貼圖”),其中texttoimg2()方法的定義在utils.js文件中:

 1 //把文字轉變為圖片jpeg
 2 function texttoimg(str)
 3 {
 4     var c=document.createElement("canvas");
 5     c.height=20;
 6     c.width=100;
 7     var context = c.getContext('2d');
 8     context.font="normal 15px sans-serif";
 9     context.clearRect(0, 0, canvas.width, canvas.height);
10     context.fillStyle="rgb(255,255,255)";
11     context.fillRect(0,0,canvas.width,canvas.height);
12     context.fillStyle = "rgb(0,0,0)";
13     context.textBaseline = 'top';
14     context.fillText(str,(c.width-str.length*15)/2,0, c.width*0.9);
15     var str_src=c.toDataURL("image/jpeg");
16     return str_src;
17     //return c;
18 }
19 //把文字轉變為圖片PNG
20 function texttoimg2(str)
21 {
22     var c=document.createElement("canvas");
23     c.height=20;
24     c.width=100;
25     var context = c.getContext('2d');
26     context.font="normal 20px sans-serif";
27     context.clearRect(0, 0, canvas.width, canvas.height);
28     //context.fillStyle="rgb(255,255,255)";
29     //context.fillRect(0,0,canvas.width,canvas.height);
30     context.fillStyle = "rgb(255,255,255)";
31     context.textBaseline = 'middle';//
32     context.fillText(str,(c.width-str.length*20)/2,10, c.width*0.9);
33     var str_src=c.toDataURL("image/png");
34     return str_src;
35     //return c;
36 }

  該代碼綜合網上多個教程修改而來,其中生成jpeg的難點在於canvas預設生成四通道圖像,而jpeg在去除透明度通道時會自動將透明度通道變成黑色,於是jpeg一片漆黑,解決方法是先畫一個不透明的白色矩形背景,擋住所有透明通道,再在白色背景上畫圖。

  在模型導入完畢後把Tom設為玩家列表對象arr_myplayers的一個屬性,如果當前玩家處於線上狀態,則還要把其載入狀態同步給其他玩家,具體同步方式稍後介紹。

  最後把玩家的相機定位到玩家模型的身後,做第三方跟隨視角狀。

三、加速度運動控制

在scene_link.js文件的中部可以看到scene.registerBeforeRender()方法,這個方法的作用是在每次渲染前調用作為它的參數的方法,我們通過這個方法在每次渲染前對物體的下一步運動情況進行計算:

 1 scene.registerBeforeRender(function()
 2     {//每次渲染前
 3         	   

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、事件: 1、模式觸發事件: ①DOM:elem.onXXX();只能觸發直接用onXXX綁定的事件處理函數;用addEventistener添加的事件監聽無法模擬出發觸發; ②jQuery:$(...).trigger("事件名");可簡寫:$(...).事件名; 2、頁面載入後執行: ①jQu ...
  • 寫的比較弱,只能處理50道以內的選項為A-D的單選題,正確答案自己輸進去,必須要大寫,不能有空格和逗號,提交會出分,錯誤的題號和答案會console.log()出來. <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title></ti ...
  • 這是我在面試大公司時碰到的一個筆試題,當時自己雲里霧裡的胡寫了一番,回頭也曾思考過,最終沒實現也就不了了之了。 昨天看到有網友說面試中也碰到過這個問題,我就重新思考了這個問題的實現方法。 對於想進大公司的童鞋,我想多說兩句,基礎知識真的很關鍵。平時在工作中也深刻體會到,沒有扎實的基礎知識,簡單問題容 ...
  • 目前來看,團隊內部前端項目已全面實施組件化開發。組件化的好處太多,如:按需載入、可復用、易維護、可擴展、少挖坑、不改組件代碼直接切成伺服器端渲染(如 "Nuclear" 組件化可以做到,大家叫同構)... 怎麼做到這麼強大的優勢,來回憶下以前見過的坑,或者現有項目里的坑。 CSS層疊樣式?保佑不要污 ...
  • 1. 使用require.js的意義 (1)實現JS文件的非同步載入,避免網頁因為載入JS文件緩慢造成網頁未響應 (2)管理模塊之間的依賴性,便於代碼的編寫和維護。頁面中只需要引入require.js和main.js,其餘的js文件全部通過require.js管理。 2. 獲取require.js r ...
  • 本文版權歸博客園和作者本人共同所有,轉載和爬蟲請註明原文地址。 寫在前面 好多做web開發的朋友,在學習數據結構和演算法時可能比較討厭C和C++,上學的時候寫過的也忘得差不多了,更別提沒寫過的了。但幸運的是,你會JavaScript啊。我想說學好數據結構和基本演算法並非是要我們必須要去書寫,演算法的工作有 ...
  • 總結一下自己在寫這個需求遇到的問題,相信大家應該是經常遇到的。即要求滾輪滾動到指定的位置。先看下基本的解決方案。 1.給鏈接a加個#的方式來實現跳轉。(錨點方法)這裡直接貼下代碼: html頁面: css樣式: 該錨點法,不需要任何的js代碼,即可實現跳轉的方法。缺點:點擊鏈接url發生變化,刷新的 ...
  • 一、Redux介紹 Redux的設計思想很簡單,就兩句話: 二、Redux基本概念和API Store Store就是保存數據(state)的地方,整個應用只能有一個Store。Redux通過createStore來生成store。 State Store對象包含所有State,某一時刻的數據集合就 ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...