Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 12 Next capture
2021 2022 2023
1 capture
12 Jun 22 - 12 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
  • Help Room /
avatar image
1
Question by anp · Nov 11, 2017 at 01:40 PM · inheritanceinterfacedata storagepolymorphism

Help me to get rid of repetitive code

Good day! This is a data persistence task. I have some data I want to conserve across scenes, as well as between application executions. While I understand how to achieve this, I cannot resolve a problem that arises when I have multiple GameObjects that are essentially separate data containers. How would I go around with creating multiple classes with DIFFERENT class members while keeping THE SAME functionality without duplicating code? I will elaborate with some code.


 using System;
 using System.IO;
 using System.Runtime.Serialization.Formatters.Binary;
 using UnityEngine;
 
 public class PlayerData : MonoBehaviour
 {
     #region data
     public float fval;
     public string msg;
     #endregion
 
     public static PlayerData data;
 
     //always keep one instance on a gameobject across scenes
     void Awake()
     {
         if (data == null)
         {
             DontDestroyOnLoad(gameObject);
             data = this;
         }
         else if (data != null)
         {
             Destroy(gameObject);
         }
     }
 
     
     public void Save()
     {
         BinaryFormatter bf = new BinaryFormatter();
         FileStream fs = File.Create(Application.persistentDataPath + "info.dat");
 
         //initialize object for serialization
         _Data dataobj = new global::_Data();
 
         //fill object with data
         dataobj.fval = this.fval;
         dataobj.msg = this.msg;
         //write
         bf.Serialize(fs, dataobj);
     }
 
     public void Load()
     {
         BinaryFormatter bf = new BinaryFormatter();
         FileStream fs = File.Open(Application.persistentDataPath + "info.dat", FileMode.Open);
 
         //initialize object for deserialization
         _Data dataobj = new global::_Data();
 
         //read into object
         dataobj = (_Data)bf.Deserialize(fs);
 
         //populate class members
         this.fval = dataobj.fval;
         this.msg = dataobj.msg;
     }
 }
 
 [Serializable]
 class _Data
 {
     public float fval;
     public string msg;
 }



I had attempts on creating a base class and inheriting from it, or creating an interface, however I encountered some problems along the way. For example I am not sure how to bind the reference of the class to the base class, so the base class takes control of the gameobject persistence (awake() logic) across scenes. With the interface idea, i think could end up with the same problem and I am not entirely sure that I can have MonoBehaviour methods in it. I am probably missing something very simple here.

Comment
Add comment · Show 2
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Bonfire-Boy · Nov 11, 2017 at 02:23 PM 1
Share

I'm not quite following what the intentions are with your code (there's nothing suggesting what your hierarchy is going to look like - and the data fields in the Data class are repeated in the PlayerData), or what the issue is.

But with regard to this bit: " I am not sure how to bind the reference of the class to the base class, so the base class takes control of the gameobject persistence (awake() logic) across scenes", the simple answer to that is that the base class takes control of everything, except when a derived class overrides.

But it's probably useful to point out that when working with these kinds of things, one often uses factory-type classes or static methods to manage things like IO.

For example, you can save the details of an object just using polymorphism; since you're saving something, it's type is known. You'd get the derived type to first save its type identifier, and then its details. But when creating a new object based on that saved data, you can't because you don't know the type yet.

There, you might use a base class static loading function ins$$anonymous$$d (or a static method in a separate factory class), which can first load a type identifier and then have a switch on that, deter$$anonymous$$ing which of the derived types to create. Once the new object is created, you can use polymorphism to load the details.

avatar image anp Bonfire-Boy · Nov 12, 2017 at 09:26 AM 0
Share

Thanks for your reply. I should have clarified more on that earlier. The idea is to have separate "data containers" where I would like to keep data across all scenes and in between executions. In unity editor, it would just be a single gameobject that would never be destroyed, or duplicated (see awake() method). So one set of data would correspond to PlayerData. (health, speed, other attributes). Another set of data would be concerning an Enemy entity and etc. I would like to keep them separate as far as loading/saving is concerned. For example, in one scene I would just choose to load player data, and do nothing with enemy data, etc.

I could just make the same script for enemy data, put that on its own gameobject with its own member data, however, you could probably tell that some of the code would be repeated. Like the awake() method. I just wanted to know if there is a more elegant solution without code repetition. So maybe have a base class that does the gameobject handling (making sure its never duplicated or destroyed. Sort of like a singleton idea). and have other data classes (with their own unique sets of data) inherit from that base class behaviour.

Your reply is a bit heavy on the technical ter$$anonymous$$ology. I'll try to dissect this info bit by bit. It would be helpful to see some example code however.

as far as the _Data class goes, I just set it up for serialization, so its packed nicely with correct padding, etc. It is just a temporary holder. You can refer to this video to understand the data persistence strategy I am using: https://www.youtube.com/watch?v=J6FfcJpbPXE&t=4m10s

1 Reply

· Add your reply
  • Sort: 
avatar image
1
Best Answer

Answer by Bunny83 · Nov 12, 2017 at 02:56 PM

First of all using BinaryFormatter is not really a good choice. Yes it's often adviced to be a simple solution. However the binary format is very strict and very verbose. So it's rather large for what is actually stored and it will easily break when you change anything on your class (adding, removing or renaming fields).


However if you want to stick with it you can simply create a generic baseclass like this:

 public abstract class BinarySerializedMonoBehaviour<TSelf, TData> : MonoBehaviour where TSelf : BinarySerializedMonoBehaviour<TSelf, TData> where TData : class, new()
 {
     public static TSelf instance;

     public abstract string fileName { get; }
     protected abstract void OnSerializeData(TData dataobj);
     protected abstract void OnDeserializeData(TData dataobj);

     protected virtual void Awake()
     {
         if (instance == null)
         {
             DontDestroyOnLoad(gameObject);
             instance = this as TSelf;
         }
         else if (instance != null)
         {
             Destroy(gameObject);
         }
     }
     public void Save()
     {
         BinaryFormatter bf = new BinaryFormatter();
         using (FileStream fs = File.Create(Application.persistentDataPath + fileName))
         {

             TData dataobj = new TData();
             OnSerializeData(dataobj);
             bf.Serialize(fs, dataobj);
         }
     }

     public void Load()
     {
         BinaryFormatter bf = new BinaryFormatter();
         using (FileStream fs = File.Open(Application.persistentDataPath + fileName, FileMode.Open))
         {
             TData dataobj = (TData)bf.Deserialize(fs);
             OnDeserializeData(dataobj);
         }
     }
 }


Every of your singleton classes that should be saved simply derive from this class like this:


 public class TestClass : BinarySerializedMonoBehaviour<TestClass, _TestClassData>
 {
     public override string fileName { get { return "TestClass.info"; } }

     public int someIntValue;
     public string someStr;

     protected override void OnSerializeData(_TestClassData data)
     {
         data.someIntValue = someIntValue;
         data.someStr = someStr;
     }
     protected override void OnDeserializeData(_TestClassData data)
     {
         someIntValue = data.someIntValue;
         someStr = data.someStr;
     }
 }

 public class _TestClassData
 {
     public int someIntValue;
     public string someStr;
 }


So all you have to do for each class is the class specific stuff. So the specific data class that should be serialized and the copying of the data from / to that class as well as the filename that should be used.


Though if you want to have multiple "saves" you might want to add an additional path parameter to the Save and Load methods.

In the end there wasn't much of "repetitive code".

Comment
Add comment · Show 2 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image anp · Nov 12, 2017 at 03:09 PM 0
Share

Thanks for taking your time. Very clear and exactly what I was looking for.

avatar image anp · Nov 12, 2017 at 03:18 PM 0
Share

Just for a future reference, you have said: "First of all using BinaryFormatter is not really a good choice". What is a better choice? I want to read up on that.

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

74 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

Inheritance Question 1 Answer

Serialize Lists with multiple inherited classes or interfaces ? 1 Answer

[SOLVED] JSON Serialization of Derived Classes 2 Answers

Polymorphism on member attribute 1 Answer

Can't instantiate new class C# 0 Answers


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges