Null Reference Exception in Android app built with Unity 6000.0.32f1 while loading asset bundle containing 3D TextMesh of TextMeshPro

I encounter a Null Reference Exception when loading an asset bundle containing 3D TextMeshPro texts. The asset bundles with the 3D TextMeshPro texts were built using Unity version 2022.3.X. This issue arises specifically when targeting Android and iOS apps built from Unity version 6000.0.32f1.

Here are the logs from the exception:

2024-11-20 13:04:47.579 15345 15427 Error Unity NullReferenceException: Object reference not set to an instance of an object.
2024-11-20 13:04:47.579 15345 15427 Error Unity   at TMPro.MaterialReference..ctor (System.Int32 index, TMPro.TMP_FontAsset fontAsset, TMPro.TMP_SpriteAsset spriteAsset, UnityEngine.Material material, System.Single padding) [0x00000] in <00000000000000000000000000000000>:0 
2024-11-20 13:04:47.579 15345 15427 Error Unity   at TMPro.TextMeshPro.SetArraySizes (TMPro.TMP_Text+TextProcessingElement[] textProcessingArray) [0x00000] in <00000000000000000000000000000000>:0 
2024-11-20 13:04:47.579 15345 15427 Error Unity   at TMPro.TextMeshPro.OnPreRenderObject () [0x00000] in <00000000000000000000000000000000>:0 
2024-11-20 13:04:47.579 15345 15427 Error Unity   at TMPro.TextMeshPro.Rebuild (UnityEngine.UI.CanvasUpdate update) [0x00000] in <00000000000000000000000000000000>:0 
2024-11-20 13:04:47.579 15345 15427 Error Unity   at TMPro.TMP_UpdateManager.DoRebuilds () [0x00000] in <00000000000000000000000000000000>:0 

This error suggests that there might be an issue with initializing the TextMeshPro components or their dependencies when loading from the asset bundle.

Please Note:

  • I want to avoid rebuilding the asset bundles with Unity 6000.X as there are thousands of asset bundles with different font styles.
  • We have successfully upgraded our entire project from 2022.X to 6000.0.X and everything else seems to be working fine. Also, the overall performance improvements are palpable in all scenes. So the bottom line is that we want to upgrade to Unity 6 from 2022.X and hence please avoid the suggestions of sticking to the older version of Unity for our app builds.

@Stephan_B, Could you please help me with some insights on how to work around this issue?

If anyone else has experienced a similar issue or has insights into potential workarounds or fixes, I would greatly appreciate your help!

Thank you!

1 Like

Hi,

I also came across such issue today while migrating my 2022.3.20f1 project to 6000.0.32f1.

  • I have a lot of scene bundles with some specific UI and none of them can display text anymore in their respective UI.
  • Only UI included in the player/base application is working correctly.
  • Everything is working fine when I run in Editor.
  • The same NullReferenceException is happening in my WebGL build when loading the scene bundle:
Error log
NullReferenceException: Object reference not set to an instance of an object.
  at TMPro.MaterialReference..ctor (System.Int32 index, TMPro.TMP_FontAsset fontAsset, TMPro.TMP_SpriteAsset spriteAsset, UnityEngine.Material material, System.Single padding) [0x00000] in <00000000000000000000000000000000>:0 
  at TMPro.TextMeshProUGUI.SetArraySizes (TMPro.TMP_Text+TextProcessingElement[] textProcessingArray) [0x00000] in <00000000000000000000000000000000>:0 
  at TMPro.TMP_Text.ParseInputText () [0x00000] in <00000000000000000000000000000000>:0 
  at TMPro.TMP_Text.GetPreferredWidth () [0x00000] in <00000000000000000000000000000000>:0 
  at TMPro.TMP_Text.get_preferredWidth () [0x00000] in <00000000000000000000000000000000>:0 
  at UnityEngine.UI.LayoutUtility+<>c.<GetPreferredWidth>b__4_1 (UnityEngine.UI.ILayoutElement e) [0x00000] in <00000000000000000000000000000000>:0 
  at UnityEngine.UI.LayoutUtility.GetLayoutProperty (UnityEngine.RectTransform rect, System.Func`2[T,TResult] property, System.Single defaultValue, UnityEngine.UI.ILayoutElement& source) [0x00000] in <00000000000000000000000000000000>:0 
  at UnityEngine.UI.LayoutUtility.GetLayoutProperty (UnityEngine.RectTransform rect, System.Func`2[T,TResult] property, System.Single defaultValue) [0x00000] in <00000000000000000000000000000000>:0 
  at UnityEngine.UI.LayoutUtility.GetPreferredWidth (UnityEngine.RectTransform rect) [0x00000] in <00000000000000000000000000000000>:0 
  at UnityEngine.UI.LayoutUtility.GetPreferredSize (UnityEngine.RectTransform rect, System.Int32 axis) [0x00000] in <00000000000000000000000000000000>:0 
  at UnityEngine.UI.ContentSizeFitter.HandleSelfFittingAlongAxis (System.Int32 axis) [0x00000] in <00000000000000000000000000000000>:0 
  at UnityEngine.UI.ContentSizeFitter.SetLayoutHorizontal () [0x00000] in <00000000000000000000000000000000>:0 
  at UnityEngine.UI.LayoutRebuilder+<>c.<Rebuild>b__12_1 (UnityEngine.Component e) [0x00000] in <00000000000000000000000000000000>:0 
  at UnityEngine.UI.LayoutRebuilder.PerformLayoutControl (UnityEngine.RectTransform rect, UnityEngine.Events.UnityAction`1[T0] action) [0x00000] in <00000000000000000000000000000000>:0 
  at UnityEngine.UI.LayoutRebuilder.Rebuild (UnityEngine.UI.CanvasUpdate executing) [0x00000] in <00000000000000000000000000000000>:0 
  at UnityEngine.UI.CanvasUpdateRegistry.PerformUpdate () [0x00000] in <00000000000000000000000000000000>:0 
  at UnityEngine.Canvas.SendWillRenderCanvases () [0x00000] in <00000000000000000000000000000000>:0

Lead 1: changes in TextMeshPro and Unity UI

I believe the main difference in Unity 6 is that TextMeshPro is now part of the Unity UI package and Unity UI has been upgraded to 2.0.0 (see changelog).

Unfortunately it doesn’t really seem possible to downgrade to Unity UI 1.0.0 (see here, I also tryied to manually add the old UI 1.0.0 and the old TextMeshPro sources to my Packages directory but there was some compilation errors (API changes on Font Engine and InputSystem)).


Lead 2: Adressable group Built in Data is not working/included in player build anymore

When compiling I get the following warning: Built In Data does not have any associated AddressableAssetGroupSchemas. Data from this group will not be included in the build. (more info in this post).

Since Built In Data used to reference assets present in Resources folder and since TextMeshPro is also using Resources directory, we can guess our old asset bundles cannot access anymore some TextMeshPro dependency…


Please note

I am also in the same situation: it is not practical to to regenerate every asset bundles (most of them are built for multiple platforms (and even multiple server environments), and I also don’t have direct access to all the sources of my colleagues who created these bundles).

I hope there is another solution/workaround since this issue is the only thing preventing me to upgrade my project to Unity 6.

Thanks!

So I made a couple of tests today, mostly related to the version change of the adressable package (Lead 2).

I have noticed it’s possible select an older version for the Adressables package in an Unity 6 project.

  • So I have downgraded my Unity 6 player project Adressables package to version 1.22.3.
  • It complies and executes fine, the “Built In data” group is restored and is referencing Resources assets
  • But sadly the NullReference error on the TextMeshPro components of AssetbBundles built with Unity 2022.3.x is still there :cry:

So I guess the issue is not related to a change in Adressables (Lead 2) but more on a change in TextMeshPro components (Lead 1).


Earlier today I also found this similar issue, but I don't know if it happened on a project involving AssetBundles:

Yes, the NRE seems to come from the TextMeshPro component rather than the Asset Bundles. I am able to load it properly in the Unity editor but it throws an exception on the Android device.

I have submitted a bug report. I hope someone from the Unity dev team can check this and provide some workaround or a fix for this.

1 Like

Investigation

I am currently looking at the sources of TextMeshPro included in the new UI package,

This is where the NullReference is happening (MaterialReferenceManager line 525):

public MaterialReference(int index, TMP_FontAsset fontAsset, TMP_SpriteAsset spriteAsset, Material material, float padding)
        {
            this.index = index;
            this.fontAsset = fontAsset;
            this.spriteAsset = spriteAsset;
            this.material = material;
            this.isDefaultMaterial = material.GetInstanceID() == fontAsset.material.GetInstanceID();
            this.isFallbackMaterial = false;
            this.fallbackMaterial = null;
            this.padding = padding;
            this.referenceCount = 0;
        }

So the only line that can throw it is line 7 where material, fontAsset or fontAsset.material could be null.

I have just wrote a piece of code to log these variables just after loading my Asset Bundles:

// dirty way to get loaded TextMeshPro objects
var tmpTexts = FindObjectsByType<TextMeshPro>(FindObjectsInactive.Include, FindObjectsSortMode.None);

foreach(var tmpText in tmpTexts) {
    // you might need to filter out what is not part of your main application
   
   Debug.Log(tmpText.name);
   Debug.Log("material" + tmpText.material);
   Debug.Log("font" + tmpText.font);
                
   if(tmpText.font) {
      Debug.Log("font.material:" + tmpText.font.material);
   }
}

and when I run it in builds, I can see the one that is null is font.material

The reason

Now I am looking at TMP_FontAsset.cs, it extends from TMP_Asset.cs and in this file I have discovered that the material field has been transformed to a property and renamed m_Material:

        /// <summary>
        /// The material used by this asset.
        /// </summary>
        public Material material
        {
            get => m_Material;
            set => m_Material = value;
        }

       [...]

        [SerializeField][FormerlySerializedAs("material")]
        internal Material m_Material;

and when I look at the previous version of the package (com.unity.textmeshpro@3.0.6, before Unity 6), the field was indeed named material

        /// <summary>
        /// The material used by this asset.
        /// </summary>
        public Material material;

So, when the assets from our old Asset Bundles are deserialized, it looks like their data from the font.material old field are not copied to the font.m_Material new field.

Solution?

We either need a way

  • to tell the engine to use the data from the old font.material field
  • or to work with a modified version of the TextMeshPro package (but since the package doesn’t appear anymore in the manifest I don’t know how to modify it in my project)

Thanks, @colin-defais, this is a great find. It seems like a plausible cause of the NRE.

@Stephan_B, could you please check this and see if it is valid?

Hi again,

I came up with a limited workaround where my script replaces the AssetBundles font materials by the ones present in the player project. This is done before instancing the AssetBundles (or in my case before activating the SceneBundle).

But this is not ideal:

  • the player project must include a copy of the fonts present in the AssetBundles with the same exact names (or use a default font if not found)
  • the font replacement step might slow down a bit the loading of your bundles
var tmpTexts = Resources.FindObjectsOfTypeAll<TMP_Text>();
// Use Resources.FindObjectsOfTypeAll instead of Object.FindObjectsByType else we miss fonts in prefabs

foreach(var tmpText in tmpTexts) {

	if(!tmpText.font.material) {
		if(_nameToFonts.TryGetValue(tmpText.font.name, out TMP_FontAsset font)) { // This dictionary is set up earlier in the code to reference the player App fonts
			tmpText.font = font;
		}
		else {
			Debug.LogWarning($"Font '{tmpText.font.name}' not found in player, using default.");
			tmpText.font = _defaultFont;
		}

		// tmpText.m_enableWordWrapping (bool) as been replaced by tmpText.m_TextWrappingMode (enum), so old Asset Bundles wraping is also lost
		// by default tmpText.m_TextWrappingMode is TextWrappingModes.NoWrap
		if(tmpText.text.Length >= 25) { // we enable line breaks only for large paragraphs (because in some cases we have text in 0 or close 0 width rect transforms)
			tmpText.textWrappingMode = TextWrappingModes.Normal;
		}
	}
}

I have also discovered a new issue, the wrapping field has also changed: it was a boolean (TMP_Text.m_enableWordWrapping) but it is now an enum (TMP_Text.textWrappingMode).

So all TMP_Text loaded from AssetBundles built before Unity 6 have by default no wrapping.
My workaround here is to enable the wrapping in my script if the text is long enougth, for example after 25 chars.

This seems like a good solution, but in our case, we have many different fonts and don’t have access to most of them as the font files were part of the asset bundles.

What do you recommend in this case?

Maybe another (and better) solution to mine is to iterate the Assets in your AssetBundle to find the font material assets (using probably AssetBundle.GetAllAssetNames() and AssetBundle.LoadAsset()).
Then, using the names, it should be possible to re-assign these materials to the fonts.

But this is my theory, I don’t know if this work or if there are limitations…

I tried updating the font asset of all the TMP components from the AssetBundle but it didn’t work at first. I had to update its material and shader as well. This removed the NRE and loaded the TMP components in the scene. Here is how I did it:

UnityEngine.Object[] assetBundleAssets = assetBundle.LoadAllAssets();

private void ReapplyTmpFontAssets(GameObject parentObject, UnityEngine.Object[] assetBundleAssets)
{
    if (parentObject && parentObject.transform.childCount > 0)
    {
        TextMeshPro[] childTextMeshProGameObjs = parentObject.GetComponentsInChildren<TextMeshPro>(true);
        TMP_FontAsset tempFontAsset;
        Shader tmpShader;
        Renderer tmpRenderer;
        foreach (var tmpText in childTextMeshProGameObjs)
        {
            if (!tmpText.font.material)
            {
                Debug.LogFormat("Font asset material is empty on {0}...", tmpText.gameObject.name);
                Debug.Log("Font asset name: " + tmpText.font.name);
                tempFontAsset = Array.Find(assetBundleAssets, obj => obj is TMP_FontAsset fontAsset &&
                    Utils.StringEquals(fontAsset.name, tmpText.font.name)) as TMP_FontAsset;
                if (tempFontAsset)
                {
                    tmpRenderer = tmpText.GetComponent<Renderer>();
                    tmpShader = TextMeshProShaderManager.instance.GetShader(tmpRenderer.material.shader.name);
                    tempFontAsset.material = new Material(tmpShader);

                    Debug.LogFormat("Reapplying font asset '{0}' and material {1}...", tempFontAsset.name,
                        tempFontAsset.material.name);
                    tmpText.font = tempFontAsset;
                }
                else
                {
                    Debug.LogError("Font asset not found in vue dependencies...");
                }
                tmpText.textWrappingMode = TextWrappingModes.PreserveWhitespace;
            }
        }
    }
}

Now, a new issue arises after this, the font looks dark compared to the actual visuals in the unity editor when loaded at runtime from the asset bundle:

Current:

Original:

What do you think could be causing this? How can I solve this?

P.S. - In the editor, it looks fine when doing it this way, but in the Android app, the TMP texts look dark when loaded like this.

Hi,

I haven’t experienced any color issue but I am mostly using Text Mesh Pro UI (Canvas) components so the properties might be different than Text Mesh Pro meshes.

One guess is that some fields might have changed between your AssetBundle builds and Unity 6. In editor everything looks fine because the editor is able to translate the old property names to the new ones (thanks to the FormerlySerializedAs attribute.

Another guess is that, if you are using a Texture in your text material, the texture color space is wrong. This is something I have experienced with ETC textures: Unity 6 Android: wrong color space when loading textures from AssetBundles made with Unity 2022.3.X.
But this make the colors lighter not darker and you can experience the issue in Editor.