程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> Unity使用Websocket實現WebGL在線游戲(無插件),websocketwebgl

Unity使用Websocket實現WebGL在線游戲(無插件),websocketwebgl

編輯:C#入門知識

Unity使用Websocket實現WebGL在線游戲(無插件),websocketwebgl


開篇  
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 找到漂釀女盆友~
    

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved