こんにちは、Unityエンジニアのオオバです。

お悩みさん
お悩みさん
  • DOTweenで謎の警告が出る
  • An error inside a tween callbackって何?
  • エディタでは動くのに実機でクラッシュする
  • オオバ
    オオバ
    本記事ではこれらの悩みを解決します。

    DOTweenのコールバック内で例外が発生している。エディタでは警告だが実機ではクラッシュする

    👉DOTweenの教科書を読んでUnityアニメーションをプログラミングしてみよう!

    どんな警告メッセージが出る?

    DOTweenを使っていると、以下のような警告がコンソールに出ることがあります。

    DOTWEEN ► An error inside a tween callback was silently taken care of  
    (Void <b__21_0>(Int32)) ► Index was outside the bounds of the array.  
    

    この警告が出ても、Unityエディタ上ではアプリが止まりません。
    そのため見落としがちですが、放置すると実機で致命的な問題になります。

    原因はDOTweenではなく自分のコード

    DOTweenが悪いわけではありません。大抵の原因はコールバック内のコードにあります。

    たとえば以下のように、OnCompleteのコールバック内で配列の範囲外アクセスが発生しているケースです。

    transform.DOLocalMoveX(1f, 1f).OnComplete(() => _array[範囲外のIndex]);  
    

    DOTweenはコールバック内の例外を内部でキャッチして警告として出力します。
    そのため例外がスローされず、エディタでは警告のみで処理が継続してしまいます。

    実機ではクラッシュするので要注意

    Unityエディタで実行する際は警告のみ出力されるだけですが、実機で実行すると問答無用でクラッシュします。

    ※iPhoneおよびAndroid実機で確認済み

    エディタで警告が出た時点で必ず原因を特定し、修正しましょう。

    なぜエディタではクラッシュしないのか

    DOTweenの内部実装を見ると、コールバック実行時にtry-catchで例外をキャッチしています。
    キャッチした例外はUnityのDebug.LogWarningで出力されるだけで、処理は継続します。

    一方、IL2CPPビルドした実機では、DOTweenの例外ハンドリングの挙動が異なる場合があります。
    特にiOSのIL2CPPビルドでは、マネージド例外の扱いがエディタと異なるため、エディタでは問題なく動作していたコードが実機でクラッシュするという厄介な状況が発生します。

    OnComplete以外のコールバックでも発生する

    この問題はOnCompleteに限った話ではありません。DOTweenが提供するすべてのコールバックで発生する可能性があります。

    // これらすべてのコールバック内で例外が発生するとクラッシュの原因になる  
    transform.DOLocalMoveX(1f, 1f)  
        .OnStart(() => { /* ここでも発生しうる */ })  
        .OnUpdate(() => { /* ここでも発生しうる */ })  
        .OnComplete(() => { /* ここでも発生しうる */ })  
        .OnKill(() => { /* ここでも発生しうる */ });  
    

    特にOnUpdateは毎フレーム呼ばれるため、条件次第で大量のエラーが発生し、パフォーマンスにも影響を与えます。

    対処法と防止策

    この問題を防ぐには、コールバック内のコードを堅牢にすることが重要です。

    1. null チェックを徹底する

    transform.DOLocalMoveX(1f, 1f).OnComplete(() =>  
    {
        if (_targetObject != null)  
        {
            _targetObject.SetActive(false);  
        }
    });  
    

    2. 配列アクセス前に範囲チェックする

    transform.DOLocalMoveX(1f, 1f).OnComplete(() =>  
    {
        if (index >= 0 && index < _array.Length)  
        {
            var value = _array[index];  
        }
    });  
    

    3. コールバック内の処理をメソッドに切り出す

    コールバックにラムダ式で長い処理を書くと、どこで例外が発生しているか特定しにくくなります。
    メソッドに切り出すことで、スタックトレースが読みやすくなります。

    transform.DOLocalMoveX(1f, 1f).OnComplete(OnMoveComplete);  
    
    void OnMoveComplete()  
    {
        // ここでの例外はスタックトレースで特定しやすい  
    }
    

    4. Tweenの生存確認を行う

    DOTweenのコールバックが呼ばれるタイミングで、参照しているオブジェクトがすでに破棄されているケースは珍しくありません。
    特にシーン遷移を挟む場合、Tweenだけが生き残り、コールバック内で破棄済みのオブジェクトにアクセスしてクラッシュすることがあります。

    // シーン遷移前にTweenを明示的にKillする  
    void OnDestroy()  
    {
        transform.DOKill();  
    }
    

    DOKill()を呼ぶことで、そのTransformに紐づくすべてのTweenが停止し、コールバックも呼ばれなくなります。
    OnDestroyOnDisableで呼んでおくのが定石です。

    デバッグ時のコツ

    この警告が出たとき、どこが原因か特定しにくいことがあります。
    いくつかのデバッグテクニックを紹介します。

    DOTweenのログレベルを上げる

    DOTweenの設定でログレベルを変更すると、より詳細な情報が出力されます。

    DOTween.logBehaviour = LogBehaviour.Verbose;  
    

    Verboseにすることで、どのTweenのどのコールバックでエラーが発生したか追いやすくなります。
    ただし出力量が多くなるため、デバッグ時のみ有効にしましょう。

    ブレークポイントを活用する

    Visual StudioやRiderを使っている場合、コールバック内にブレークポイントを設定することで、例外発生時の変数の状態を確認できます。

    DOTweenが例外をキャッチしてしまう前に止めたい場合は、IDEの「例外発生時にブレーク」設定を有効にすると、例外の発生箇所で自動的に停止します。

    まとめ

    DOTweenのコールバック内で例外が発生すると、エディタでは警告のみですが実機ではクラッシュします。
    エディタでこの警告を見つけたら、必ず原因を特定して修正しましょう。

    対処のポイントは以下の4つです。

    1. コールバック内でnullチェック・範囲チェックを徹底する
    2. 処理が長くなる場合はメソッドに切り出す
    3. シーン遷移前にDOKill()でTweenを停止する
    4. デバッグ時はログレベルをVerboseにして原因を特定する
    DOTweenの教科書
    【600冊以上販売】Unityのアニメーション用ライブラリのDOTweenを体系的に学べる教科書。11万文字の大ボリュー...
    詳しくはこちら
    DOTweenの教科書

    Unityオブジェクトの描画順の制御って難しいですよね。
    この度、Unityの描画順を体系的に学べる「Unity描画順の教科書」を執筆しました。

    Unityの描画順を基礎から学びたい方はぜひ確認してみてください!
    Unity描画順の教科書

    最後まで読んでいただきありがとうございました!
    すばらしいUnityライフをお過ごしください。

    オススメ記事
    検証環境