UnityでAndroid向けアプリを作成している時にtrying to add gameobject for graphic rebuild
while we are already inside a graphic rebuild loop.this is not supportedというエラーが発生したので
こちらのエラーを紹介していきます。
いつ発生したか?
Androidにビルドして実機テストを行っている時にこのエラーが発生しました。
厄介だったのはUnityエディタではこのエラーが発生せず、実機にビルドした時のみに
このエラーが発生しました。
またこのエラーのほかにも以下のエラーが発生しました。
Error Unity Trying to remove Panel (UnityEngine.UI.Image) from rebuild list while we are already inside a rebuild loop. This is not supported.
原因はリビルドループ中の動的なUIの変更
調査してもそれらしい記事が見つからなかったのでChatGPTに投げて聞いてみました。
Unityエラー:
リビルドループ内にいる間に、リビルドリストからマスク (UnityEngine.UI.Image) を
削除しようとしていますが、これはサポートされていません。
このエラーの原因
このエラーは、UIの更新中(リビルドループの最中)にMaskコンポーネント(またはUnityEngine.UI.Image)を削除または変更しようとした場合に発生します。
UnityのUIシステムは、リビルドループの間にコンポーネントの追加・削除が発生すると安定性に影響を与えるため、この操作を禁止しています。
解決策
タイミングを見直すマスクやUI要素の変更や削除を、
リビルドが行われていないタイミングで行うようにします。
例えば、次のタイミングで変更を加えることができます:
LateUpdateで処理する一度フラグを立てて、次のフレームで変更を行う
Canvas.ForceUpdateCanvasesの使用
リビルドを強制的に終了させた後に処理を行うようにする方法です:
csharpCanvas.ForceUpdateCanvases();//
その後、Maskを削除非同期処理を検討するリビルドループが終了した後で操作を行うために、
非同期で処理を遅らせる方法もあります
csharpIEnumerator RemoveMaskLater(GameObject maskObject)
{
yield return null; // 1フレーム待機
Destroy(maskObject);
}
デバッグを行うどのタイミングでMaskが削除されようとしているのかを確認するために、
Debug.Logを追加して、問題のコード部分を特定します。
参考
この問題はUIが複雑なプロジェクトや、頻繁に動的な変更が行われる場合に発生しやすいです。
エラー発生箇所を特定し、変更タイミングを調整することで、問題を解決できます。
リビルドループ…どうやら聞きなれない言葉が出てきました。
リビルドループについても調査してもピンとくるものが出てこなかったので
ChatGPTに聞いてみました。
リビルドループとは
リビルドループ(Rebuild Loop)は、UnityのUIシステム(UGUI)がUI要素を再描画(リビルド)するために実行するプロセスのことです。このプロセスでは、CanvasやUI要素(Text、Imageなど)の変更が画面に反映されるよう、必要な計算や更新が行われます。
リビルドループの流れ
- Canvasの更新検出:
- UIの要素(位置、サイズ、テキスト、画像など)に変更があると、Unityはその要素が属する
Canvas
をリビルドの対象としてマークします。- レイアウトの再計算:
RectTransform
やLayoutGroup
(例: Horizontal Layout、Vertical Layout)を使っている場合、子要素の位置やサイズを再計算します。- グラフィック要素の更新:
Image
やText
といった描画コンポーネントのデータ(頂点情報など)が再構築されます。- 実際の描画:
- 再計算された情報を基に、画面上にUIが再描画されます。
この一連の流れがリビルドループであり、基本的にはフレームごとに実行されるわけではなく、
変更があった場合にのみトリガーされます。
リビルドループ中に発生する制約
リビルドループ中にUI要素を動的に変更すると、以下の問題が発生することがあります:
- 無効化や削除のタイミング:
- リビルドループ中に要素を無効化(
SetActive(false)
)または削除(Destroy
)しようとすると、Canvasがまだその要素を処理中であるため、エラーが発生します。- リスト操作の競合:
- Unityの内部では、リビルドループ中に「リビルドが必要なUI要素のリスト」を管理しています。このリストを操作しようとすると競合が起き、エラーが発生します(今回のエラーがこれ)。
つまり、リビルドループとはSetActive関数などで画面を動的に変更するとき、画面を表示するために
Unityのシステムで再計算や更新が行われているということです。
この画面の再計算や更新の時にUIを動的に変更しているのでエラーが発生しているのだと思われます。
確かにソースコードを見直すとSetActiveを頻繁に使っており、
UIの表示にアニメーションも使っていたので、
処理が重かったのだと気づきました。
コルーチンの使用とオブジェクトの見直しで解決
Start関数でSetActiveを使っていたので、まずはStart関数をコルーチンに変更し、
処理を1フレーム遅らせることを行いました。
【変更前】
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
using System;
public class TitleManager : MonoBehaviour
{
public GameObject TransParent;
// ローディング画面を出すライブラリ
public ProgressBarLoopUI ProgressBarLoopUI
void Start()
{
sendButton.onClick.AddListener(StartBtnClick);
// アニメーションライブラリ開始処理
ProgressBarLoopUI.OnButtonClick();
// 後ろの透明のオブジェクトをtrue
TransParent.SetActive(true);
// アニメーションライブラリ終了処理
ProgressBarLoopUI.UpdateTransition();
}
}
【変更後】
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
using System;
public class TitleManager : MonoBehaviour
{
public GameObject TransParent;
public ProgressBarLoopUI ProgressBarLoopUI
// ★コルーチンに変更
IEnumerator Start()
{
sendButton.onClick.AddListener(StartBtnClick);
// アニメーションライブラリ開始処理
ProgressBarLoopUI.OnButtonClick();
// ★追加 1フレーム待機
yield return null;
// 後ろの透明のオブジェクトをtrue
TransParent.SetActive(true);
// ★追加 1フレーム待機
yield return null;
// アニメーションライブラリ終了処理
ProgressBarLoopUI.UpdateTransition();
}
}
また、操作不可能にするオブジェクトはTransParentというオブジェクトなのですが、
こちらをUI>Panelで作成しており、このPanelの部分でエラーが発生していたので、
PanelではなくImageに変更しました。するとエラーが解消しました。
コルーチンについては下記記事が参考になります。
コメント