Unity支持WebGL之前,想做頁游的話需要轉換成Flash平台發布,但是很麻煩中間會有很多東西需要特殊處理。或者不使用Flash那就需要Unity 的 WebPlayer,這是一個Web插件,運行之前要先在浏覽器上安裝此插件,才可以運行Unity程序。 BUT!!!現在Unity可以直接翻譯成JS在浏覽器上使用WebGL進行渲染,哈哈,Nice。解決了頁游插件問題。但是聯網呢?傳統的Socket方式肯定不行,浏覽器不會讓你用Socket進行傳輸的。然兒HTML5還有一個新特性就是Websocket,可以在浏覽器上建立Socket長連接了。怎麼實現呢?鄙人嘗試了很多方式,最後發現一個真正可行的方式,而且貌似只有這樣才可以,遂分享之~~~實踐
!!!DEMO全源碼:http://www.cnblogs.com/yinlong1991/p/5090289.html !!! 我先描述下大概的邏輯: Unity可以調用Web JS,然後JS裡面使用WebSocket連接服務器,Websocket通過SendMessage給Unity 發回收到的消息包。代碼如下:/*---------------- WebGl Platform ---------------------*/ [DllImport("__Internal")] private static extern void ConnectJS(string str); [DllImport("__Internal")] private static extern void SendMsgJS(byte[] data,int length); [DllImport("__Internal")] private static extern void CloseJS(); [DllImport("__Internal")] private static extern void AlertJS(string str); private void Connect_2(string str) { ConnectJS(str); } private void SendMsg_2(MessageBase msg) { byte[] data = SerializeMsg(msg); SendMsgJS(data,data.Length); } private void Close_2() { CloseJS(); } private void OnOpen_2() { OnOpen(); } private void OnMessage_2(string msg) { string []byteIntS = msg.Split(' '); byte[] data = new byte[byteIntS.Length]; for (int i = 0; i < byteIntS.Length; i++) { data[i] = (byte)int.Parse(byteIntS[i]); } OnMessage(data); } private void OnClose_2() { OnClose(); } /*--------------------- Serialze ------------------------*/ BinaryFormatter binFormat = new BinaryFormatter();//創建二進制序列化器 private byte[] SerializeMsg(MessageBase msg) { MemoryStream stream = new MemoryStream(); binFormat.Serialize(stream, msg); return stream.GetBuffer(); }Web端使用的jslib,此文件要放在Plugins目錄下,會被翻譯成JS代碼在浏覽器上運行。我的路徑是:Assets\Plugins\WebGL\WebsocketJS.jslib
var WebsocketJS = { $webSocket:{}, ConnectJS : function(url) { var s_url = Pointer_stringify(url); webSocket = new WebSocket(s_url); webSocket.onmessage = function (e) { if (e.data instanceof Blob) { var reader = new FileReader(); reader.addEventListener("loadend", function() { var array = new Uint8Array(reader.result); var msg = ""; for(var i = 0;i<array.length;i++) { if(i == array.length - 1) msg += array[i]; else msg += array[i]+" "; } SendMessage("WebSocket","OnMessage_2",msg); }); reader.readAsArrayBuffer(e.data); } else { alert("msg not a blob instance"); } }; webSocket.onopen = function(e) { SendMessage("WebSocket","OnOpen_2"); }; webSocket.onclose = function(e) { SendMessage("WebSocket","OnClose_2"); }; }, SendMsgJS: function (msg,length) { webSocket.send(HEAPU8.buffer.slice(msg, msg+length)); }, CloseJS: function () { webSocket.close(); }, AlertJS:function (msg) { var s_msg = Pointer_stringify(msg); alert(s_msg); } }; autoAddDeps(WebsocketJS, '$webSocket'); mergeInto(LibraryManager.library, WebsocketJS);注意jslib的寫法格式比較特殊,$webSocket:{}, 表示聲明一個全局變量,autoAddDeps(WebsocketJS,'$webSocket'); 這個是使用該變量?反正要Add進去。
這裡面所有的函數都是由Unity來調用的如上面的C#代碼:private static extern void ConnectJS(string str); 這樣的寫法,就可以調用JS方法了。 細節: var s_url =Pointer_stringify(url); 所有的str類型內容在這裡面要使用Pointer_stringify 函數轉化一下。 HEAPU8.buffer.slice(msg, msg+length) 所有byte[]類型的數據需要用HEAPU8.buffer.slice(byte[] data,int length)函數來轉化。 還有一些其他的數據類型轉化,暫時沒用到不詳細解釋,需要可也參考他人代碼:https://github.com/hecomi/UWO/blob/master/Assets/Plugins/WebSocket.jslib 丫的這是國外網站最流行的一段jslib代碼,但是我覺得不好!!! 重點要說一下 webSocket.onmessage 方法體的內容if (e.data instanceof Blob) { var reader = new FileReader(); reader.addEventListener("loadend", function() { var array = new Uint8Array(reader.result); var msg = ""; for(var i = 0;i<array.length;i++) { if(i == array.length - 1) msg += array[i]; else msg += array[i]+" "; } SendMessage("WebSocket","OnMessage_2",msg); }); reader.readAsArrayBuffer(e.data); }從代碼中可以看出來接收到的數據是Blob塊類型的,然後通過FileReader讀字節流,然後把字節流轉換成8位無符號整形數組 Uint8Array 然後放入到字符串裡以空格間隔,為什麼要這麼寫呢?,這樣豈不是大大的增加了數據傳輸量(PS:非網絡流量傳輸,只是JS到Unity之間)。原因是我要通過 SendMessage("WebSocket","OnMessage_2",msg); 函數吧數據串給Unity,我查了一大頓的文檔居然這貨不支持除了string類型的其他所有數據類型的傳輸,非文本類型的字符也不能傳輸比如'\0'字符就傳輸失敗。我擦嘞,那我只能用如上所述的暴力方式封裝String類型的字符串,然後到Unity中再去做解析了。這塊也是為什麼上面我說外國網站上用的那種jslib讀取網絡數據包不好的地方。
外國網站上的傳輸方式: C#代碼:[DllImport("__Internal")] private static extern void SocketRecv (int socketInstance, IntPtr ptr, int length); [DllImport("__Internal")] private static extern int SocketRecvLength (int socketInstance); public byte[] Recv() { int length = SocketRecvLength (m_NativeRef); if (length == 0) return null; byte[] buffer = new byte[length]; IntPtr unmanagedPointer = Marshal.AllocHGlobal(length); SocketRecv (m_NativeRef, unmanagedPointer, length); Marshal.Copy (unmanagedPointer, buffer, 0, length); Marshal.FreeHGlobal(unmanagedPointer); return buffer; }jslib代碼:
SocketSend: function (socketInstance, ptr, length) { var ptr = HEAPU32[ptr>>2]; var socket = webSocketInstances[socketInstance]; socket.socket.send (HEAPU8.buffer.slice(ptr, ptr+length)); }, SocketRecvLength: function(socketInstance) { var socket = webSocketInstances[socketInstance]; if (socket.messages.length == 0) return 0; return socket.messages[0].length; },我只說下他的大概運作機理,Unity調用JS連接服務器,服務器返回包之後JS先緩存起來,然後Unity以循環方式調用JS讀取接受到的數據的頭指針,然後根據長度讀取數據塊數據。這種loop方式無疑增大了數據報的延遲,並且使用起來也很不方便!
注意:我的那種方式也要在C#中緩沖包數據,SendMessage函數回調是在另一個線程中,不能改變主線程Transform等組件的值。 服務器不限制語言,只要是符合Websocket規范的就可以接受發送數據。C#的參見: https://github.com/sta/websocket-sharp !!!DEMO全源碼:http://www.cnblogs.com/yinlong1991/p/5090289.html !!!結束語
大家還有什麼不懂的地方盡管問我。 最後,祝大家新年快樂,2016 找到漂釀女盆友~