- Home /
How to make a deep copy instead of a shallow one?
Hi everyone, so as of right now I am making a shallow copy of an item in a list. When I click on this item in game, a copy is made of it and that copy is put in another list. The problem is, when I click on this item more than once, I want each individual copy of this original item to still have different elements. Since a shallow copy is being made right now, each clone is exactly the same... which is not what I want. I am pretty sure the way to do this is to create a deep copy instead, but I don't know how to change my code to be a deep copy.
To be more specific, I want the time I create each copied item to be different on each one. So when the first time the item is copied, the time on copy1 is 1:00. The second time the item is copied, the time on copy2 is 1:05. That sort of thing. Here is my code below:
[System.Serializable]
public class Item
{
public string activityName;
public Sprite icon;
public float Productivity = 1f;
public float Social = 1f;
public float Health = 1f;
public float Fun = 1f;
public string dateButtonAdded;
public string timeAdded;
public Sprite checkMark;
}
public void Setup(Item currentItem, ActivityScrollList currentScrolllist){
item = currentItem;
nameLabel.text = item.activityName;
iconImage.sprite = item.icon;
prodValue.text = item.Productivity.ToString ();
socValue.text = item.Social.ToString ();
healthValue.text = item.Health.ToString ();
funValue.text = item.Fun.ToString ();
checkMark.sprite = item.checkMark;
scrollList = currentScrolllist;
}
I create this items through a simple object pool. I got the code for this from a Unity tutorial. Here is the code for the object pool:
// A very simple object pooling class
public class SimpleObjectPool : MonoBehaviour
{
// the prefab that this object pool returns instances of
public GameObject prefab;
// collection of currently inactive instances of the prefab
private Stack<GameObject> inactiveInstances = new Stack<GameObject>();
// Returns an instance of the prefab
public GameObject GetObject()
{
GameObject spawnedGameObject;
// if there is an inactive instance of the prefab ready to return, return that
if (inactiveInstances.Count > 0)
{
// remove the instance from the collection of inactive instances
spawnedGameObject = inactiveInstances.Pop();
}
// otherwise, create a new instance
else
{
spawnedGameObject = (GameObject)GameObject.Instantiate(prefab);
// add the PooledObject component to the prefab so we know it came from this pool
PooledObject pooledObject = spawnedGameObject.AddComponent<PooledObject>();
pooledObject.pool = this;
}
// put the instance in the root of the scene and enable it
spawnedGameObject.transform.SetParent(null);
spawnedGameObject.SetActive(true);
// return a reference to the instance
return spawnedGameObject;
}
// Return an instance of the prefab to the pool
public void ReturnObject(GameObject toReturn)
{
PooledObject pooledObject = toReturn.GetComponent<PooledObject>();
// if the instance came from this pool, return it to the pool
if(pooledObject != null && pooledObject.pool == this)
{
// make the instance a child of this and disable it
toReturn.transform.SetParent(transform);
toReturn.SetActive(false);
// add the instance to the collection of inactive instances
inactiveInstances.Push(toReturn);
}
// otherwise, just destroy it
else
{
Debug.LogWarning(toReturn.name + " was returned to a pool it wasn't spawned from! Destroying.");
Destroy(toReturn);
}
}
}
// a component that simply identifies the pool that a GameObject came from
public class PooledObject : MonoBehaviour
{
public SimpleObjectPool pool;
}
So basically I am unsure of how to change my code to be able to create a deep copy instead. Any help on how to do this is greatly appreciated.
Ask another question and remove this one.
The question you want to ask is Reversible check point of Object.
Your answer basically is making a Dictionary.
Can you expand on this? What is a reversible check point and how does this tie into making a dictionary?
Sorry not dictionary, we don't have Stack Dict or Queue Dict in C#.
What you want is make another wrapper Struct or Class
private Stack<BackupData> inactiveInstances = new Stack<BackupData>();
public class BackupData{
float time;
GameObject obj;
}
this totally depends on what conponents you want to be different about your game objects. when you instantiate a game object it is not going to make multiply copys of the image files used for texture or the shader or material files or the mesh filesor animation files and blah blah blah. it would be a proficiencey nightmare.
whatever component or propery of a component you want independant you would need to specifically define it with the "new()" command.
scripts are the exeption if they are not marked static. they allready do run independantly when objects are spawned with your custom scripts attached
Answer by JDelekto · Jun 27, 2018 at 02:32 AM
The difference between the shallow and deep copy are not just copying the references and value types of an object's fields, but in the case of reference types, creating brand new objects and populating those with copies of its properties, and recursively.
You can provide a DeepCopy method and create a new instance of your object, then copy over simple properties, while reference types need to have new instances created. Because of the shallow copy you are seeing, your variables such as nameLabel, iconImage, prodValue, etc. are just copying the references over to the new object and the data which they contain will be the same between all objects.
It appears that several types in Unity provide an Instantiate method which can take a reference to an existing type and clone it for you. In the case of simple types like bool, float, string, etc. it's a straight copy of the properties, for things like Image, on the other hand, require the instantiation.
Here is an example of something that uses this technique:
public class Item {
float someValue;
string someString;
bool someBoolean;
Image iconImage;
public Item DeepCopy()
{
Item sc = new Item();
sc.someValue = this.someValue;
sc.someString = this.someString;
sc.someBoolean = this.someBoolean;
sc.iconImage = Image.Instantiate<Image>(this.iconImage);
return sc;
}
}
One other thing, if you are working with pooled objects and do not want to create a new instance, then you can overload (or replace) your DeepCopy() method and pass a parameter which is a reference to your pooled object. It would then look something like the following (item still returned as a convenience):
public Item DeepCopy(Item po)
{
po.someValue = this.someValue;
po.someString = this.someString;
po.someBoolean = this.someBoolean;
po.iconImage = Image.Instantiate<Image>(this.iconImage);
return po;
}
Thank you! I will try this out. Just a question... Would I replace this function with my current "Setup()" function? Just making sure I understand this correctly.
You don't need to replace your Setup() function. The DeepCopy() method I provided goes onto the class that you want to be able to copy.
If you use the DeepCopy version which takes a reference to the object that you want to copy data to, then in your Setup() function, you can just use the single line: currentItem.DeepCopy(this)
.
You pass the 'this' reference to your item, then currentItem will deep copy its properties into your new object. It is basically populating itself from currentItem.
I am trying your method but it still doesn't seem to be working for me. I should also note, my setup function is in another class than where I create my item. I will show which function is in which class this time. This is what I have changed so far:
public class ActivityBookButton : $$anonymous$$onoBehaviour {
public Button button;
public Text nameLabel;
public Image iconImage;
public Text prodValue;
public Text socValue;
public Text healthValue;
public Text funValue;
public Text timeAdded;
public Item item;
private ActivityScrollList scrollList;
public void Setup(Item currentItem, ActivityScrollList currentScrolllist){
item = currentItem;
nameLabel.text = item.activityName;
iconImage.sprite = item.icon;
prodValue.text = item.Productivity.ToString();
socValue.text = item.Social.ToString ();
healthValue.text = item.Health.ToString ();
funValue.text = item.Fun.ToString ();
timeAdded.text = currentItem.timeAdded;
scrollList = currentScrolllist;
}
[System.Serializable]
public class Item
{
public string activityName;
public Sprite icon;
public float Productivity = 1f;
public float Social = 1f;
public float Health = 1f;
public float Fun = 1f;
public string dateButtonAdded;
public string timeAdded;
public Item DeepCopy()
{
Item po = new Item ();
po.activityName = this.activityName;
po.icon = this.icon;
po.Productivity = this.Productivity;
po.Social = this.Social;
po.Health = this.Health;
po.Fun = this.Fun;
po.dateButtonAdded = this.dateButtonAdded;
po.timeAdded = this.timeAdded;
return po;
}
}
I'm sorry if these seem like basic questions but I am pretty new to all of this so I'm not really sure where I am going wrong now. Why is everything still exactly the same even though I added the DeepCopy function? Thanks in advance.
Your answer
Follow this Question
Related Questions
copy an list in c# 1 Answer
A node in a childnode? 1 Answer
Copy From Array to List without reference [C#] 2 Answers
Add item to string by name? 1 Answer
How do get a specific item in a list? 0 Answers