I am trying to see if anyone has an idea why I’m getting this error.
I do not see any problems, maybe I am missing something.
The object I am Serializing is CharacterData I only count a total of 3 levels.
Error:
Serialization depth limit 7 exceeded at ‘MyGame::EquippedWeapons.rightHand’. There may be an object composition cycle in one or more of your serialized classes.
[Serializable]
public class CharacterData
{
[NonSerialized]
public Character owner;
public string characterName;
public int characterLevel;
public Attributes attributes;
public EquippedWeapons equippedWeapons = new EquippedWeapons();
}
[Serializable]
public class EquippedWeapons
{
public EquippedWeaponInfo rightHand = new EquippedWeaponInfo();
public EquippedWeaponInfo leftHand = new EquippedWeaponInfo();
}
[Serializable]
public class EquippedWeaponInfo
{
public float range = Constants.DEFAULT_MELEE_RANGE;
public float speed = 1;
public float minDamage = 1;
public float maxDamage = 1;
public float averageDamage = (1 + 1) / 2;
public WeaponAnimationType weaponAnimType = WeaponAnimationType.None;
public WeaponType weaponType = WeaponType.None;
public DamageType damageType = DamageType.Physical;
public List<WeaponAttackType> weaponAttackTypes = new List<WeaponAttackType>();
public int objectSoundsId = Constants.DEFAULT_WEAPON_OBJECT_SOUNDS_ID;
}
^ Related to that, this sneaky one got me long ago:
[Serializable]
public class SomeType
{
private SomeType child;
}
My thinking was that, since child is private, it shouldn’t get serialized, right? Unfortunately it still raises the circular reference error because private and protected fields are still processed by the serialization pipeline, even if they’re not included in the final serialized output.
The fix is:
[Serializable]
public class SomeType
{
[NonSerialized] private SomeType child;
}
As a test, you can trim it down until Unity stops complaining about the depth limit. Then, making sure your project’s serialization is set to Force Text, use a text editor to open the .asset or .scene file that contains your CharacterData object. Count the number of indents. This might give you an insight into how many levels Unity’s actually using.
Could there be a circular reference (like lordofduct wrote about) in Attributes? You didn’t post the code for that class.
First trim down your class definition until the warning goes away. You want the warning to go away so Unity can successfully serialize CharacterData and you can look at the result. Assuming the problem is that you’re hitting the depth limit, your modified CharacterData should now serialize 7 levels deep. Save your scene.
Then select Edit > Project Settings > Editor. Set Serialization to Force Text.
Then open an OS file browser to the folder containing your .scene file. Open the .scene file in a text editor (e.g., Textpad on Mac, Notepad on Windows). You should see a text-based YAML representation of your scene. I’m assuming that CharacterData is a field inside a MonoBehaviour script that you’ve added to a GameObject in your scene.
If it’s a ScriptableObject asset, open the .asset file instead.
So you helped me figure it out.
I trimmed down the class so I could look at the scene file.
After trimming it down and rebuilding the code I noticed it complaining about a different class now.
That one was easy because it was a reference to an object I don’t want to serialize.
After fixing that I looked at the output and as expected it was 4 levels deep.
So then I reverted to the original code and I’m not getting the warning anymore.
I looks like Unity was unhappy about the other class that I fixed but was reporting this class as being the problem.
Holy cows. I can’t believe this is still a thing! I have what would be a very simple and elegant script for copying the entire pose of a character as JSON… except that any reasonable character obviously has a deeper bone hierarchy than 7, and I hit this stupidly low limit.
Sure, there should be some limit to avoid infinite recursion. But how about 1023? Or even 127? Why seven, fer cryin’ out loud?
Now my script is going to be neither simple nor elegant. Grr…
How to find out where this warning originates from? The warning is not really helpful
Serialization depth limit 7 exceeded at 'UnityEngine.Events::ArgumentCache.m_ObjectArgument'. There may be an object composition cycle in one or more of your serialized classes.
Serialization hierarchy:
8: UnityEngine.Events::ArgumentCache.m_ObjectArgument
7: UnityEngine.Events::persistentCall.m_Arguments
6: UnityEngine.Events::persistentCallGroup.m_Calls
5: UnityEngine.Events::UnityEventBase.m_PersistentCalls
Well, the limit of 7 is somewhat arbitrary, but the issue is that the amount of allocated objects does not scale linearly or quadratic but somewhat exponentially depending on the underlying structure. Have a look at this old blog post. Since custom serializable classes act like structs, with a depth limit of 1000+ you could end up with millions if not billions of objects which would probably just crash Unity. I’m pretty sure they ran a lot of tests in the early days what limit does still run somewhat stable. There are many workarounds to flatten the hierarchy of hierarchical data. One example is my ResourceDB class to store / serialize the path information of all files in the resources folder(s).
The actual depth limit doesn’t really matter since sooner or later someone will hit it. Such issues are hard to detect and when to decide to stop serializing. A good example is the billion laughs attack.
Well, be careful with UnityEvents. They are built completely in C# and have already several levels of nesting internally. So you can not use UnityEvents in too deeply nested hierarchies.
Thx, that’s what i suspected; it’s not recursive, just too deeply nested which is unfortunate as it’s probably due to using an animation controlling third party asset. 7 seems quite low as default in this case.
using System;
using UnityEngine;
public class NewBehaviourScript : MonoBehaviour
{
public C_0 c;
}
[Serializable]
public class C_0
{
[SerializeField] private C_1 c_1;
}
[Serializable]
public class C_1
{
[SerializeField] private C_2 c_2;
}
[Serializable]
public class C_2
{
[SerializeField] private C_3 c_3;
}
[Serializable]
public class C_3
{
[SerializeField] private C_4 c_4;
}
[Serializable]
public class C_4
{
[SerializeField] private C_5 c_5;
}
[Serializable]
public class C_5
{
[SerializeField] private C_6 c_6;
}
[Serializable]
public class C_6
{
[SerializeField] private C_7 C_7;
}
[Serializable]
public class C_7
{
[SerializeField] private C_8 C_8;
}
[Serializable]
public class C_8
{
[SerializeField] private C_9 C_9;
}
[Serializable]
public class C_9
{
[SerializeField] private C_10 C_10;
}
[Serializable]
public class C_10
{
[SerializeField] private C_11 C_11;
}
[Serializable]
public class C_11
{
[SerializeField] private C_12 C_12;
}
[Serializable]
public class C_12
{
[SerializeField] private C_13 C_13;
}
[Serializable]
public class C_13
{
[SerializeField] private int C_14;
}
[Serializable]
public class C_14
{
[SerializeField] private int C_15;
}
[Serializable]
public class C_15
{
[SerializeField] private int C_11;
}