Untiy學習:WebGL與Http伺服器通訊

2020-08-12 17:46:18

1;什麼是Unity webGL

webGL 的編譯選項允許unity發佈像使用了HTML5和webGL渲染API技術來使unity程式可以跑在瀏覽器中的javascript 程式。想要編譯和測試WebGL程式,只需要在Build Playersetting裡選擇WebGL編譯平臺即可。 
2:unity是怎麼樣發佈爲webGl程式的 
爲了執行webgl,需要我們的所有程式碼都是採用JavaScript編寫,unity使用emscripten編譯器工具鏈交叉編譯unity執行時的程式碼(C和C++)爲asm.js JavaScript,asm.js 是一個高度優化的JavaScript子集,並且可以採用javascript 的AOT編譯引擎將asm.js程式碼變爲高效能的程式碼。 如果程式碼採用C#編譯,那麼unity將使用Il2CPP技術將C#程式碼轉換爲相應的的C++原始檔,然後編譯器使用emscripten來使C++再轉爲JavaScript程式碼。當然,這裏會存在相容性的問題。unity的webGL 程式目前在大多數的桌面瀏覽器中都能支援,但是行動端的目前還無法支援。 
1:,Unity並不是所有的功能都能在webGl中支援, 
首先;由於JavaScript不支援多執行緒,所以多執行緒只適用在unity內部執行緒對程式的加速和一些託管DLL以及使用一些執行緒指令碼程式碼 ,基本上System.Threading名稱空間裡的東西是不支援的。 
2:webGL還無法在VS和MonoDevelop裡偵錯 
3:由於安全問題瀏覽器不能直接存取IP通訊端。 
4:WebGL圖形API相當於OpenGL ES 2,這具有一定的侷限性。 
5:WebGL生成使用自定義後臺音訊,基於Web Audio API。這隻支援基本的音訊功能 
6:WebGL是一個AOT(靜態編譯)平臺,所以它不允許動態生成的程式碼中使用system.reflection.emit。這是對所有其他il2cpp平臺,如iOS,和大多數控制檯是相同的。 
3:瀏覽器的相容情況

桌面瀏覽器相容情況 
Mozilla Firefox 42 Google Chrome 46 Apple Safari 9.0 MS Internet Explorer 11 MS Edge 13 
對執行unity的webGL內容的支援能力 X (1) X (1) X (Safari 8 and higher) X (IE 11 and higher) X 
Web Audio unity WebGL中的音訊需要呼叫Web Audio API來播放 X X X - X 
全螢幕支援 X X - (2) X X 
滑鼠鎖定支援 X X - - - (3) 
Gamepad support X X - - X 
IndexedDB Required for local storage as used by the Data Caching feature, the PlayerPrefs class, and WWW.LoadFromCacheOrDownload. X (4) X X (4) X X 
WebSockets Required for Networking. X X X X X 
WebRTC Required by the WebCamTexture class. X X - - X 
WebGL 2.0 - (5) - - - - 
asm.js AOT compilation asm.js is a susbset of JavaScript for which a browser can specifically optimize. Browsers which implement asm.js support may be able to run Unity WebGL content faster, as Unity uses asm.js. X - - - X 
Notes (6) (7)

註釋: 
1:WebGL可能不支援特定的老顯示卡。 
2:Safari瀏覽器支援HTML5全螢幕API,但全螢幕模式時沒有鍵盤輸入,所以unity在Safari中執行時將禁止全螢幕功能。 
3:Edge不支援滑鼠鎖定,Edge13可能會支援。 
4:Firefox和Safari上42版本不支援在一個iframe中執行的內容IndexedDB。火狐43或更高版本將修復問題。、 
5:Firefox 支援WebGL2.0,但它預設是禁用的,需要啓用:設定。 
6:Chrome可能需要大量的記憶體來解析生成的JavaScript程式碼,當在32位元版本瀏覽器中載入webgl內容時可能會導致記憶體錯誤或崩潰。 
7:IE瀏覽器不支援音訊並且還太慢對於載入unity的webGL內容,出於這個原因,

unity將在使用Internet Explorer開啓內容時顯示使用不支援的瀏覽器的警告 
4:webGl的發佈工作

 編譯一個webGL 專案時,unity將會建立一些檔案

*一個index.html檔案,這個檔案可以直接使用瀏覽器開啓,但是出於考慮,chrome限制使用者從本地開啓這種檔案。 
*一個Development和 Release 資料夾, 這些是生成的輸出檔案,一個用於繼續開發,一個是可以用於直接發佈。 
*一個TemplateData資料夾,一些資原始檔。 
發佈時當勾選Development Build複選框時,unity將生成一個Development Build(帶有分析器支援和錯誤控制檯); 
此外,Development Build是非壓縮的,所以生成的JavaScript是可讀的並且保留了函數名(以便於你得到有用的錯誤堆疊跟蹤)但是非常分散。 
使用Use pre-built Engine選項可用於加快開發迭代時間在開發過程中。啓用此選項時,只有託管程式碼將被重建,然後與預構建Unity引擎動態鏈接,因此工程重新生成的速度會提升30%到40%。但是注意,這種編譯只適合開的目的,因爲這樣會產生多餘的引擎程式碼。此外,由於動態鏈接開銷,這種編譯的效能有點慢於正常編譯。 
當你想設定你的unity webGL內容時必須勾選Autoconnect Profiler複選框,雖然在webGL上連線分析器使用WebSockets ,但是瀏覽器只允許從內容向外的連線,所以在WebGL使用Profiler的唯一方法是選中「自動連線分析器」有內容連線到編輯。 
5:webGl Graphics 
WebGL是一個瀏覽器的圖形渲染引擎API,目前是基於OpenGL ES 2.0 圖形庫。Unity webGL目前只支援烘焙GL不支援實時GL,而且,僅僅支援非定向lightmaps,webGL在執行時也不支援過程化材質,過程化材質在編譯時將被烘焙爲普通材質。webGL 也不支援使用Movie Texture播放視訊,但是你可以通過使用HTML元素在WebGL裡高效的播放視訊。 
6:WebGL的網路通訊 
由於安全性的影響,JavaScript程式碼沒有直接存取IP通訊端來實現網路連線。因此,該.NET網路類(System.Net名稱空間中的一切,特別是System.Net.Sockets)在WebGL中不能工作。UnityEngine.Network* 類也是這樣,編譯WebGL時將找不到這些類。如果你需要在WebGL使用網路通訊,你現在可以選擇使用unity的WWW 或UnityWebRequest 類或則支援webGL的新的Unity 網路通訊特性。或在JavaScript中使用WebSockets或WebRTC實現你自己的網路通訊。 
7:WWW 或WebRequest類的使用 
WebGl支援WWW和unitywebrequest類。他們在JavaScript裡使用XMLHttpRequest類實現,使用瀏覽器來處理網路請求。這對存取跨域資源施加了一些安全限制。基本上對於伺服器的任何WWW請求不同於託管伺服器的是WebGL內容需要通過你試圖存取的伺服器授權。在WebGL的跨域存取WWW資源,您試圖存取的伺服器需要使用CORS授權。如果你使用WWW或unitywebreqest試圖存取內容,但是遠端伺服器沒有CORS系統設定或沒有正確設定,你會在瀏覽器控制檯看到類似這樣的錯誤: 
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://myserver.com/. This can be fixed by moving the resource to the same domain or enabling CORS. 
CORS表示跨域資源共用。基本上,伺服器需要向它發送的HTTP響應裡新增一些存取控制頭,這將告訴瀏覽器允許它存取伺服器上的內容。這是一個控制頭設定的例子,將允許unity WebGL存取來源於任何Web伺服器資源,通過常見的請求頭和使用HTTP GET,POST或OPTIONS方法: 
「Access-Control-Allow-Credentials」: 「true」, 
「Access-Control-Allow-Headers」: 「Accept, X-Access-Token, X-Application-Name, X-Request-Sent-Time」, 
「Access-Control-Allow-Methods」: 「GET, POST, OPTIONS」, 
「Access-Control-Allow-Origin」: 「*」,

注意,www.responseheaders限於實際響應檔頭的一個子集,根據7.1.1的CORS規範。還要注意XMLHttpRequest不允許使用數據流,因此WebGL的WWW類只會處理下載完成的數據(所以assestbundles不能像其他平臺上那樣解壓和載入)。 
8:爲什麼需要通訊 
當爲web構建內容時,您可能需要與web頁上的其他元素進行通訊。或者您可能希望使用Unity當前不預設的Unity API來實現功能。在這兩種情況下,您都需要直接與瀏覽器的JavaScript引擎對接。unity的WebGL提供不同的方法來實現這些。 
9:從Unity裡呼叫JavaScript裡的方法。 
你可以使用下面 下麪的程式碼從瀏覽器的JavaScript裡呼叫unity裡的方法: 
SendMessage (‘MyGameObject’, ‘MyFunction’, ‘foobar’); 
MyGameObject是場景內物體名稱,MyFunction方法名,foobar是參數 
下面 下麪是個例子:

<!--傳值方法-->
    <script type="text/javascript">
        //按鈕點選事件id爲test()
        function test() {
            //獲取ID名爲storeID的Value的值,賦值給sparm
            var sparm= document.getElementById("storeID").value;
            //傳參到U3D場景內GoName掛載指令碼的MyFunc函數,參數爲一個字串
            SendMessage("GoName", "MyFunc", sparm);
        }
    </script>

unity端範例

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
 
public class Test : MonoBehaviour {
    public Text WebText;
    public void MyFunc(string abc)
    {
        WebText.text = abc;  //在U3D的ugui的WebText上顯示傳值進來的字串
    }
}

10:如何通過SendMessage傳遞多個參數 
我們知道unity的SendMessage只接受一個字串參數,但是我們的專案經常會用到多參數,這樣就很尷尬了,關於這個,目前我還沒找到好的方式,只是看到大家都在使用連線多個string參數爲一個字串的形式,當然,這樣也只是能傳遞個string參數。

<!--傳值方法-->
    <script type="text/javascript">        
        function test() {          
            var Parm_1= "Parm_1";
            var Parm_2= "Parm_2";
            var Parm_3= "Parm_3";
            //傳參到U3D場景內GoName掛載指令碼的MyFunc函數,參數爲一個字串
            SendMessage("GoName","MyFunc",Parm_1+'-'+Parm_2+'-'+Parm_3);
        }
    </script>

unity裡的方法爲:

void MyFunc(string indata)
{
 
string[] words = indata.Split('-');
 
data1 = words[0];
moredata2 = words[1];
anotherpiece3 = words[2];
 
}

11:從Unity裡呼叫網頁裡的方法 
你可以使用Application.ExternalCall()和 Application.ExternalEval()函數呼叫嵌入網頁的名爲functionName的JavaScript函數,並傳遞給定的參數。支援原始的數據型別(string, int, float, char)和這些型別的數位。如何其他的物件被轉化爲字串(使用ToString方法)並作爲字串傳遞。這個函數呼叫時不會被阻塞,即ExternalCall立即返回的功能而不必等待被完成。傳遞的參數數量是可變的。

// 呼叫網頁上的MyFunction1並不使用參數。
Application.ExternalCall ("MyFunction1");
//呼叫網頁上的MyFunction2並使用字串參數。
Application.ExternalCall ("MyFunction2", "Hello World!");
//呼叫網頁上的MyFunction3並使用幾個不同類型的參數。
Application.ExternalCall ("MyFunction3", "str", 3, 5.0);

被呼叫的在HTML中的函數只需要使用標準的語法即可,例如:

<script type="text/javascript">
// 響應Unity的呼叫並接受"Hello World!" 做爲參數
function MyFunction2( arg )
    {
        alert( arg );
    }
</script>

Get方式:

  1. private IEnumerator SendUrl(string url)

  2. {

  3. using (UnityWebRequest www = UnityWebRequest.Get(url))

  4. {

  5. yield return www.Send();

  6. if (www.error != null)

  7. {

  8. Debug.Log(www.error);

  9. }

  10. else

  11. {

  12. if (www.responseCode == 200)//200表示接受成功

  13. {

  14. Debug.Log(www.downloadHandler.text);

  15. }

  16. }

  17. }

  18. }

Post方式:

  • public IEnumerator PostUrl(string url, string postData)

  • {

  • using (UnityWebRequest www = new UnityWebRequest(url,"POST"))

  • {

  • byte[] postBytes = System.Text.Encoding.UTF8.GetBytes(postData);

  • www.uploadHandler = (UploadHandler)new UploadHandlerRaw(postBytes);

  • www.downloadHandler = (DownloadHandler)new DownloadHandlerBuffer();

  • www.SetRequestHeader("Content-Type", "application/json");

  • yield return www.Send();

  • if (www.isError)

  • {

  • Debug.Log(www.error);

  • }

  • else

  • {

  • // Show results as text

  • if (www.responseCode == 200)

  • {

  • Debug.Log(www.downloadHandler.text);

  • }

  • }

  • }

  • }

  •