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
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.
We made two changes.
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.
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!
It looks like you're new here. If you want to get involved, click one of these buttons!
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.