Forum rules - please read before posting.

Gather Text hangs after the last scene in Unity 6.5 — IMGUI call inside ActionSpeech.GetOwner()

edited August 21 in Technical Q&A

Environment

  • Unity 6000.5.7f1
  • Adventure Creator 1.86.4 and 1.86.5
  • macOS only
  • Speech Manager option Separate speech into lines enabled

Problem

When running Speech Manager → Gather Text, the process appeared to hang when it reached the last scene in Build Settings.

The scene itself loads successfully in about 0.2 seconds and produces no scene-loading exceptions. It is simply the last scene shown in the progress window before AC starts processing ActionList assets.

Editor.log then reported:

text GUILayout: Mismatched LayoutGroup.mouseMove

Root cause

Gather Text is called directly from SpeechManager.ShowGUI():

   if (GUILayout.Button("Gather text", EditorStyles.miniButtonLeft))
   {
       PopulateList();
   }

PopulateList() opens and saves multiple scenes while still running inside an IMGUI event. This eventually invalidates the current GUILayout state.

We first moved PopulateList() to EditorApplication.delayCall. That exposed the underlying exception:

   NullReferenceException: Object reference not set to an instance of an object
     at UnityEngine.GUILayoutUtility.BeginLayoutGroup(...)
     at UnityEditor.EditorGUILayout.BeginVertical(...)
     at AC.CustomGUILayout.BeginVertical()
     at AC.Action.IDToField<T>(...)
     at AC.ActionSpeech.GetOwner(Int32 index)
     at AC.SpeechManager.ExtractTranslatable(...)
     at AC.SpeechManager.ProcessActions(...)
     at AC.SpeechManager.ProcessActionListAsset(...)
     at AC.SpeechManager.PopulateList(...)
     at UnityEditor.EditorApplication.Internal_CallDelayFunctions()

The relevant code in ActionSpeech.GetOwner() was:

   if (isAssetFile)
   {
       if (!isPlayer && parameterID == -1)
       {
           speaker = IDToField<Char>(speaker, constantID, false);
       }
   }

IDToField() is not only a data lookup method. It also draws editor controls using CustomGUILayout, EditorGUILayout, and GUILayout.

Therefore, ActionSpeech.GetOwner() performs GUI rendering while AC is gathering data from ActionList assets. It only works when called inside a valid IMGUI layout context, but running the whole Gather operation inside IMGUI causes the scene-switching layout mismatch.

The affected project contains ActionList assets with non-player speech actions that reference a scene character through a non-zero Constant ID.

Workaround

We made two changes.

1. Run Gather outside the IMGUI event

In SpeechManager.ShowGUI():

   if (GUILayout.Button("Gather text", EditorStyles.miniButtonLeft))
   {
       EditorApplication.delayCall += () =>
       {
           try
           {
               PopulateList();
           }
           finally
           {
               EditorUtility.ClearProgressBar();
           }
       };
   }

The finally block also prevents a stale progress window if another exception occurs.

2. Resolve the speaker without drawing GUI

In ActionSpeech.GetOwner():

   if (isAssetFile &&
       !isPlayer &&
       parameterID == -1 &&
       constantID != 0)
   {
       speaker = ConstantID.GetComponent<Char>(constantID);
   }

This replaces the call to IDToField() with a data-only lookup.

I’m not sure if this is the correct way to fix the issue, but if it is, would it be possible to incorporate this change into the codebase so we don’t have to reapply it after every AC update? Or perhaps there is a better solution to this problem. Thank you!

Comments

  • Thanks for the detailed write-up. That all looks essentially right. I'll need to look into it further but the principle is sound, and I should expect a change along these lines will make it into the next release.

Sign In or Register to comment.

Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Welcome to the official forum for Adventure Creator.