Search Unity

Vector3 is not marked serializable

Discussion in 'Scripting' started by yxliang01, Oct 8, 2016.

  1. yxliang01

    yxliang01

    Joined:
    Oct 8, 2016
    Posts:
    5
  2. takatok

    takatok

    Joined:
    Aug 18, 2016
    Posts:
    1,496
    This code works in 5.4.0f3
    Code (CSharp):
    1. public class GenericTest : MonoBehaviour {
    2.  
    3.     [SerializeField]
    4.     private Vector3 oneVector;
    5. }
    I am able to set oneVector's x,y,z components in the editor.
    Do you get some error 5.4.1 when you try to use SerializeField on a Vector3?
     
  3. yxliang01

    yxliang01

    Joined:
    Oct 8, 2016
    Posts:
    5
    Hello @takatok. Well, yes, you can do this. I think I didn't specify the question clearly. By that, what I meant is that it can't be serialized by BinaryFormatter. Thanks.
     
  4. jimroberts

    jimroberts

    Joined:
    Sep 4, 2014
    Posts:
    560
    You need to use the [System.Serializable] attribute on your class/struct in order for it to be serializable with BinaryFormatter.
     
  5. yxliang01

    yxliang01

    Joined:
    Oct 8, 2016
    Posts:
    5
    I do. But, when there is Vector3 as member, it will say Vector3 is not marked serializable.
     
  6. takatok

    takatok

    Joined:
    Aug 18, 2016
    Posts:
    1,496
    I understand. Its because the Unity Serialization of Vector3 and Quaternion for use with Editor is a different kind of Serialization that .NET does for the Binary Formatter. So the document is correct if a little unclear about this distinction.

    Using MSDN documention here I was able to create a way to Serialize Vector3:
    https://msdn.microsoft.com/en-us/li...erialization.surrogateselector(v=vs.110).aspx

    Stick this code in a Script somewhere it doesn't need to be attached to a GameObject:
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Runtime.Serialization;
    3. using System.Collections;
    4.  
    5. public class Vector3SerializationSurrogate : ISerializationSurrogate
    6. {
    7.  
    8.     // Method called to serialize a Vector3 object
    9.     public void GetObjectData(System.Object obj,SerializationInfo info, StreamingContext context)
    10.     {
    11.  
    12.         Vector3 v3 = (Vector3)obj;
    13.         info.AddValue("x", v3.x);
    14.         info.AddValue("y", v3.y);
    15.         info.AddValue("z", v3.z);
    16.     }
    17.  
    18.     // Method called to deserialize a Vector3 object
    19.     public System.Object SetObjectData(System.Object obj,SerializationInfo info,
    20.                                        StreamingContext context,ISurrogateSelector selector)
    21.     {
    22.  
    23.         Vector3 v3 = (Vector3)obj;
    24.         v3.x = (float)info.GetValue("x", typeof(float));
    25.         v3.y = (float)info.GetValue("y", typeof(float));
    26.         v3.z = (float)info.GetValue("z", typeof(float));
    27.         obj = v3;
    28.         return obj;
    29.     }
    30. }
    31.  
    Then this code should Serilalize/Deserialize Vector3:
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine.UI;
    5. using UnityEngine.EventSystems;
    6. using System.Runtime.Serialization.Formatters.Binary;
    7. using System.IO;
    8. using System.Runtime.Serialization;
    9.  
    10. public class GenericTest : MonoBehaviour {
    11.  
    12.     [System.Serializable]
    13.     public class SaveClass
    14.     {
    15.         private Vector3 oneVector = Vector3.zero;
    16.  
    17.         public Vector3 OneVector
    18.         {
    19.             get { return oneVector; }
    20.             set { oneVector = value; }
    21.         }
    22.     }
    23.  
    24.     void Start()
    25.     {
    26.         BinaryFormatter bf = new BinaryFormatter();
    27.         SurrogateSelector surrogateSelector = new SurrogateSelector();
    28.         Vector3SerializationSurrogate vector3SS = new Vector3SerializationSurrogate();
    29.  
    30.         surrogateSelector.AddSurrogate(typeof(Vector3),new StreamingContext(StreamingContextStates.All),vector3SS);
    31.         bf.SurrogateSelector = surrogateSelector;
    32.  
    33.         FileStream file = File.Create(Path.Combine(Application.dataPath, "SerializeTest"));
    34.         SaveClass saveClass = new SaveClass();
    35.         saveClass.OneVector = new Vector3(1f, 2f, 3f);
    36.  
    37.         bf.Serialize(file, saveClass);
    38.         file.Close();
    39.  
    40.         file = File.Open(Path.Combine(Application.dataPath, "SerializeTest"),FileMode.Open);
    41.         SaveClass loadClass = (SaveClass)bf.Deserialize(file);
    42.         Debug.Log(loadClass.OneVector);
    43.     }
    44. }
    Note: You can add multiple surrogates to the surrogate Selector. If you need to serialize Quaternions or other Unity Objects that aren't compatible with .Net Serialization. You just add them all before setting them to the binaryFormatter
     
  7. jimroberts

    jimroberts

    Joined:
    Sep 4, 2014
    Posts:
    560
    They definitely need to make structs [Serializable] in the UnityEngine.dll... I would also love to know why Quaternion, Vector2, Vector3, and Vector4 are mutable.
     
    CloudyVR and Kalladystine like this.
  8. yxliang01

    yxliang01

    Joined:
    Oct 8, 2016
    Posts:
    5
    Yeah. Thank you for your code, @takatok :). However, this makes the code hard to maintain... Hopefully, this is some better solution which can maintain the maintainability.
     
  9. takatok

    takatok

    Joined:
    Aug 18, 2016
    Posts:
    1,496
    Why would it make it harder to maintain? You write the surrogate code one time for all your projects. You could even create a static method returns a binary writer with the appropriate surrogates attached, that could also be rewritten one time and reused each time you wanted to write data using a binary writer. It would take a little bit of up front work, but in the long run all you'd be doing is replacing the default call to create a BinaryWriter with your own custom function.

    I'm fairly certain surrogates are the way .NET wants you to handle objects that the default binary writer doesn't handle.
     
  10. BrokenAngel

    BrokenAngel

    Joined:
    Mar 24, 2013
    Posts:
    92
    Can I ask that will surrogate ways work on mobile platform ? ( android and IOS )

    Thanks.
     
  11. takatok

    takatok

    Joined:
    Aug 18, 2016
    Posts:
    1,496
    I'm not 100% sure. I've never developted on android or IOS. Though the surrogate stuff is just a part of .Net Can always write a small script and see.
     
  12. nmbileg

    nmbileg

    Joined:
    Sep 29, 2017
    Posts:
    23
    Hi! Now I know how to serialize vectors using surrogate selector. But now I want save and serialize List<List<Vector2>>. Is there anyways to do that? Thanks!!!
     
  13. theagemaway

    theagemaway

    Joined:
    May 21, 2017
    Posts:
    7
    Just one more tidbit from someone who just found this solution and used it:

    I have a habit of following Visual Studio's cues of "this namespace isn't necessary" and so I deleted the "System." portion of all the "System.Object" references in @takatok's code.

    THESE ARE ACTUALLY EXTREMELY IMPORTANT. Without the "System.", It defualts to compiling as "UnityEngine.Object" instead of "System.Object", and I spent about 30 minutes needlessly trying to fix a compile error that arose from myself deleting necessary pieces of code.
     
  14. Nocktion

    Nocktion

    Joined:
    Sep 29, 2016
    Posts:
    9
    Or you could simply use this struct.
    Code (CSharp):
    1.  
    2.     [Serializable]
    3.     public struct Vector3S
    4.     {
    5.         public float x;
    6.         public float y;
    7.         public float z;
    8.  
    9.         public Vector3S(float x, float y, float z)
    10.         {
    11.             this.x = x;
    12.             this.y = y;
    13.             this.z = z;
    14.         }
    15.  
    16.         public override bool Equals(object obj)
    17.         {
    18.             if (!(obj is Vector3S))
    19.             {
    20.                 return false;
    21.             }
    22.  
    23.             var s = (Vector3S)obj;
    24.             return x == s.x &&
    25.                    y == s.y &&
    26.                    z == s.z;
    27.         }
    28.  
    29.         public override int GetHashCode()
    30.         {
    31.             var hashCode = 373119288;
    32.             hashCode = hashCode * -1521134295 + x.GetHashCode();
    33.             hashCode = hashCode * -1521134295 + y.GetHashCode();
    34.             hashCode = hashCode * -1521134295 + z.GetHashCode();
    35.             return hashCode;
    36.         }
    37.  
    38.         public Vector3 ToVector3()
    39.         {
    40.             return new Vector3(x, y, z);
    41.         }
    42.  
    43.         public static bool operator ==(Vector3S a, Vector3S b)
    44.         {
    45.             return a.x == b.x && a.y == b.y && a.z == b.z;
    46.         }
    47.  
    48.         public static bool operator !=(Vector3S a, Vector3S b)
    49.         {
    50.             return a.x != b.x && a.y != b.y && a.z != b.z;
    51.         }
    52.  
    53.         public static implicit operator Vector3(Vector3S x)
    54.         {
    55.             return new Vector3(x.x, x.y, x.z);
    56.         }
    57.  
    58.         public static implicit operator Vector3S(Vector3 x)
    59.         {
    60.             return new Vector3S(x.x, x.y, x.z);
    61.         }
    62.     }
     
    leo150, TachyonBlast and rkvieira like this.
  15. nyscersul

    nyscersul

    Joined:
    Oct 17, 2018
    Posts:
    136
    I think it would pay to seperate the the usage of the binary formatter from this idea, play with it a little first for loading and saving files, the binary formatter provides many useful methods for transferring data, i am currently attempting to use it to transfer serializable classes via udp packets - so you can have a save file sent by the server and reconstructed by the client, without a real limit on size assuming you build a robust catch packets->rebuild->request missing packets system. Or, it can just save your files. Ultimately, understanding the usage of byte arrays will make data communication easier in the long run. And you can simplify these code snippets by seperating the activity from the actual data construct... all you actually need is a class like this...

    [Serializable]
    public class SaveableVector3
    {
    public float x;
    public float y;
    public float z;
    }

    And then, you use it just like a vector 3, except it doesnt include the extension methods of course, you just do for example

    SaveableVector3 newData = new SaveableVector3();

    newData.x = transform.position.x;
    newData.x = transform.position.y;
    newData.x = transform.position.z;

    To set its data.

    Now the binary formatter part is more difficult, and i suggest looking at the tutorials that cover it for unity, altho i am referring to the serialization built into c# itself.

    Many simple scripts exist online for saving/loading, but the byte array you get during the process can be modified, split, or sent via the internet, or saved to disk. Quite versatile... My implementation splits the byte array into a number of packets which each packet is smaller than the maximum guaranteed unfragmented udp packet size, then sends them via udp and then reconstitutes it the other side.
     
  16. Nocktion

    Nocktion

    Joined:
    Sep 29, 2016
    Posts:
    9

    My solution is almost the same except it has a constructor new Vector3S(x, y, z) and it also allows you to implicitly convert a Vector3 to a Vector3S.
     
  17. nyscersul

    nyscersul

    Joined:
    Oct 17, 2018
    Posts:
    136
    Yes, it does that but it also shows much more code than is needed to show the actual important aspect - the recording of the saved values.

    No doubt in actual useage your code is superior, but for the matter of learning, i was simplifying it to the simplest components.

    I also missed the using statement for the serializable attribute, but no worries hehe.
     
  18. rkvieira

    rkvieira

    Joined:
    Apr 9, 2017
    Posts:
    2
    Nice one, thanks!

    Shouldn't the "IsDifferent method" have "ORs" instead of "ANDs"?
    It is different if any of these conditions happens.
    1. public static bool operator !=(Vector3S a, Vector3S b)
    2. {
    3. return (a.x != b.x) || (a.y != b.y) || (a.z != b.z);
    4. }
     
  19. Nocktion

    Nocktion

    Joined:
    Sep 29, 2016
    Posts:
    9
    Oh yeah sorry, this was my mistake, but you are right. ORs should be used instead of ANDs.
     
  20. palex-nx

    palex-nx

    Joined:
    Jul 23, 2018
    Posts:
    1,748
    I believe this construction should work without additional code.

    Code (CSharp):
    1.     [Serializable, StructLayout(LayoutKind.Explicit)]
    2.     unsafe struct Vector3Serializer {
    3.         [NonSerialized, FieldOffset(0)] public Vector3 vector;
    4.         [FieldOffset(0)] public fixed byte binaryVector[12];
    5.     }
    where magic number 12 is my wild gues for sizeof(Vector3) based on the fact Vector3 is 3 floats 4 bytes each. Probably, fixed size array might be replaced by another serializable type if you don't want to mess with unsafe/binary things.
     
    TomavX likes this.
  21. nyscersul

    nyscersul

    Joined:
    Oct 17, 2018
    Posts:
    136
    I use custom serializations now, which results in serilizations to a size which is often less than the total bytes used to record the same number of float variables, or whatever types.

    Using standard serialization is heavily bloated, just counting wasted white spaces alone.
     
  22. nyscersul

    nyscersul

    Joined:
    Oct 17, 2018
    Posts:
    136
  23. Nocktion

    Nocktion

    Joined:
    Sep 29, 2016
    Posts:
    9
    Another way is to use float3 from the Mathematics package. I think it's serializable and can be implicitly converted to Vector3s.
     
    Ducaticoder likes this.
  24. nyscersul

    nyscersul

    Joined:
    Oct 17, 2018
    Posts:
    136
    @Nocktion using the float3 version would result in being changed to vectir3 and back all the time unless you also used the math functions in the unity.mathematics package. Casting back and forth could be a slowdown if done too much.
     
  25. theangrybagelbox

    theangrybagelbox

    Joined:
    Nov 29, 2018
    Posts:
    1
    It sucks that vectors aren't serializable. When I encountered this problem I made a new class and an extension for Vector3

    Code (CSharp):
    1.     [System.Serializable]
    2.     public class SerializedVector
    3.     {
    4.         public float x;
    5.         public float y;
    6.         public float z;
    7.  
    8.         public Vector3 Convert()
    9.         {
    10.             return new Vector3(x, y, z);
    11.         }
    12.     }
    13.     public static SerializedVector ConvertToSerialized(this Vector3 vector)
    14.     {
    15.         return new SerializedVector() { x=vector.x, y=vector.y, z=vector.z};
    16.     }
    To convert a vector that I want to serialize and save, all it takes is something like
    Code (CSharp):
    1.  new Vector3(5, 5, 5).ConvertToSerialized();
    to serialize it, and when I want to deserialize it I use
    Code (CSharp):
    1. Vector3 vector = serializedVector.Convert()
     
  26. EMOTION-THEORY

    EMOTION-THEORY

    Joined:
    Jul 16, 2013
    Posts:
    83
    I know this thread is a little old, but hey, if I google for this issue and this is the first thing that comes up, then I think it's still relevant.
    This has been a problem for me too, and I can confirm the solution by takatok works.
    I too used to use a "serialized" class instead of the Vector class, as suggested by theangrybagelbox, but since they weren't quite interchangeable with Vector3's, it meant I had to use it everywhere INSTEAD of Vector3, and I wasn't a fan of that.

    Here's what I use that makes it simple and clean.

    Code (CSharp):
    1.     static private BinaryFormatter binaryFormatter;
    2.     static private BinaryFormatter BinaryFormatter
    3.     {
    4.         get
    5.         {
    6.             if (binaryFormatter == null)
    7.             {
    8.                 binaryFormatter = new BinaryFormatter();
    9.  
    10.                 var surrogateSelector = new SurrogateSelector();
    11.  
    12.                 { // Vector3
    13.                     Vector3SerializationSurrogate vector3SS = new Vector3SerializationSurrogate();
    14.                     surrogateSelector.AddSurrogate(
    15.                         typeof(Vector3),
    16.                         new StreamingContext(StreamingContextStates.All),
    17.                         vector3SS);
    18.                 }
    19.  
    20.                 { // Color
    21.                     ColorSerializationSurrogate colorSS = new ColorSerializationSurrogate();
    22.                     surrogateSelector.AddSurrogate(
    23.                         typeof(Color),
    24.                         new StreamingContext(StreamingContextStates.All),
    25.                         colorSS);
    26.                 }
    27.  
    28.                 binaryFormatter.SurrogateSelector = surrogateSelector;
    29.             }
    30.  
    31.             return binaryFormatter;
    32.         }
    33.     }
    Make sure to have the Vector3 surrogate class (I also mode one for Color, another Unity class that needs this), as originally posted by takatok:
     
  27. losingisfun

    losingisfun

    Joined:
    May 26, 2016
    Posts:
    36
    You're a god among men sir.