跳到主要內容

精選

遊戲伺服器2025年初架設與研究part14收送封包的Json格式與Unity內建JsonUtility相關的問題(支援性)

 上次針對如何在Unity的新版TMP Text中使用中文字 而這次則是來分享一下先前有提過的 Json文字轉換 的問題 首先我們知道Unity內建的Json轉換是 JsonUtility JsonUtility 在使用上有一些限制在 算是有優點也有缺點 優點是畢竟是寫在Unity底層的, 效能 比較好 如果功能沒有問題的情況下,可以比額外安裝的Json轉換插件效能要好上不少 甚至有些插件能差兩倍以上的執行效能 不過自然也是有缺點 就是它有不少 不支援 的型別 很多時候常常讓伺服器與遊戲端的人打架的其中一個原因,伺服器表示資料明明就有送,遊戲端則是表示我沒收到(伺服器傳的Json格式轉不出來的種種問題或是可能性) 詳細可以參考Unity的官方文件 https://docs.unity3d.com/6000.1/Documentation/Manual/json-serialization.html 那我這邊也把一些重點特別拿出來講 首先我們先前在聊天訊息回傳的時候,就已經有用到基本的功能 可以先看到我們伺服器端的程式碼(Python) 如以上這語法 這是我們先前使用的方式 那這邊因為是跟先前相同的 相同的部分我就用截圖來顯示(詳細程式碼在Part11有可以複製) 可以看到我們主要回傳的結構 json.dumps({"type": "chat", "message": msg}) 這段就是把後面的內容透過Json轉換成Json字串後傳給使用者(遊戲端) 而我們Unity接收的位置是這樣寫的 ServerPacket packet = JsonUtility.FromJson<ServerPacket>(json); 這行就是我們今天 主要 想討論的地方 並且假設大家想要使用額外安裝的Json套件,也幾乎就是替換這行就能達到想要的效果 接下來我們看到ServerPacket這個class 這邊可以看到關鍵是我們的class要 可序列化 也就是說要加上 [Serializable] 那雖然說是只要加上可序列化,就能轉換 但是也是因為這樣,有很多不能被Unity內建支援的型別,則會轉換不出來 而...

遊戲伺服器2025年初架設與研究part12將聊天室內容顯示在Unity的Game視窗(遊戲視窗)

 那首先回顧一下上次的內容

上次我們已經簡單的建立了登入(無驗證)

並且讓聊天訊息可以發送出去了

那大家應該也有注意到

我們上次只是把訊息印在log

實際上遊戲畫面是什麼反應都沒有的

這次我們就是要來把遊戲畫面中的機制給簡單的處理一下


那首先我們先注意一下目前Game視窗的內容

假設也是有顯示No Cameras rendering

那就是先給他新增一個Camera,這樣這提示訊息就不會擋在這了

不過這邊如果大家建立場景的時候是選Basic而不是選空場景

那種情況預設就會有一台攝影機,所以不用做這個操作


我們在Hierarchy點右鍵,建立Camera
不過攝影機上的東西跟這次無關
單純只是讓先前的提示訊息不要顯示而已

接下來我們要來簡單的布置一下目前的UI配置
讓我們的聊天室內容可以顯示出來
在UI這邊建立空物件




先簡單的建立出這幾個物件
目前都是空物件,我們等等才會開始給物件加上功能
名字可以參考,也可以自己取



接下來我們先到專案內新增兩個C#腳本
分別是ChatMessage跟ChatMessageUnit


接下來我們先把這兩個腳本掛物件上

掛上去之後我們再開始寫功能

首先是我們的ChatMessage

using System.Collections.Generic;
using UnityEngine;

public class ChatMessage : MonoBehaviour
{

	//複製用的聊天訊息物件
	public ChatMessageUnit m_cloneObj = null;
	//複製的物件的父物件
	public Transform m_gridNode = null;


	public void RefreshChatUi(string addText)
	{
		ChatMessageUnit unit = Instantiate(m_cloneObj, m_gridNode);
		unit.RefreshData(addText);
	}
}



接下來是ChatMessageUnit

using TMPro;
using UnityEngine;

public class ChatMessageUnit : MonoBehaviour
{

	public TMP_Text chatText = null;

	public void RefreshData(string content)
	{
		chatText.text = content;
	}




}



主要在ChatMessage中準備了一個負責收到聊天訊息,並且透過收到的聊天訊息來創造物件
創造好物件後放置於指定的父物件底下,然後處理刷新
那ChatMessageUnit就是準備一個可以被刷新Text的Function

寫好把腳本存檔,我們回到Unity繼續後面的操作



首先我們先在Grid物件上新增一個腳本(Component)
這個腳本是Unity內建的,叫做Grid Layout Group
我們點選物件底下的Add Component
會出現搜尋的框,接下來輸入grid
通常如果專案內沒有其他grid腳本的話,會是第二個
選擇後就可以加入
這邊我是簡單的調整了一下物件具體的位置



並且在場景中大約在左下角的位置(相對於UI整體版面)


接下來我們到MessageUnit物件底下新增一個物件(畫面上是已經新增好的)
要新增的話如下圖的操作就可以創建

這樣我們就可以把這個Text給創建出來


接下來我們要把剛剛腳本宣告的物件給拉上來
MessageUnit下方的Script,把剛剛我們創建好的Text(TMP)給丟到ChatText的欄位


接下來我們回到上層,也就是MessageNode這層
把我們ChatMessage剛剛宣告的物件也一併拉上
主要就是CloneObj拉上我們的MessageUnit
而底下的GridNode拉上我們的MessageGrid物件

接下來為了測試
我們先把HideObj關起來
避免影響測試


先把物件旁邊的勾給取消,這樣可以暫時隱藏這個物件
接下來我們還要處理先前的Websocket腳本

using System;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using NativeWebSocket;

[Serializable]
public class ClientPacket
{
	public string type;
	public string username;
	public string message;
}

[Serializable]
public class ServerPacket
{
	public string type;
	public string message;
}

public class WebSocketClient : MonoBehaviour
{
	private WebSocket websocket;

	public ChatMessage chatMessage;

	// 設定伺服器位址
	[SerializeField]
	private string serverUrl = "ws://localhost:8765";

	private async void Start()
	{
		websocket = new WebSocket(serverUrl);

		websocket.OnOpen += OnOpen;
		websocket.OnError += OnError;
		websocket.OnClose += OnClose;
		websocket.OnMessage += OnMessage;

		await websocket.Connect();
	}

	private void Update()
	{
#if !UNITY_WEBGL || UNITY_EDITOR
		websocket?.DispatchMessageQueue();
#endif
	}

	private void OnApplicationQuit()
	{
		websocket?.Close();
	}

	private void OnOpen()
	{
		Debug.Log("WebSocket opened.");
	}

	private void OnError(string errorMsg)
	{
		Debug.LogError("WebSocket error: " + errorMsg);
	}

	private void OnClose(WebSocketCloseCode closeCode)
	{
		Debug.Log("WebSocket closed with code: " + closeCode);
	}

	private void OnMessage(byte[] bytes)
	{
		string json = Encoding.UTF8.GetString(bytes);
		try
		{
			ServerPacket packet = JsonUtility.FromJson<ServerPacket>(json);
			switch (packet.type)
			{
				case "welcome":
					Debug.Log(packet.message);
					break;
				case "chat":
					Debug.Log("Chat: " + packet.message);
					chatMessage.RefreshChatUi(packet.message);
					break;
				default:
					Debug.LogWarning($"Unknown packet type: {packet.type}");
					break;
			}
		}
		catch (Exception e)
		{
			Debug.LogError("Failed to parse server packet: " + e);
		}
	}

	public async Task SendLogin(string username)
	{
		ClientPacket packet = new ClientPacket
		{
			type = "login",
			username = username
		};
		string json = JsonUtility.ToJson(packet);
		await websocket.SendText(json);
	}

	public async Task SendChat(string message)
	{
		ClientPacket packet = new ClientPacket
		{
			type = "chat",
			message = message
		};
		string json = JsonUtility.ToJson(packet);
		await websocket.SendText(json);
	}
}



那我把有修改的部分進行了粗體跟加大顯示,並提供完整內容方便對照
主要就只有修改兩行


調整完之後,回到我們Unity場景中
把Manager掛上的腳本也拉上來
也就是把MessageNode給拉到ChatMessage的位置


那為了方便對位,我也給一張目前Game視窗的UI版面圖
左下角是Chat跟其輸入框
右上角是Login跟其輸入框

接下來我們就可以執行遊戲來測試了
先點擊上方Unity的Play
操作與上次相同
我們先登入,在右上方的登入框內輸入名稱
接下來在左下角的聊天框中輸入想要輸入的字
預設只有英文字體,不過我這邊是先使用了google的免費字體
所以讓這邊可以簡單地顯示出中文字了

如果繼續輸入的話,這邊字就會繼續往上


這樣子我們就可以簡單的把聊天室內容顯示在遊戲視窗上了
大家也可以以此為基礎新增更多功能
並且這也是伺服器回傳的資料進行顯示,我們也可以帶更多資料,來做更詳細的處理
同樣的這也表示目前就能多台遊戲端可以同步顯示聊天訊息在遊戲視窗上了
也可以自己複製一個專案,透過另一個Unity進行兩邊交叉測試
那這次就先分享到這邊,如有疑問也可以提出~







留言