Ao desenvolver jogos multijogador com PUN2, haverá casos em que o envio de tipos personalizados, como estrutura ou classe definida pelo usuário, por meio de RPC e Evento, etc. No entanto, o envio desses tipos personalizados diretamente para a rede não é permitido em PUN2. Em vez disso, o PUN2 fornece um método de registro de tipo personalizado como solução.
Para usar o método de registro de tipo personalizado do PUN2, um tipo personalizado deve ser serializado em uma matriz de bytes. Em muitos casos, a serialização de bytes é realizada usando o Binary Formatter. No entanto, o Binary Formatter cria uma quantidade significativa de bytes adicionais, o que prejudicará o tráfego de jogos com uso intensivo de rede e proporcionará uma experiência ruim para usuários móveis que não podem usar a conexão Wi-Fi gratuita. Além disso, o Binary Formatter não pode serializar Vector2, Vector3, Quaternion e etc.
Este arquivo fornece um método de registro de tipo personalizado com script e compara a diferença com outros métodos de serialização.
A estrutura fornecida será serializada com o Binary Formatter e o tamanho será medido pelo Marshal. Observe que objetos Unity como Vector3, Quaternion e etc não estão incluídos, pois não podem ser serializados pelo Formatador Binário.
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 ,
} ;
A serialização da estrutura e a medição do tamanho dela são as seguintes:
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 ) ) ;
}
O resultado é o seguinte:
tamanho (bytes) | |
---|---|
Original | 24 bytes |
Formatador binário | 199 bytes |
O tamanho teórico de determinada estrutura é de 24 bytes. Quando a estrutura fornecida é serializada com o Formatador Binário, o tamanho é 199 bytes, 8 vezes maior que o tamanho teórico. Isso pode causar sobrecarga de tráfego ao serializá-lo e enviá-lo para a rede.
A estrutura fornecida acima será serializada com JsonUtility e convertida em bytes. A serialização da estrutura é a seguinte:
public void JsonSerialize ( TestStruct testStruct )
{
byte [ ] bytes = Encoding . UTF8 . GetBytes ( JsonUtility . ToJson ( testStruct ) ) ;
Debug . Log ( string . Format ( " JsonUtility Serialized Size : {0} bytes " , bytes . Length ) ) ;
}
O resultado é o seguinte:
tamanho (bytes) | |
---|---|
Original | 24 bytes |
JsonUtility e conversão de bytes | 94 bytes |
Quando a estrutura fornecida é serializada com JsonUtility e convertida em bytes, o tamanho é 94 bytes, cerca de 4 vezes maior que o tamanho teórico. Este tamanho pode ser reduzido encurtando os nomes das variáveis. Por exemplo, quando os nomes das variáveis são alterados conforme mostrado abaixo, o resultado é o seguinte.
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 ,
} ;
tamanho (bytes) | |
---|---|
Original | 24 bytes |
JsonUtility e conversão de bytes | 67 bytes |
O tamanho dos bytes foi reduzido de 94 bytes para 67 bytes. No entanto, ainda é maior que o tamanho teórico de 24 bytes.
Este arquivo apresenta um serializador personalizado que pode serializar tipos personalizados, como estrutura ou classe definida pelo usuário. Este serializador pode fornecer um tamanho próximo ao tamanho teórico. Os tipos e tamanhos serializáveis são os seguintes:
Tipo | Tamanho (bytes) |
---|---|
byte | 1 byte |
byte[] | 4 + (1 * Comprimento) bytes |
bool | 1 byte |
bool[] | 4 + (1 * Comprimento) bytes |
interno | 4 bytes |
interno[] | 4 + (4 * Comprimento) bytes |
flutuador | 4 bytes |
flutuador[] | 4 + (4 * Comprimento) bytes |
Vetor2 | 8 bytes |
Vetor2[] | 4 + (8 * Comprimento) bytes |
Vetor3 | 12 bytes |
Vetor3[] | 4 + (12 * Comprimento) bytes |
Quatérnio | 16 bytes |
Quatérnio[] | 4 + (16 * Comprimento) bytes |
corda | 4 + α (codificação UTF8) bytes |
corda[] | 4 + ((4 + α) * Comprimento) bytes |
Primeiro de tudo, declare usando MSLIMA.Serializer acima.
using MSLIMA.Serializer;
Então, suponha que a estrutura seja dada da seguinte forma:
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 )
} ;
Primeiro, crie métodos estáticos com os nomes "Serialize" e "Deserialize" dentro do tipo personalizado . O método "Serialize" possui um parâmetro com tipo de objeto e tipo de retorno com tipo de byte[]. O método "Deserialize" possui um parâmetro com tipo de byte[] e tipo de retorno de objeto.
Observe que os nomes dos métodos, parâmetros e tipos de retorno devem ser os mesmos descritos. Além disso, esses métodos devem ser estáticos.
public static byte [ ] Serialize ( object customObject )
{
}
public static object Deserialize ( byte [ ] bytes )
{
}
Em segundo lugar, converta customObject para o tipo personalizado e declare a matriz de bytes no método "Serialize".
public static byte [ ] Serialize ( object customObject )
{
TestStruct o = ( TestStruct ) customObject ;
byte [ ] bytes = new byte [ 0 ] ;
}
Agora, use o método do Serializer para serializar os campos desejados e finalmente retornar os bytes. Observe que a matriz de bytes é passada com a palavra-chave 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 ;
}
Terceiro, crie um novo tipo personalizado no método "Deserialize" e declare a variável offset com o tipo int e inicialize-a com 0.
public static object Deserialize ( byte [ ] bytes )
{
TestStruct o = new TestStruct ( ) ;
int offset = 0 ;
}
Agora, use o método desserializar do Serializer para desserializar os campos serializados acima. O deslocamento é passado com a palavra-chave ref e retorna o tipo personalizado criado acima.
Observe que a ordem de desserialização deve ser igual à ordem de serialização.
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, o tipo personalizado deve ser registrado em PUN2. Chame o método descrito abaixo uma vez para registrar o tipo personalizado. Se for necessário registrar vários tipos personalizados, o código de bytes deverá ser diferente. Simplesmente conseguido alterando o alfabeto do parâmetro no método.
Serializer . RegisterCustomType < TestStruct > ( ( byte ) 'A' ) ;
O resultado da serialização de determinada estrutura é o seguinte:
tamanho (bytes) | |
---|---|
Original | 64 bytes |
serializador personalizado | 69 bytes |
O tamanho teórico é de 64 bytes, enquanto o tamanho serializado real é de 69 bytes. A diferença de 5 bytes é causada pela string, que pode ser dimensionada de forma variável de acordo com o comprimento. O resultado é aceitável.
O serializador personalizado fornece um tamanho menor em vez da serialização do Formatador Binário ou do JsonUtility. No entanto, existem limitações que podem ser inconvenientes para conectar todos os métodos de serialização para todos os tipos personalizados que devem ser serializados e não suportam tipos aninhados. No entanto, se serializar tipos personalizados simples com tipos primitivos e enviá-los para a rede com frequência, esse serializador personalizado ajudaria.
Adicione o escopo deste registro.
"scopedRegistries" : [
{
"name" : " MS-LIMA " ,
"url" : " https://package.openupm.com " ,
"scopes" : [
" com.ms-lima "
]
}
]