プログラマにとってログ出力は原始的で最も頼もしいデバッグツールです。
UnityにもConsoleウインドウに文字列を出力するためのメソッドがあります。
Debug.Log("Hello World!");
これについては説明する必要はないですね。
問題はこのメソッドが、リリースビルドでも実行されるという事実です。
Debugクラスのメソッドなので、デバッグビルドだけで機能しそうですが、リリースビルドでも機能するので、そのままだと配布したアプリでもログが出力されてしまいます。
Unityの設定でログ出力を無効にできますが、メソッドの呼び出しコストは残ってしまいます。
リリースビルドでDebugクラスのメソッドを完全に機能しないようにするには、DEBUGのシンボルで切り分けて書くか、独自のクラスでラップする方法が考えられます。
#if DEBUG
Debug.Log("Hello World!");
#endif
Debugクラスのメソッドを書くたびに、DEBUGで囲むのも面倒くさいです。
独自のクラスでラップする場合も、最初からそういうルールを徹底しておかないといけません。
何か良い手段はないものか…
この場合、Conditional属性を付けて呼び出しそのものを無効にする方法が簡単です!
C#ではnamespaceを使わず同名クラスが定義された場合、グローバルのクラスが優先されるという特徴があります。
以下のコードは、Unityの”Scripting Define Symbols”に NO_DEBUG を定義すると、Unity標準のDebugクラスを置き換えます。各メソッドは DUMMY が定義されたときにしか呼び出されないので、置き換えられたクラスの中身は空になります。(※ DUMMY は文字通りダミーなので定義しません)
この方法の最大のメリットは既存コードに何も手を加えなくて良いことです。
アセットストアなどで購入したアセットに含まれるスクリプトも、UnityのDebugクラスを使用する箇所はすべて置き換わるので、すべての呼び出しが無効になります。
#if NO_DEBUG
using UnityEngine;
using System.Diagnostics;
public static class Debug
{
//DUMMYは定義しなくて良い。
[Conditional("DUMMY")] public static void Assert(bool condition, string message, Object context) {}
[Conditional("DUMMY")] public static void Assert(bool condition, object message, Object context) {}
[Conditional("DUMMY")] public static void Assert(bool condition, string message) {}
[Conditional("DUMMY")] public static void Assert(bool condition, object message) {}
[Conditional("DUMMY")] public static void Assert(bool condition, Object context) {}
[Conditional("DUMMY")] public static void Assert(bool condition) {}
[Conditional("DUMMY")] public static void Assert(bool condition, string format, params object[] args) {}
[Conditional("DUMMY")] public static void AssertFormat(bool condition, string format, params object[] args) {}
[Conditional("DUMMY")] public static void AssertFormat(bool condition, Object context, string format, params object[] args) {}
[Conditional("DUMMY")] public static void Break() {}
[Conditional("DUMMY")] public static void ClearDeveloperConsole() {}
[Conditional("DUMMY")] public static void DebugBreak() {}
[Conditional("DUMMY")] public static void DrawLine(Vector3 start, Vector3 end, Color color, float duration, bool depthTest) {}
[Conditional("DUMMY")] public static void DrawLine(Vector3 start, Vector3 end, Color color, float duration) {}
[Conditional("DUMMY")] public static void DrawLine(Vector3 start, Vector3 end) {}
[Conditional("DUMMY")] public static void DrawLine(Vector3 start, Vector3 end, Color color) {}
[Conditional("DUMMY")] public static void DrawRay(Vector3 start, Vector3 dir, Color color, float duration) {}
[Conditional("DUMMY")] public static void DrawRay(Vector3 start, Vector3 dir, Color color, float duration, bool depthTest) {}
[Conditional("DUMMY")] public static void DrawRay(Vector3 start, Vector3 dir) {}
[Conditional("DUMMY")] public static void DrawRay(Vector3 start, Vector3 dir, Color color) {}
[Conditional("DUMMY")] public static void Log(object message) {}
[Conditional("DUMMY")] public static void Log(object message, Object context) {}
[Conditional("DUMMY")] public static void LogAssertion(object message, Object context) {}
[Conditional("DUMMY")] public static void LogAssertion(object message) {}
[Conditional("DUMMY")] public static void LogAssertionFormat(Object context, string format, params object[] args) {}
[Conditional("DUMMY")] public static void LogAssertionFormat(string format, params object[] args) {}
[Conditional("DUMMY")] public static void LogError(object message, Object context) {}
[Conditional("DUMMY")] public static void LogError(object message) {}
[Conditional("DUMMY")] public static void LogErrorFormat(string format, params object[] args) {}
[Conditional("DUMMY")] public static void LogErrorFormat(Object context, string format, params object[] args) {}
[Conditional("DUMMY")] public static void LogException(System.Exception exception, Object context) {}
[Conditional("DUMMY")] public static void LogException(System.Exception exception) {}
[Conditional("DUMMY")] public static void LogFormat(Object context, string format, params object[] args) {}
[Conditional("DUMMY")] public static void LogFormat(string format, params object[] args) {}
[Conditional("DUMMY")] public static void LogWarning(object message) {}
[Conditional("DUMMY")] public static void LogWarning(object message, Object context) {}
[Conditional("DUMMY")] public static void LogWarningFormat(string format, params object[] args) {}
[Conditional("DUMMY")] public static void LogWarningFormat(Object context, string format, params object[] args) {}
}
#endif
このままプロジェクトに突っ込み、NO_DEBUG を定義すれば使えるお手軽さです。
ただし、このテクニックが使える有効な書き方と無効な書き方がある点に注意してください。
有効 (グローバルのDebugクラスが呼び出される)
using UnityEngine; されたコードから Debug.Log 等を呼び出したとき。
Unity標準のC#テンプレートなので、自分で用意するスクリプトはこのケースです。
using UnityEngine;
public class Sample : MonoBehaviour
{
private void Start()
{
//普段はUnityEngine.Debug.Logが呼ばれる。
//NO_DEBUGでは置き換えたグローバルのDebugクラスが優先される。
Debug.Log("Hello World!");
}
}
無効 (UnityEngineのDebugクラスが呼び出される)
namespace UnityEngine {} の中で Debug.Log 等を呼び出したとき。
namespace UnityEngine
{
public class Sample : MonoBehaviour
{
private void Start()
{
//常にUnityEngine.Debug.Logが呼ばれる。
Debug.Log("Hello World!");
}
}
}
UnityEngine.Debug.Log で呼び出したとき。
public class Sample : MonoBehaviour
{
private void Start()
{
//明示的に書いてるので、当然、UnityEngine.Debug.Logが呼ばれる。
UnityEngine.Debug.Log("Hello World!");
}
}
using Debug = UnityEngine.Debug; されてるとき。
using Debug = UnityEngine.Debug;
public class Sample : MonoBehaviour
{
private void Start()
{
//DebugをUnityEngine.Debugにusingしてるので、UnityEngine.Debug.Logが呼ばれる。
Debug.Log("Hello World!");
}
}
外部アセットで稀に上記のケースを見かけます。
すべての書き方に万能ではありませんが、手っ取り早くログを取り除くには便利です。