在使用PUN2開發多人遊戲時,會存在透過RPC和Event等發送自訂類型(例如使用者定義的結構或類別)的情況。相反,PUN2 提供了自訂類型註冊方法作為解決方案。
為了使用PUN2的自訂類型註冊方法,自訂類型必須序列化為位元組數組。在許多情況下,位元組序列化是使用二進位格式化程式執行的。然而,Binary Formatter 會產生大量額外字節,這會損害網路密集型遊戲的流量,並給無法使用免費 WIFI 連線的行動用戶帶來較差的體驗。另外Binary Formatter無法序列化Unity的Vector2、Vector3、Quaternion等。
此存檔提供了透過腳本自訂類型註冊方法,並比較了與其他序列化方法的差異。
給定的結構將使用二進位格式化程式進行序列化,並且大小將由 Marshal 進行測量。請注意,不包括 Vector3、四元數等 Unity 對象,因為這些物件無法透過 Binary Formatter 進行序列化。
public struct TestStruct
{
public int int_1 ;
public int int_2 ;
public string string_1 ;
public float float_1 ;
public float float_2 ;
}
TestStruct testStruct = new TestStruct
{
int_1 = 30 ,
int_2 = 71 ,
string_1 = " ABC가나다 " ,
float_1 = 0.162f ,
float_2 = 62f ,
} ;
其結構及測量尺寸系列化如下:
public void BinaryFormatterSerialize ( TestStruct testStruct )
{
byte [ ] bytes ;
MemoryStream memoryStream = new MemoryStream ( ) ;
BinaryFormatter binaryFormatter = new BinaryFormatter ( ) ;
binaryFormatter . Serialize ( memoryStream , testStruct ) ;
memoryStream . Close ( ) ;
bytes = memoryStream . ToArray ( ) ;
Debug . Log ( string . Format ( " Bianary Formatter Serialized Size : {0} bytes " , bytes . Length ) ) ;
}
public void CheckSize ( TestStruct testStruct )
{
Debug . Log ( string . Format ( " Original Size : {0} bytes " , Marshal . SizeOf ( testStruct ) ) ) ;
Debug . Log ( JsonUtility . ToJson ( testStruct , true ) ) ;
}
結果如下:
大小(位元組) | |
---|---|
原來的 | 24字節 |
二進位格式化程序 | 199 位元組 |
給定結構的理論大小為 24 位元組。當使用 Binary Formatter 序列化給定結構時,大小為 199 位元組,比理論大小大 8 倍。當序列化並將其發送到網路時,這可能會導致流量開銷。
上面給定的結構將使用 JsonUtility 序列化並轉換為位元組。序列化結構如下:
public void JsonSerialize ( TestStruct testStruct )
{
byte [ ] bytes = Encoding . UTF8 . GetBytes ( JsonUtility . ToJson ( testStruct ) ) ;
Debug . Log ( string . Format ( " JsonUtility Serialized Size : {0} bytes " , bytes . Length ) ) ;
}
結果如下:
大小(位元組) | |
---|---|
原來的 | 24字節 |
JsonUtility & 位元組轉換 | 94字節 |
當給定的結構體以 JsonUtility 序列化並轉換為位元組時,大小為 94 位元組,大約是理論大小的 4 倍。可以透過縮短變數名稱來減少此大小。例如,當如下所示更改變數名稱時,結果如下。
public struct TestStruct
{
public int a ;
public int b ;
public string c ;
public float d ;
public float e ;
}
TestStruct testStruct = new TestStruct
{
a = 30 ,
b = 71 ,
c = " ABC가나다 " ,
d = 0.162f ,
e = 62f ,
} ;
大小(位元組) | |
---|---|
原來的 | 24字節 |
JsonUtility & 位元組轉換 | 67字節 |
位元組大小從 94 位元組減少到 67 位元組。但是,它仍然大於理論大小 24 個位元組。
此檔案引入了自訂序列化器,它可以序列化自訂類型,例如使用者定義的結構或類別。此串列器可以提供接近理論大小的大小。可序列化的類型和大小如下:
類型 | 大小(位元組) |
---|---|
位元組 | 1位元組 |
位元組[] | 4 + (1 * 長度) 位元組 |
布林值 | 1位元組 |
布爾[] | 4 + (1 * 長度) 位元組 |
整數 | 4位元組 |
整數[] | 4 + (4 * 長度) 位元組 |
漂浮 | 4位元組 |
漂浮[] | 4 + (4 * 長度) 位元組 |
向量2 | 8位元組 |
向量2[] | 4 + (8 * 長度) 位元組 |
向量3 | 12位元組 |
向量3[] | 4 + (12 * 長度) 位元組 |
四元數 | 16字節 |
四元數[] | 4 + (16 * 長度) 位元組 |
細繩 | 4 + α(UTF8 編碼)位元組 |
細繩[] | 4 + ((4 + α) * 長度) 位元組 |
首先,聲明使用上面的MSLIMA.Serializer。
using MSLIMA.Serializer;
然後,假設結構如下:
public struct TestStruct
{
public int int_1 ;
public int int_2 ;
public float float_1 ;
public bool bool_1 ;
public string string_1 ;
public Vector3 vector3_1 ;
public Vector3 vector3_2 ;
public Quaternion quaternion_1 ;
}
TestStruct testStruct = new TestStruct
{
int_1 = 30 ,
int_2 = 71 ,
float_1 = 0.162f ,
bool_1 = true ,
string_1 = " ABC가나다 " ,
vector3_1 = new Vector3 ( - 23f , 62f , 26f ) ,
vector3_2 = new Vector3 ( 1f , 7f , - 15f ) ,
quaternion_1 = Quaternion . Euler ( 35f , 0f , 15f )
} ;
首先,在自訂類型內部建立名稱為「Serialize」和「Deserialize」的靜態方法。 「Serialize」方法有一個參數,類型為object,回傳類型為byte[]。 “Deserialize”方法有一個參數,類型為byte[],並傳回類型為物件。
請注意,方法名稱、參數和傳回類型必須與描述的相同。此外,這些方法必須是靜態的。
public static byte [ ] Serialize ( object customObject )
{
}
public static object Deserialize ( byte [ ] bytes )
{
}
其次,將 customObject 轉換為自訂類型,在「Serialize」方法中宣告位元組數組。
public static byte [ ] Serialize ( object customObject )
{
TestStruct o = ( TestStruct ) customObject ;
byte [ ] bytes = new byte [ 0 ] ;
}
現在,使用 Serializer 的方法序列化所需的欄位並最終返回位元組。請注意,位元組數組是透過 ref 關鍵字傳遞的。
public static byte [ ] Serialize ( object customObject )
{
.. .
Serializer . Serialize ( o . int_1 , ref bytes ) ;
Serializer . Serialize ( o . int_2 , ref bytes ) ;
Serializer . Serialize ( o . float_1 , ref bytes ) ;
Serializer . Serialize ( o . bool_1 , ref bytes ) ;
Serializer . Serialize ( o . string_1 , ref bytes ) ;
Serializer . Serialize ( o . vector3_1 , ref bytes ) ;
Serializer . Serialize ( o . vector3_2 , ref bytes ) ;
Serializer . Serialize ( o . quaternion_1 , ref bytes ) ;
return bytes ;
}
第三,在「Deserialize」方法中建立新的自訂類型,並宣告 int 類型的偏移量變量,並將其初始化為 0。
public static object Deserialize ( byte [ ] bytes )
{
TestStruct o = new TestStruct ( ) ;
int offset = 0 ;
}
現在,使用 Serializer 的 deserialize 方法來反序列化上面序列化的欄位。偏移量透過 ref 關鍵字傳遞並傳回上面建立的自訂類型。
請注意,反序列化的順序必須與序列化的順序相同。
public static object Deserialize ( byte [ ] bytes )
{
.. .
o . int_1 = Serializer . DeserializeInt ( bytes , ref offset ) ;
o . int_2 = Serializer . DeserializeInt ( bytes , ref offset ) ;
o . float_1 = Serializer . DeserializeInt ( bytes , ref offset ) ;
o . bool_1 = Serializer . DeserializeBool ( bytes , ref offset ) ;
o . string_1 = Serializer . DeserializeString ( bytes , ref offset ) ;
o . vector3_1 = Serializer . DeserializeVector3 ( bytes , ref offset ) ;
o . vector3_2 = Serializer . DeserializeVector3 ( bytes , ref offset ) ;
o . quaternion_1 = Serializer . DeserializeQuaternion ( bytes , ref offset ) ;
return o ;
}
最後,自訂類型應註冊到 PUN2。呼叫一次下面描述的方法來註冊自訂類型。如果需要註冊多個自訂類型,則字節碼必須不同。只需更改方法中參數的字母即可實現。
Serializer . RegisterCustomType < TestStruct > ( ( byte ) 'A' ) ;
給定結構序列化的結果如下:
大小(位元組) | |
---|---|
原來的 | 64字節 |
自訂序列化器 | 69字節 |
理論大小為 64 字節,實際序列化大小為 69 位元組。 5 個位元組的差異是由字串引起的,字串的大小可以根據長度而變化。結果是可以接受的。
自訂序列化程式提供的大小比 Binary Formatter 或 JsonUtility 序列化更小。但是,存在一些限制,即為每個應該序列化且不支援巢狀類型的自訂類型編寫所有序列化方法可能會很不方便。儘管如此,如果用原始類型序列化簡單的自訂類型並經常將其發送到網絡,則此自訂序列化程序會有所幫助。
新增此註冊表的範圍。
"scopedRegistries" : [
{
"name" : " MS-LIMA " ,
"url" : " https://package.openupm.com " ,
"scopes" : [
" com.ms-lima "
]
}
]