Problem copying one array to another

I’m trying to create a scripted object array that creates temporary attack, defense, and speed stats that are set to default attack, defense, and speed values at the beginning of my game, but then can be modified during the game.

I’ve creating three arrays: attack_init, defense_init, and speed_init. These store the initial values. I then create nonserialized attack, defense, and speed arrays and set them to their *_init version each time the game starts.

I tested this without arrays and it works as expected. However, now that I’ve converted everything to arrays, my temporary arrays (attack, defense, speed) are somehow linked to the default values (attack_init, defense_init, and speed_init). So as my game updates the temporary version, it immediately updates the *_init values also. This makes the changes in the values persistent across game sessions.

What am I doing wrong?

using UnityEngine;
using System.Collections.Generic;

[CreateAssetMenu(menuName = "SO Variables")]

public class SOFloat : ScriptableObject, ISerializationCallbackReceiver
{
    public int[] player_number;
    public int[] player_team;
    public bool[] active;
    public bool[] ai;
    public int[] ai_personality;
    public int[] ai_multiplier;
    public int[] attack_init;
    public int[] defense_init;
    public int[] speed_init;

   [System.NonSerialized]
    public int[] attack, defense, speed;

    public void OnAfterDeserialize()
    {
        attack = attack_init;
        defense = defense_init;
        speed = speed_init;
    }

    public void OnBeforeSerialize()
    {

    }
}

I’ve experienced the same thing with lists recently.

From my understanding when you set an array equal to another array in the way that you have in OnAfterDeserialize, you don’t create a new array, but rather a reference to the original. This means that when you alter something at index[n] in the attack array it redirects that change to the corresponding index of attack_init.

My method for getting around this with lists was to create a new list, then use a for loop to copy over all of the values manually. I’m not sure if this is the best way but for now it’s what I’m going with.

I see there’s an array.Clone() method too which might work somehow. Honestly I rarely use arrays directly at the moment so don’t know exactly how this one works.

Array.Clone Method (System) | Microsoft Docs

1 Like

Thank you so much! This is a great explanation. Manually copying is a good solution. I also struggled a bit to get Clone() working, but then ran across the CopyTo function, which seems to copy the underlying data without linking the two arrays.

I was able to copy the data to my temporary array using the following code:

        attack_init.CopyTo(attack, 0);
        defense_init.CopyTo(defense, 0);
        speed_init.CopyTo(speed, 0);

That’s handy. Thanks for sharing! I’ll definitely remember that solution for next time. Much easier to write than manual copying.

I’m like 99% sure you don’t need to/shouldn’t be using the serialisation callbacks for this. OnEnable/OnDisable will give you the same effect.

The problem with the serialisation callbacks is they’re threaded, and referencing data on other objects becomes unreliable. The regular Unity callbacks are on the main thread and can safely look at data on other objects.

2 Likes

I didn’t realize that.Thank you!