Lors du développement d'un jeu multijoueur avec PUN2, il y aura des cas où l'envoi de types personnalisés tels qu'une structure ou une classe définie par l'utilisateur via RPC et événement, etc. Cependant, l'envoi de ces types personnalisés directement au réseau n'est pas autorisé dans PUN2. Au lieu de cela, PUN2 fournit une méthode d'enregistrement de type personnalisée comme solution.
Pour utiliser la méthode d'enregistrement de type personnalisé de PUN2, un type personnalisé doit être sérialisé dans un tableau d'octets. Dans de nombreux cas, la sérialisation des octets est effectuée à l'aide du Binary Formatter. Cependant, Binary Formatter crée une quantité importante d'octets supplémentaires, ce qui nuira au trafic pour les jeux gourmands en réseau et donnera une mauvaise expérience aux utilisateurs mobiles qui ne peuvent pas utiliser la connexion WIFI gratuite. De plus, Binary Formatter ne peut pas sérialiser les Vector2, Vector3, Quaternion, etc. d'Unity.
Cette archive fournit une méthode d'enregistrement de type personnalisée avec un script et compare la différence avec d'autres méthodes de sérialisation.
La structure donnée sera sérialisée avec Binary Formatter et la taille sera mesurée par Marshal. Notez que les objets Unity tels que Vector3, Quaternion, etc. ne sont pas inclus car ils ne peuvent pas être sérialisés par 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 ,
} ;
La sérialisation de la structure et la mesure de sa taille sont les suivantes :
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 ) ) ;
}
Le résultat est le suivant :
taille (octets) | |
---|---|
Original | 24 octets |
Formateur binaire | 199 octets |
La taille théorique d'une structure donnée est de 24 octets. Lorsque la structure donnée est sérialisée avec Binary Formatter, la taille est de 199 octets, soit 8 fois plus grande que la taille théorique. Cela peut entraîner une surcharge de trafic lors de la sérialisation et de l'envoi au réseau.
La structure donnée ci-dessus sera sérialisée avec JsonUtility et convertie en octets. La sérialisation de la structure est la suivante :
public void JsonSerialize ( TestStruct testStruct )
{
byte [ ] bytes = Encoding . UTF8 . GetBytes ( JsonUtility . ToJson ( testStruct ) ) ;
Debug . Log ( string . Format ( " JsonUtility Serialized Size : {0} bytes " , bytes . Length ) ) ;
}
Le résultat est le suivant :
taille (octets) | |
---|---|
Original | 24 octets |
JsonUtility et conversion d'octets | 94 octets |
Lorsque la structure donnée est sérialisée avec JsonUtility et convertie en octets, la taille est de 94 octets, soit environ 4 fois la taille théorique. Cette taille peut être réduite en raccourcissant les noms des variables. Par exemple, lorsque les noms des variables sont modifiés comme indiqué ci-dessous, le résultat est le suivant.
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 ,
} ;
taille (octets) | |
---|---|
Original | 24 octets |
JsonUtility et conversion d'octets | 67 octets |
La taille des octets est réduite de 94 octets à 67 octets. Cependant, elle reste supérieure à la taille théorique de 24 octets.
Cette archive introduit un sérialiseur personnalisé qui peut sérialiser un type personnalisé tel qu'une structure ou une classe définie par l'utilisateur. Ce sérialiseur peut fournir une taille proche de la taille théorique. Les types et tailles sérialisables sont les suivants :
Taper | Taille (octets) |
---|---|
octet | 1 octet |
octet[] | 4 + (1 * Longueur) octets |
bouffon | 1 octet |
booléen[] | 4 + (1 * Longueur) octets |
int | 4 octets |
int[] | 4 + (4 * Longueur) octets |
flotter | 4 octets |
flotter[] | 4 + (4 * Longueur) octets |
Vecteur2 | 8 octets |
Vecteur2[] | 4 + (8 * Longueur) octets |
Vecteur3 | 12 octets |
Vecteur3[] | 4 + (12 * Longueur) octets |
Quaternion | 16 octets |
Quaternion[] | 4 + (16 * Longueur) octets |
chaîne | 4 + α (codage UTF8) octets |
chaîne[] | 4 + ((4 + α) * Longueur) octets |
Tout d’abord, déclarez en utilisant MSLIMA.Serializer ci-dessus.
using MSLIMA.Serializer;
Supposons alors que la structure soit donnée comme suit :
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 )
} ;
Tout d’abord, créez des méthodes statiques avec les noms « Serialize » et « Deserialize » à l’intérieur du type personnalisé . La méthode "Serialize" a un paramètre avec le type d'objet et un type de retour avec le type d'octet[]. La méthode "Deserialize" a un paramètre avec le type d'octet[] et le type de retour de l'objet.
Notez que les noms des méthodes, des paramètres et des types de retour doivent être les mêmes que ceux décrits. De plus, ces méthodes doivent être statiques.
public static byte [ ] Serialize ( object customObject )
{
}
public static object Deserialize ( byte [ ] bytes )
{
}
Deuxièmement, convertissez customObject en type personnalisé, déclarez un tableau d'octets dans la méthode "Serialize".
public static byte [ ] Serialize ( object customObject )
{
TestStruct o = ( TestStruct ) customObject ;
byte [ ] bytes = new byte [ 0 ] ;
}
Maintenant, utilisez la méthode de Serializer pour sérialiser les champs souhaités et renvoyer enfin les octets. Notez que le tableau d'octets est transmis avec le mot-clé 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 ;
}
Troisièmement, créez un nouveau type personnalisé dans la méthode "Deserialize" et déclarez la variable de décalage avec le type int et initialisez-la avec 0.
public static object Deserialize ( byte [ ] bytes )
{
TestStruct o = new TestStruct ( ) ;
int offset = 0 ;
}
Maintenant, utilisez la méthode de désérialisation de Serializer pour désérialiser les champs sérialisés ci-dessus. Le décalage est transmis avec le mot-clé ref et renvoie le type personnalisé créé ci-dessus.
Notez que l'ordre de désérialisation doit être le même que l'ordre de sérialisation.
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 ;
}
Enfin, le type personnalisé doit être enregistré dans PUN2. Appelez une fois la méthode décrite ci-dessous pour enregistrer le type personnalisé. Si l’enregistrement de plusieurs types personnalisés est nécessaire, le code d’octet doit être différent. Simplement réalisé en changeant l'alphabet du paramètre dans la méthode.
Serializer . RegisterCustomType < TestStruct > ( ( byte ) 'A' ) ;
Le résultat de la sérialisation d’une structure donnée est le suivant :
taille (octets) | |
---|---|
Original | 64 octets |
Sérialiseur personnalisé | 69 octets |
La taille théorique est de 64 octets alors que la taille réelle sérialisée est de 69 octets. La différence de 5 octets est causée par la chaîne, dont la taille peut être variable en fonction de la longueur. Le résultat est acceptable.
Le sérialiseur personnalisé fournit une taille plus petite que la sérialisation Binary Formatter ou JsonUtility. Cependant, il existe des limitations selon lesquelles il peut être gênant d'écrire toutes les méthodes de sérialisation pour chaque type personnalisé qui est censé être sérialisé et ne prend pas en charge les types imbriqués. Néanmoins, si vous sérialisez des types personnalisés simples avec des types primitifs et que vous les envoyez fréquemment au réseau, ce sérialiseur personnalisé serait utile.
Ajoutez une portée à ce registre.
"scopedRegistries" : [
{
"name" : " MS-LIMA " ,
"url" : " https://package.openupm.com " ,
"scopes" : [
" com.ms-lima "
]
}
]