При разработке многопользовательской игры с PUN2 будут случаи, когда отправка пользовательских типов, таких как определяемая пользователем структура или класс, через RPC и Event и т. д. Однако отправка этих пользовательских типов непосредственно в сеть не разрешена в PUN2. Вместо этого PUN2 предоставляет в качестве решения метод регистрации пользовательского типа.
Для использования метода регистрации пользовательского типа PUN2 пользовательский тип должен быть сериализован в массив байтов. Во многих случаях сериализация байтов выполняется с использованием Binary Formatter. Однако Binary Formatter создает значительное количество дополнительных байтов, что отрицательно влияет на трафик в играх с интенсивным использованием сети и ухудшает качество работы мобильных пользователей, которые не могут использовать бесплатное соединение WIFI. Кроме того, Binary Formatter не может сериализовать Unity Vector2, Vector3, Quaternion и т. д.
Этот архив предоставляет метод регистрации пользовательского типа с помощью сценария и сравнивает разницу с другими методами сериализации.
Данная структура будет сериализована с помощью Binary Formatter, а размер будет измерен Marshal. Обратите внимание, что такие объекты Unity, как Vector3, Quaternion и т. д., не включены, поскольку их нельзя сериализовать с помощью 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» внутри пользовательского типа . Метод «Сериализация» имеет один параметр с типом объекта и тип возвращаемого значения с типом byte[]. Метод «Десериализация» имеет один параметр с типом 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 ] ;
}
Теперь используйте метод сериализатора для сериализации нужных полей и, наконец, возврата байтов. Обратите внимание, что массив байтов передается с ключевым словом 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 ;
}
В-третьих, создайте новый пользовательский тип в методе «Десериализация», объявите переменную смещения с типом int и инициализируйте ее значением 0.
public static object Deserialize ( byte [ ] bytes )
{
TestStruct o = new TestStruct ( ) ;
int offset = 0 ;
}
Теперь используйте метод десериализации Serializer для десериализации полей, сериализованных выше. Смещение передается с ключевым словом 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 "
]
}
]