How to break reference from .asset classes and runtime copied values?
Hello, I have an issue with the .assets files I created through a editor extension I made myself. Basically I create in a base class, a List containing my objects. These are complex objects containing other variables that get their value from other .asset files (in the editor), everything is serialized of course. Everything works fine, I can perform all the operations both in the editor and in game. The problem is that even if when I start the game I copy the values of the base List in an array with CopyTo, any change I make to any variable in the object is saved to the .asset file. Moreover, if I add the same object multiple times in the array and modify one of them, every object that uses the same variable changes accordingly. What I would like to do is: 1) to break the link between instantiated objects in game and the .asset database 2)break the link between objects that use the same value for a variable (coming from another .asset file), so that if I modify a variable in one object the changes do not propagate to the other objects.
I am afraid I was not clear enough but any help is appreciated.
Answer by Adam-Mechtley · Oct 27, 2016 at 07:13 AM
Hi @asters! If I understand correctly, you need to:
Assuming your .asset is a ScriptableObject of some kind, you need to call Instantiate() when your game starts to create a duplicate. Make any run-time modifications on the duplicate rather than on the source asset.
You need to make sure that when you assign the same item to multiple different list indices that it is a copy. If the list element is a value type (e.g., float, int, struct) this is automatic. If it is a reference type (e.g., any class object) then you need to make sure you are assigning a copy. If the elements are a subclass of UnityEngine.Object, do the same as step 1 for each duplicated element. If they are a custom class of some kind, then you need to do something like make them ICloneable or otherwise copy any necessary data to a new instance.
Answer by Zankaster · Oct 27, 2016 at 06:13 PM
Thank you, I was able to solve my problem and I learned something in the process while doing so, thank to your explanation. Basically what I needed was a so called "Deep Copy" of my main Class. You can do it as you said, making the classes ICloneable and implementing a symple Clone() function for them to make a copy without reference of the variables, so with a main function that did that starting from the outer class and going down to all referenced objects. I used a more lazy but quite effective approach, using this helper function: using System; using System.IO; using System.Runtime.Serialization; using System.Runtime.Serialization.Formatters.Binary;
/// <span class="code-SummaryComment"><summary></span>
/// Provides a method for performing a deep copy of an object.
/// Binary Serialization is used to perform the copy.
/// <span class="code-SummaryComment"></summary></span>
public static class ObjectCopier
{
/// <span class="code-SummaryComment"><summary></span>
/// Perform a deep Copy of the object.
/// <span class="code-SummaryComment"></summary></span>
/// <span class="code-SummaryComment"><typeparam name="T">The type of object being copied.</typeparam></span>
/// <span class="code-SummaryComment"><param name="source">The object instance to copy.</param></span>
/// <span class="code-SummaryComment"><returns>The copied object.</returns></span>
public static T Clone<T>(this T source)
{
if (!typeof(T).IsSerializable)
{
throw new ArgumentException("The type must be serializable.", "source");
}
// Don't serialize a null object, simply return the default for that object
if (Object.ReferenceEquals(source, null))
{
return default(T);
}
IFormatter formatter = new BinaryFormatter();
Stream stream = new MemoryStream();
using (stream)
{
formatter.Serialize(stream, source);
stream.Seek(0, SeekOrigin.Begin);
return (T)formatter.Deserialize(stream);
}
}
}
It serializes your Object (Class, List, etc.) and deserializes it into a new Object, so you get a fresh copy of everything, free of any reference. The only drawback is that every class must be tagged serializable, but in my case I had to add it for just a couple of them. Here is the source of this script for anyone who will stumble into my same problem: http://www.codeproject.com/Articles/23832/Implementing-Deep-Cloning-via-Serializing-objects