sh1’s diary

プログラミング、読んだ本、資格試験、ゲームとか私を記録するところ

Unity Debug.Log 使用方法に関する最適化のまとめ

Unity のデバッグで Unity Console にメッセージを書き込むために Debug.Log を利用していると、デフォルトではビルド後もログを書き残してしまいます。

この仕様についてメモ。


Unity - UnityEngine.Debug クラス

Unity で利用する UnityEngine.Debug クラスを見てみると、普通のクラスのようです。リリース時やデバッグ時でなんらかの使い分けがされているわけでもなさそうです。

Android 端末から Debug.Log() の内容を確認する 1 というような記事があるとおり、端末からデバッグとしてテキストを確認するニーズがあるため、C#System.Diagnostics.Debug クラスのようにリリース時は Debug メソッドをクリアするようなことをしてくれません。(デフォルトではしてくれない)

f:id:shikaku_sh:20200122151321p:plain:w400
Log は属性なにもない

補足として、Assert 関係のメソッドは、Unity でも Conditioal 属性 UNITY_ASSERTIONS が付与されています。

つまり、同じ名前で同じようなデバッグ機能を提供するクラスですが、動作に異なるところがあります。注意が必要です。


C# - System.Diagnostics.Debug クラス

一応ですが、C# デフォルトの System.Diagnostics.Debug クラスは、リリース時はログ(メソッド自体のコスト)を残しません。どういうことかポイントをメモ。

これは、Debug クラスのすべてのメソッドにConditional 属性で #DEBUG# が設定されています。

f:id:shikaku_sh:20200122151356p:plain:w400
ずらーとわかりやすい DEBUG 属性

本当かどうか気になる人は、Microsoft の Reference Source で Debug クラスを確認してみてください。


ログを無効にする設定変更

  • Debug.logger.logEnabled = false;

これも Conditioal 属性でメソッド自体をクリアしてしまうわけではないので、メソッド自体の呼び出しコストは発生します。

Debug.Log() メソッドを呼び出すときに、文字列を処理するようなコードが書かれていたなら GC も動くことになるため、リリース時の最適化としては不十分です。

// Debug メソッドはなにもしなくても、呼び出し時点でテキスト処理がある
Debug.Log($"Debug: x={x}, y={y}, z={z}, exception={ex.message}");

そんなわけで、現在の Unity 事情は Conditional 属性を与えたラッパークラスを作成する方法が一般的のようです。


スタックトレースの表示範囲の設定

Debug の情報レベル(Error, Warning, Log, Exception など)で表示する情報をフィルタリングすることができます。

f:id:shikaku_sh:20200122151501p:plain:w350
フィルタリングの機能

ただし、これも表示をフォルタリングをするだけなので、メソッドそのもののコストを残さないわけではありません。ログを見やすくするための機能ですね。


対策 ラッパークラスの作成

自作のラッパークラスを作成して、開発時だけ有効なメソッドになるようにします。この際は、「Micorosoft Reference Source - Debug クラス」の実装が参考になると思います。

Conditional 属性に与えるテキストは、どちらかの方法をとります。

  • Unity プラットフォームの判定に利用される UNITY_EDITOR を指定すること
  • 独自の変数名を与えて、Scripting Define Symbols に変数名を追加する

なんらかの端末で動作させるとき、Log メソッドのデフォルトは、メソッド自体を無効とします。自分が決めた変数名 (例:ENABLE_DEBUG_MODE ) が有効なときだけ、端末動作時にメソッドを有効化します。

通常時は UNITY_EDITOR の変数名を与え、開発時(プラットフォーム)かどうかで有効/無効を切り替えるだけでよさそうです。次のコードみたいになりました。

public static class Debugger
{
#if !ENABLE_DEBUG_MODE
    private const string CONDITIONAL_TEXT = "UNITY_EDITOR";
#else
    private const string CONDITIONAL_TEXT = "ENABLE_DEBUG_MODE";
#endif

    [Conditional(CONDITIONAL_TEXT)]
    public static void Log(object message)
    {
        UnityEngine.Debug.Log(message);
    }
}

ラッパークラスなので、Debug クラスの持つ必要なメソッド(属性を持たないメソッドなど)をラップしてあげればよいと思います。

ラッパークラスの名前は、Debug クラスに似たような感じにしておけば、使用するときに違和感が減っていいかもしれないです。あんまり個性を出さなくてよい部分かと。


サンプル

ラッパークラスを完成させたサンプルがこちら。


ラッパークラスの拡張

せっかくラッパークラスを作ったんだから、なにか付与してもよいと思います。(アダプトっぽい感じになりますが)

デバッグのログを装飾するメソッドを用意してみるのもよいと思います。2 ただ、使い方を知っておけばいいだけの気もします。

個人的にメソッド名を Red, Green, Blue のようにしてしまうのは(細かいですが)好みではないです。C# の一般的な命名規則として採用しづらいはずです。


参考

楽しく学ぶ Unity2D超入門講座

楽しく学ぶ Unity2D超入門講座