Al desarrollar un juego multijugador con PUN2, habrá casos en los que se envíen tipos personalizados, como una estructura o clase definida por el usuario, a través de RPC y eventos, etc. Sin embargo, enviar estos tipos personalizados directamente a la red no está permitido en PUN2. En cambio, PUN2 proporciona un método de registro de tipo personalizado como solución.
Para utilizar el método de registro de tipos personalizados de PUN2, se debe serializar un tipo personalizado en una matriz de bytes. En muchos casos, la serialización de bytes se realiza mediante Binary Formatter. Sin embargo, Binary Formatter crea una cantidad significativa de bytes adicionales, lo que afectará el tráfico de los juegos con uso intensivo de la red y brindará una mala experiencia a los usuarios de dispositivos móviles que no pueden usar una conexión WIFI gratuita. Además, Binary Formatter no puede serializar Vector2, Vector3, Quaternion, etc. de Unity.
Este archivo proporciona un método de registro de tipo personalizado con script y compara la diferencia con otros métodos de serialización.
La estructura dada se serializará con Binary Formatter y Marshal medirá el tamaño. Tenga en cuenta que los objetos de Unity como Vector3, Quaternion, etc. no están incluidos ya que Binary Formatter no puede serializarlos.
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 ,
} ;
La serialización de la estructura y la medición del tamaño de la misma son las siguientes:
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 ) ) ;
}
El resultado es el siguiente:
tamaño (bytes) | |
---|---|
Original | 24 bytes |
Formateador binario | 199 bytes |
El tamaño teórico de una estructura dada es de 24 bytes. Cuando la estructura dada se serializa con Binary Formatter, el tamaño es de 199 bytes, que es 8 veces mayor que el tamaño teórico. Esto puede generar una sobrecarga de tráfico al serializarlo y enviarlo a la red.
La estructura dada arriba se serializará con JsonUtility y se convertirá a bytes. La serialización de la estructura es la siguiente:
public void JsonSerialize ( TestStruct testStruct )
{
byte [ ] bytes = Encoding . UTF8 . GetBytes ( JsonUtility . ToJson ( testStruct ) ) ;
Debug . Log ( string . Format ( " JsonUtility Serialized Size : {0} bytes " , bytes . Length ) ) ;
}
El resultado es el siguiente:
tamaño (bytes) | |
---|---|
Original | 24 bytes |
JsonUtility y conversión de bytes | 94 bytes |
Cuando la estructura dada se serializa con JsonUtility y se convierte a bytes, el tamaño es de 94 bytes, aproximadamente 4 veces mayor que el tamaño teórico. Este tamaño se puede reducir acortando los nombres de las variables. Por ejemplo, cuando se cambian los nombres de las variables como se muestra a continuación, el resultado es el siguiente.
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 ,
} ;
tamaño (bytes) | |
---|---|
Original | 24 bytes |
JsonUtility y conversión de bytes | 67 bytes |
El tamaño de los bytes se reduce de 94 bytes a 67 bytes. Sin embargo, sigue siendo mayor que el tamaño teórico de 24 bytes.
Este archivo presenta un serializador personalizado que puede serializar tipos personalizados, como una estructura o clase definida por el usuario. Este serializador puede proporcionar un tamaño cercano al tamaño teórico. Los tipos y tamaños serializables son los siguientes:
Tipo | Tamaño (bytes) |
---|---|
byte | 1 byte |
byte[] | 4 + (1 * Longitud) bytes |
booleano | 1 byte |
booleano[] | 4 + (1 * Longitud) bytes |
entero | 4 bytes |
entero[] | 4 + (4 * Longitud) bytes |
flotar | 4 bytes |
flotar[] | 4 + (4 * Longitud) bytes |
Vector2 | 8 bytes |
Vector2[] | 4 + (8 * Longitud) bytes |
Vector3 | 12 bytes |
Vector3[] | 4 + (12 * Longitud) bytes |
Cuaternio | 16 bytes |
Cuaternio[] | 4 + (16 * Longitud) bytes |
cadena | 4 + α (codificación UTF8) bytes |
cadena[] | 4 + ((4 + α) * Longitud) bytes |
En primer lugar, declare el uso de MSLIMA.Serializer arriba.
using MSLIMA.Serializer;
Entonces, supongamos que la estructura está dada de la siguiente manera:
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 )
} ;
Primero, cree métodos estáticos con los nombres "Serialize" y "Deserialize" dentro del tipo personalizado . El método "Serializar" tiene un parámetro con el tipo de objeto y el tipo de retorno con el tipo de byte []. El método "Deserializar" tiene un parámetro con tipo de byte [] y tipo de retorno de objeto.
Tenga en cuenta que los nombres de los métodos, parámetros y tipos de retorno deben ser los mismos que se describen. Además, estos métodos deben ser estáticos.
public static byte [ ] Serialize ( object customObject )
{
}
public static object Deserialize ( byte [ ] bytes )
{
}
En segundo lugar, convierta customObject a un tipo personalizado y declare una matriz de bytes en el método "Serializar".
public static byte [ ] Serialize ( object customObject )
{
TestStruct o = ( TestStruct ) customObject ;
byte [ ] bytes = new byte [ 0 ] ;
}
Ahora, use el método de Serializer para serializar los campos deseados y devolver bytes por fin. Tenga en cuenta que la matriz de bytes se pasa con la palabra clave 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 ;
}
En tercer lugar, cree un nuevo tipo personalizado en el método "Deserializar" y declare la variable de compensación con el tipo int e inicialícela con 0.
public static object Deserialize ( byte [ ] bytes )
{
TestStruct o = new TestStruct ( ) ;
int offset = 0 ;
}
Ahora, use el método deserialize de Serializer para deserializar los campos que están serializados arriba. El desplazamiento se pasa con la palabra clave ref y devuelve el tipo personalizado creado anteriormente.
Tenga en cuenta que el orden de deserialización debe ser el mismo que el orden de serialización.
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 ;
}
Por último, el tipo personalizado debe registrarse en PUN2. Llame al método que se describe a continuación una vez para registrar el tipo personalizado. Si es necesario registrar varios tipos personalizados, el código de bytes debe ser diferente. Simplemente se logra cambiando el alfabeto del parámetro en el método.
Serializer . RegisterCustomType < TestStruct > ( ( byte ) 'A' ) ;
El resultado de la serialización de una estructura dada es el siguiente:
tamaño (bytes) | |
---|---|
Original | 64 bytes |
Serializador personalizado | 69 bytes |
El tamaño teórico es de 64 bytes, mientras que el tamaño serializado real es de 69 bytes. La diferencia de 5 bytes se debe a la cadena, cuyo tamaño puede variar según su longitud. El resultado es aceptable.
El serializador personalizado proporciona un tamaño más pequeño en lugar de la serialización Binary Formatter o JsonUtility. Sin embargo, existen limitaciones que pueden resultar inconvenientes al escribir todos los métodos de serialización para cada tipo personalizado que se supone debe ser serializado y que no admite tipos anidados. Sin embargo, si serializa tipos personalizados simples con tipos primitivos y los envía a la red con frecuencia, este serializador personalizado sería útil.
Agregue el alcance de este registro.
"scopedRegistries" : [
{
"name" : " MS-LIMA " ,
"url" : " https://package.openupm.com " ,
"scopes" : [
" com.ms-lima "
]
}
]