Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Aren't GameObjects suppose to be serializable?

Discussion in 'Scripting' started by Falcoshin, Aug 17, 2017.

  1. Falcoshin

    Falcoshin

    Joined:
    May 31, 2017
    Posts:
    168
    I've been told a few times that GameObjects and just about everything derivative of System.Object is suppose to be serializable by a BinaryFormatter object, but for some reason I always get the error "Type UnityEngine.GameObject is not marked as serializable". The same happens when I try to save individual components. Was I lied to about the BinaryFormatter being able to save GameObjects? More importantly, though, is there a way for me to save all the parts of my model (Components and all) without having to painstakingly save the properties as floats and strings so that I have to use a method to put it back together?
     
    Last edited: Aug 17, 2017
  2. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    There isn't much point serializing a GameObject. Its just a container that references a bunch of components. Nothing else.

    JSONUtility works well to serialize individual components. (In an ideal would this would be able to serialize entire sections of the hierarchy, but its currently not supported. Vote for it here.)
     
  3. Falcoshin

    Falcoshin

    Joined:
    May 31, 2017
    Posts:
    168
    Is this JSONUtility thing already built into Unity?
     
  4. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
  5. Falcoshin

    Falcoshin

    Joined:
    May 31, 2017
    Posts:
    168
    And it will allow me to serialize, let's say, an entire SkinnedMeshRenderer component? Mesh, Root Bone, Materials, and all?
     
  6. Falcoshin

    Falcoshin

    Joined:
    May 31, 2017
    Posts:
    168
    I just tried using JsonUtility.ToJson to try and serialize an animation component and it said "JsonUtility.ToJson does not support engine types". Does this mean I have to save all of the component's properties inside the Json manually? Because that doesn't seem like it'd be very secure.

    EDIT: Seems it won't even let me save a single animation clip either. What exactly is the point of this thing?
     
  7. Cherno

    Cherno

    Joined:
    Apr 7, 2013
    Posts:
    515
    Saving GameObject or even single components is a nightmare with Unity, it's just the way it is. Basically, the standard method (as far as I can tell, and the way UnitySerializer did it) is to have a Dictionary<string,object> for each member of a component, where the keys are the member names and the values are the member values. If the value is anoher class with multiple variables, it's treated as a sub-dictionary, and so on.


    I wrote a "simple" save & load system for Unity myself to teach me and other one way to do it. It's not perfect, but it is free and serves as a base for a user's own solution.

    https://forum.unity3d.com/threads/unity-save-load-utility-free-save-and-load-your-data.435506/
     
  8. Falcoshin

    Falcoshin

    Joined:
    May 31, 2017
    Posts:
    168
    Ok, so if I understand this right, the idea is to save the keys as oppose to the objects and then load the keys so that a method can find the objects they correspond to? I'm not really sure I understand this.
     
  9. Errorsatz

    Errorsatz

    Joined:
    Aug 8, 2012
    Posts:
    555
    Usually you don't save entire GameObjects. You save the model/state information, and then create or modify the appropriate GameObjects/components when you load it.

    For example, if you're saving the state of, say, a platformer level, you'd just save:
    * What was the base level.
    * For objects that can change (move, be destroyed, etc), what is their current state?
    * For enemies, what is their current state (location, health, etc)?
    * What is the player's current state (location, health, score, etc)?

    For something like a SkinnedMeshRenderer - why are you serializing it?
     
  10. Falcoshin

    Falcoshin

    Joined:
    May 31, 2017
    Posts:
    168
    Because it seemed like it'd be bad practice to have a bunch of strings and if statements inside a data container so that the model can be put back together from a base prefab later.

    I'll explain what I mean: I'm trying to create a character customization feature and need to save the model created, but this is the only solution I could think of so far that kind of works.

    Code (csharp):
    1.  
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5. using System;
    6.  
    7. [Serializable]
    8.  
    9. public class PlayerData : System.Object
    10. {
    11.     [SerializeField] string sex;
    12.     [SerializeField] string charClass;
    13.     [SerializeField] string hairModel;
    14.     [SerializeField] string beardModel;
    15.     [SerializeField] float[] hairColor = new float[3];
    16.     [SerializeField] float skinColor;
    17.     [SerializeField] float jawWidth;
    18.     [SerializeField] float chinWidth;
    19.     [SerializeField] float cheekWidth;
    20.     [SerializeField] float jawHeight;
    21.     [SerializeField] float chinLength;
    22.     [SerializeField] float eyeSize;
    23.     [SerializeField] float noseWidth;
    24.     [SerializeField] float noseLength;
    25.     [SerializeField] float earSize;
    26.  
    27.     //Accessors - SET
    28.  
    29.     public void setSex(string inSex)
    30.     {
    31.         sex = inSex;
    32.     }
    33.  
    34.     public void setClass(string inClass)
    35.     {
    36.         charClass = inClass;
    37.     }
    38.  
    39.     public void setHair(string inHair)
    40.     {
    41.         hairModel = inHair;
    42.     }
    43.  
    44.     public void setBeard(string inBeard)
    45.     {
    46.         beardModel = inBeard;
    47.     }
    48.  
    49.     public void setHairColor(Color inHairColor)
    50.     {
    51.         hairColor[0] = inHairColor.r;
    52.         hairColor[1] = inHairColor.g;
    53.         hairColor[2] = inHairColor.b;
    54.     }
    55.  
    56.     public void setSkinColor(float inSkinColor)
    57.     {
    58.         skinColor = inSkinColor;
    59.     }
    60.  
    61.     public void setJawWidth(float inJawWidth)
    62.     {
    63.         jawWidth = inJawWidth;
    64.     }
    65.  
    66.     public void setChinWidth(float inChinWidth)
    67.     {
    68.         chinWidth = inChinWidth;
    69.     }
    70.  
    71.     public void setCheekWidth(float inCheekWidth)
    72.     {
    73.         cheekWidth = inCheekWidth;
    74.     }
    75.  
    76.     public void setJawHeight(float inJawHeight)
    77.     {
    78.         jawHeight = inJawHeight;
    79.     }
    80.  
    81.     public void setChinLength(float inChinLength)
    82.     {
    83.         chinLength = inChinLength;
    84.     }
    85.  
    86.     public void setEyeSize(float inEyeSize)
    87.     {
    88.         eyeSize = inEyeSize;
    89.     }
    90.  
    91.     public void setNoseWidth(float inNoseWidth)
    92.     {
    93.         noseWidth = inNoseWidth;
    94.     }
    95.  
    96.     public void setNoseLength(float inNoseLength)
    97.     {
    98.         noseLength = inNoseLength;
    99.     }
    100.  
    101.     public void setEarSize(float inEarSize)
    102.     {
    103.         earSize = inEarSize;
    104.     }
    105.  
    106.     //Accessors - GET
    107.  
    108.     public GameObject assembleModel()
    109.     {
    110.         GameObject model = new GameObject();
    111.  
    112.         /*
    113.         Here I'd load up a base prefab in Resources and then modify it based on the above parameters. While it technically
    114.         works, it feels kinda... I don't know... "Noobish". It feels like there should be a more professional and flexible
    115.         way to do this.
    116.         */
    117.  
    118.         return model;
    119.     }
    120. }
    121.  
    This object would then be serialized in a data file, loaded back into a PlayerData object, and the assembleModel() method would be called to assign the model to whatever script it's being called from.
     
  11. Errorsatz

    Errorsatz

    Joined:
    Aug 8, 2012
    Posts:
    555
    That looks about right, IMO. Unless the players have the ability to modify the details of the character's hair beyond "make it style #5, red-brown", then there's no reason to save more info than that. Not only to keep the size down, but also so that you don't get unintended conflicts - for instance, you find out that the hair shader you were using performs badly on some devices, so you switch to a new one, but players' saved character data is still referencing the old one ... say hello to glitches.

    You can make this more flexible by using library assets (ScriptableObject works well) that reference the various prefabs by id, and then serialize that same id. So you'd have something like:
    Code (csharp):
    1. CharacterView char;
    2. char.Hair = HairLibrary[hairModel]
    If you want something more generalized, you could refer to properties by id this way too:
    Code (csharp):
    1. [Serializable]
    2. public class FaceProperty {
    3.    public string id;
    4.    public float value;
    5. }
    6. ...
    7. [SerializeField] List<FaceProperty> faceProperties;
    8. ...
    9. foreach(var prop in faceProperties)
    10.    char.Face[prop.id] = prop.value;
    11.  
     
    Last edited: Aug 18, 2017
  12. nyscersul

    nyscersul

    Joined:
    Oct 17, 2018
    Posts:
    136
    Probably a bit late on this one but couldnt you just serialise the mesh data if needed outside of the meshrenderer? Make a self serializing meshrenderer conponent that strips the options and the data and replaces it. Ive made self serializing objects which use the trigger of being deserilaized to rematerialise in the game world. The *container* you place the deserialised data into is your own choice, you simply place it in your own construct usually, why not send it straight to the gameobject instead?