Unityを触り始めた頃、つい何でもかんでも Update() の中に print() を書いてデバッグしてしまいがちですよね。
「とりあえずログ出しておけば後で見返せるだろう」と思っていると、いつの間にか Console ウィンドウがログで埋まり、どこで何が起きているのか分からなくなってしまいます。

さらに、ビルドした実機環境では Unity の Console ウィンドウは見えません。
スマホやスタンドアロンビルドで動かしたとき、「今なにが起きてるの?」が全く分からなくなってしまうんですね。

そこでこの記事では、ゲーム画面内に半透明の黒いログウィンドウを表示し、Debug.Log()print() の内容をそのままゲーム内テキストに出すコンポーネント
「LogConsole」 を作っていきます。

【Unity】ゲーム内デバッグを快適に!「LogConsole」コンポーネント

「LogConsole」は、Unity の Application.logMessageReceived をフックして、
エディタの Console に流れるログをそのままゲーム画面内の UI Text に流し込むコンポーネントです。

  • 半透明の黒背景+白文字
  • ログの行数を制限(古いログから自動で削除)
  • 特定キーで表示/非表示のトグル

といった機能を一つのコンポーネントにまとめることで、
プレイヤーの動きや敵 AI の状態を、ビルドしたゲーム上でそのまま観察できるようにしていきましょう。


ソースコード全文:LogConsole.cs


using System.Collections.Generic;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.InputSystem;

/// <summary> 
/// 画面内に半透明の黒窓を出し、Debug.Log / print の内容を表示するコンポーネント。
/// - Canvas / Image / Text を自動生成してくれる
/// - 指定キーで表示/非表示を切り替え可能
/// - 最大行数を超えたログは古いものから自動削除
/// 
/// ※ Unity6 + New Input System 想定
/// </summary>
[DisallowMultipleComponent]
public class LogConsole : MonoBehaviour
{
    [Header("見た目設定")]
    [SerializeField]
    private Vector2 consoleSize = new Vector2(600f, 300f); // ログウィンドウのサイズ

    [SerializeField]
    private Vector2 consoleAnchorMin = new Vector2(0f, 0f); // 画面左下
    [SerializeField]
    private Vector2 consoleAnchorMax = new Vector2(0f, 0f);

    [SerializeField]
    private Vector2 consolePivot = new Vector2(0f, 0f); // 左下基準

    [SerializeField]
    [Range(0f, 1f)]
    private float backgroundAlpha = 0.6f; // 背景の透明度

    [SerializeField]
    private Color textColor = Color.white;

    [SerializeField]
    private int fontSize = 16;

    [Header("ログ設定")]
    [SerializeField]
    [Min(1)]
    private int maxLines = 100; // 表示する最大行数

    [SerializeField]
    private bool showStackTraceOnError = true; // エラー時にスタックトレースを表示するか

    [Header("操作設定")]
    [SerializeField]
    private Key toggleKey = Key.Backquote; // 表示/非表示を切り替えるキー(` キー)

    // 内部参照
    private Canvas _canvas;
    private RectTransform _panelRect;
    private Image _backgroundImage;
    private Text _logText;

    // ログのバッファ
    private readonly List<string> _lines = new List<string>();
    private readonly StringBuilder _builder = new StringBuilder();

    // 現在の表示状態
    private bool _visible = true;

    // InputSystem のキーボード参照
    private Keyboard _keyboard;

    private void Awake()
    {
        // キーボードの取得(New Input System)
        _keyboard = Keyboard.current;

        // すでに Canvas が子にあるか探す(再利用できる場合は再利用)
        _canvas = GetComponentInChildren<Canvas>();

        if (_canvas == null)
        {
            // Canvas がなければ新規作成
            GameObject canvasObj = new GameObject("LogConsoleCanvas");
            canvasObj.transform.SetParent(transform, false);

            _canvas = canvasObj.AddComponent<Canvas>();
            _canvas.renderMode = RenderMode.ScreenSpaceOverlay;
            _canvas.sortingOrder = 9999; // 一番前に出す想定

            canvasObj.AddComponent<CanvasScaler>();
            canvasObj.AddComponent<GraphicRaycaster>();
        }

        // パネル(背景)を作成
        GameObject panelObj = new GameObject("LogConsolePanel");
        panelObj.transform.SetParent(_canvas.transform, false);

        _panelRect = panelObj.AddComponent<RectTransform>();
        _backgroundImage = panelObj.AddComponent<Image>();

        // 黒背景+アルファ
        Color bgColor = Color.black;
        bgColor.a = backgroundAlpha;
        _backgroundImage.color = bgColor;

        // アンカー・ピボット・サイズ設定
        _panelRect.anchorMin = consoleAnchorMin;
        _panelRect.anchorMax = consoleAnchorMax;
        _panelRect.pivot = consolePivot;
        _panelRect.sizeDelta = consoleSize;
        _panelRect.anchoredPosition = Vector2.zero;

        // ログ表示用 Text を作成
        GameObject textObj = new GameObject("LogText");
        textObj.transform.SetParent(panelObj.transform, false);

        RectTransform textRect = textObj.AddComponent<RectTransform>();
        _logText = textObj.AddComponent<Text>();

        // Text のレイアウト(パネルいっぱいに広げる)
        textRect.anchorMin = new Vector2(0f, 0f);
        textRect.anchorMax = new Vector2(1f, 1f);
        textRect.pivot = new Vector2(0.5f, 0.5f);
        textRect.offsetMin = new Vector2(10f, 10f); // 余白
        textRect.offsetMax = new Vector2(-10f, -10f);

        // Text の見た目設定
        _logText.color = textColor;
        _logText.fontSize = fontSize;
        _logText.alignment = TextAnchor.LowerLeft;
        _logText.horizontalOverflow = HorizontalWrapMode.Wrap;
        _logText.verticalOverflow = VerticalWrapMode.Truncate;

        // デフォルトフォントを設定(エディタでもランタイムでも動くように)
        _logText.font = Resources.GetBuiltinResource<Font>("Arial.ttf");

        // 初期状態は空
        _logText.text = string.Empty;

        // 表示 ON
        SetVisible(_visible);
    }

    private void OnEnable()
    {
        // ログ受け取りイベントに登録
        Application.logMessageReceived += HandleLog;
    }

    private void OnDisable()
    {
        // ログ受け取りイベントから解除
        Application.logMessageReceived -= HandleLog;
    }

    private void Update()
    {
        // トグルキーが押されたら表示/非表示を切り替え
        if (_keyboard != null && _keyboard[toggleKey].wasPressedThisFrame)
        {
            _visible = !_visible;
            SetVisible(_visible);
        }
    }

    /// <summary>
    /// Unity のログが発行されたときに呼ばれるコールバック。
    /// </summary>
    private void HandleLog(string logString, string stackTrace, LogType type)
    {
        // ログのプレフィックスを付ける
        string prefix = type switch
        {
            LogType.Error => "[Error] ",
            LogType.Assert => "[Assert] ",
            LogType.Warning => "[Warning] ",
            LogType.Log => "[Log] ",
            LogType.Exception => "[Exception] ",
            _ => "[Log] "
        };

        // エラーや例外の場合は色を変えたいところだが、
        // 今回は Text コンポーネント一つでシンプルに行くため、プレフィックスのみで区別。
        string line = prefix + logString;

        // 必要ならスタックトレースも追加
        if (showStackTraceOnError && (type == LogType.Error || type == LogType.Exception))
        {
            line += "\n" + stackTrace;
        }

        AppendLine(line);
    }

    /// <summary>
    /// ログ行を追加し、最大行数を超えた場合は古い行から削除する。
    /// </summary>
    private void AppendLine(string line)
    {
        _lines.Add(line);

        // 行数制限
        while (_lines.Count > maxLines)
        {
            _lines.RemoveAt(0);
        }

        // StringBuilder に再構築
        _builder.Clear();
        for (int i = 0; i < _lines.Count; i++)
        {
            _builder.AppendLine(_lines[i]);
        }

        _logText.text = _builder.ToString();
    }

    /// <summary>
    /// コンソールの表示/非表示を切り替える。
    /// </summary>
    private void SetVisible(bool visible)
    {
        if (_panelRect != null)
        {
            _panelRect.gameObject.SetActive(visible);
        }
    }
}

使い方の手順

ここでは、プレイヤーや敵の挙動をログで確認する具体例を交えながら手順を説明します。

  1. スクリプトをプロジェクトに追加
    • 上記の LogConsole.cs をそのままコピーし、
      プロジェクトの Assets フォルダ内に保存します。
    • ファイル名は LogConsole.cs にしてください(クラス名と一致させる)。
  2. シーンに LogConsole を配置
    • 空の GameObject を作成し、名前を LogConsole などにします。
    • その GameObject に LogConsole コンポーネントをアタッチします。
    • 再生すると、自動的に Canvas / パネル / Text が生成され、
      画面左下に半透明の黒いログウィンドウが出るはずです。
  3. ログを出したいスクリプトで Debug.Log / print を使う

    例えば、プレイヤーがジャンプした瞬間をログに出したい場合:

    
    using UnityEngine;
    
    public class PlayerJumpLogger : MonoBehaviour
    {
        [SerializeField] private KeyCode jumpKey = KeyCode.Space;
    
        private void Update()
        {
            if (Input.GetKeyDown(jumpKey))
            {
                // 実際のジャンプ処理(例)
                // Jump();
    
                // ログ出力(LogConsole が拾って画面に表示してくれる)
                print($"Jump! time = {Time.time:F2}");
            }
        }
    }
        

    このスクリプトをプレイヤーにアタッチしてゲームを再生すると、
    スペースキーを押したタイミングで、画面左下の LogConsole にログが流れていきます。

  4. 表示/非表示の切り替えと見た目の調整
    • ゲーム実行中に `(バッククォート)キー を押すと、ログウィンドウの表示/非表示を切り替えられます。
    • LogConsole コンポーネントのインスペクタから:
      • Console Size … ウィンドウのサイズ(例:800×400 にして大きめに)
      • Background Alpha … 背景の透明度(0.3〜0.8 あたりで調整)
      • Font Size … テキストの大きさ
      • Max Lines … 保持するログ行数(多すぎると重くなるのでほどほどに)
    • 敵 AI の状態確認などに使う場合は、
      敵のスクリプト内で Debug.Log("Enemy state: Patrol") のように出力しておけば、
      ビルド後の実機でも挙動を追いやすくなります。

メリットと応用

LogConsole を入れておくと、開発中のプレハブやレベルデザインがかなり楽になります。

  • ビルド後でもログが見えるので、スマホ・コンソール・スタンドアロンなど、
    エディタ外の環境での不具合調査がしやすくなります。
  • ログ表示のために毎回 UI を組む必要がなく、1コンポーネントをシーンに置くだけなので、
    プレハブをまたいで共通のデバッグ環境を簡単に用意できます。
  • 「巨大なデバッグマネージャー」を作らず、ログ表示という単一責務だけを持つコンポーネントとして独立しているので、
    他のシステムと疎結合に保てます。
  • レベルデザイン時に、敵のスポーンタイミングやトリガーの発火タイミングをログで可視化できるので、
    「なぜここで敵が出ないのか?」といった調査がしやすくなります。

さらに、LogConsole を少し改造して、任意のタイミングでログをクリアする機能を追加することもできます。

例えば、F1 キーでログを消す関数を追加する例:


    // LogConsole クラスの中に追記する例
    private void LateUpdate()
    {
        // F1 キーが押されたらログをクリア
        if (Keyboard.current != null && Keyboard.current.f1Key.wasPressedThisFrame)
        {
            ClearLogs();
        }
    }

    /// <summary>
    /// 画面内のログと内部バッファをすべてクリアする。
    /// </summary>
    private void ClearLogs()
    {
        _lines.Clear();
        _builder.Clear();
        _logText.text = string.Empty;
    }

このように、小さな責務のコンポーネントとして LogConsole を用意しておくと、
プロジェクトごとに必要な機能だけを少しずつ足していくことができます。
巨大な「なんでも屋」クラスにせず、「ログ表示担当」コンポーネントとして育てていきましょう。