Search Unity

On Screen Debug Log Streamer

Discussion in 'Scripting' started by Toxic Blob, Feb 2, 2011.

  1. Toxic Blob

    Toxic Blob

    Joined:
    Jun 8, 2010
    Posts:
    18
    For the past few months I’ve been teaching myself Unity, JavaScript game development with the goal of producing an iPad game. Unity Answers and this forum has been superbly wonderful in helping to answer my questions - more often than not the questions have already been asked and answered by others. So I thought I’d give something back today that I developed and have found useful.

    Often while playing with the iPad compiled version of my game, I’ve found it awkward to switch my gaze from the iPad screen to the XCode screen to check Debug.Log messages. Additionally, those debug messages contain extra information which made seeing what I wanted to see harder. So I wrote a debug log streamer that displays a multi-line history of the information passed to it.

    This script has helped me debug my variables much faster and I hope others find this as useful as I have.

    Just add this script to any game object, then from another script send the DebugStreamer messages:

    Code (csharp):
    1. // Pass in simple text to display
    2. DebugStreamer.message = “Hello world!;
    3.  
    4. // Or send variables
    5. DebugStreamer.message = (“Current time is:+ Time.time);
    6.  
    7. // Gaps can be added into the stream by passing an empty string
    8. DebugStreamer.message = “”;
     

    Attached Files:

    arinu3d likes this.
  2. _Adriaan

    _Adriaan

    Joined:
    Nov 12, 2009
    Posts:
    417
    Thank you for this, my friend :)
     
  3. ankitgoel177

    ankitgoel177

    Joined:
    Jan 28, 2011
    Posts:
    5
    Hi guys, this is the same debug log streamer but in c#.
    Same syntax applies as above to access the debugger (i.e. DebugStreamer.message = "";).
     

    Attached Files:

    arinu3d likes this.
  4. CartwheelGames

    CartwheelGames

    Joined:
    May 24, 2011
    Posts:
    29
    Thanks jamiem and ankitgoel177!
     
  5. matis1989

    matis1989

    Joined:
    Mar 23, 2011
    Posts:
    162
    Nice! Thank you guys very usefull - especiatly that C# version. Is here any option how to let text disappear after for example 5seconds?
     
  6. ina

    ina

    Joined:
    Nov 15, 2010
    Posts:
    799
    @jamiem: thanks for this resource!

    @matis try using itween for each text fade
     
  7. DethRaid

    DethRaid

    Joined:
    Aug 29, 2010
    Posts:
    209
    I just added this to my project and it's amazing. No more pouring through output logs to test my builds! (I need to test a built version of my game as all the important stuff needs two or more players connected)

    This script is amazing, thank you again.
     
  8. hakermania

    hakermania

    Joined:
    Aug 27, 2012
    Posts:
    9
    Thanks a whole lot for this :)
     
  9. Nicholas Rishel

    Nicholas Rishel

    Joined:
    May 7, 2013
    Posts:
    16
    Many thanks, this helped me catch a divide by zero error that only showed up on initialization, and only affected Android. :)
     
  10. Nicholas Rishel

    Nicholas Rishel

    Joined:
    May 7, 2013
    Posts:
    16
    I updated the C# implementation to take multiple strings per update cycle. The following replaces the DebugStreamer.message = "text to display" semantic.
    Code (csharp):
    1. DebugStreamer.messages.Add("text to display");
     

    Attached Files:

    Last edited: Jun 12, 2013
  11. Nicholas Rishel

    Nicholas Rishel

    Joined:
    May 7, 2013
    Posts:
    16
    Added a couple updates to this script.
    View attachment $DebugStreamer.cs

    • Now call AddMessage(stuff) instead of message = stuff.
    • You don't have to call stuff.ToString() anymore, AddMessage() calls that for you.
    • You don't have to add this to an object in the scene, AddMessage() does that for you.
     
    Last edited: Dec 8, 2013
  12. nsxNawe

    nsxNawe

    Joined:
    Jul 15, 2012
    Posts:
    3
    This came really handy :) it worked flawlessly.

    Only a minor detail, I had to add the line:
    "guiObj = new GameObject ();"
    before "guiObj.AddComponent("GUIText");" in the Awake call. Otherwise it was throwing a null pointer.

    Thank you so much!,

    Cheers
     
  13. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    8,062
    I agree, this is really nice. I may even start using it while working in the editor, as there are some team projects I'm on that have cluttered the console log with a constant stream of spew (and I can find no way to search or filter there).

    I'm inclined to change the nomenclature a little, though; AddMessage seems to do the same thing as Debug.Log, so why not name it Log? Then all you have to do is change an existing Debug.Log to DebugStreamer.Log, and you're all set. (And since I'm nitpicking, OnScreenDebug or DebugOnScreen makes more sense to me than DebugStreamer, but hey, whatever! ;))

    Thanks to Toxic for getting this started, and everyone else for contributing!
     
  14. Nicholas Rishel

    Nicholas Rishel

    Joined:
    May 7, 2013
    Posts:
    16
    That's because you added it to the scene. You should not do this, it is added to the scene when you call AddMessage(...) if you're using my last update.

    I'll add a warning for that in a future update, maybe make a git repo so that there aren't 10 versions of this script floating around.
     
    Last edited: Dec 17, 2013
  15. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    8,062
    You should be able to use it both ways. If you rely solely on dynamic creation through the static method, then you have no easy control over properties like text color. If your game has a mostly white background, then it would make sense to add the debug streamer to a scene, add a GUIText component to it, and tweak the GUIText properties there.

    So, like most singleton-ish objects, the static accessor should find the one in the scene if it's there, creating one only if it can't find one.
     
  16. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    8,062
    So as to not be merely a mooch, here's my version... you can add a Screen Printer object to the scene, attach this script, and configure the GUIText as needed; or you can just let it be created automatically with default attributes. In either case, you can print to the default instance with ScreenPrinter.Print; or you can have a reference to a ScreenPrinter component, and call LocalPrint on that directly. This lets you have several screen-printing areas with different positions and attributes, if needed.

    Code (csharp):
    1. using UnityEngine;
    2. using System.Collections.Generic;
    3.  
    4. [RequireComponent (typeof (GUIText))]
    5.  
    6. public class ScreenPrinter : MonoBehaviour {
    7.     public TextAnchor anchorAt = TextAnchor.LowerLeft;
    8.     public int numberOfLines = 5;
    9.     public int pixelOffset = 5;
    10.    
    11.     static ScreenPrinter defaultPrinter = null;
    12.     static bool quitting = false;
    13.  
    14.     List<string> newMessages = new List<string>();
    15.     TextAnchor _anchorAt;
    16.     float _pixelOffset;
    17.     List<string> messageHistory = new List<string>();
    18.  
    19.     // static Print method: finds a ScreenPrinter in the project,
    20.     // or creates one if necessary, and prints to that.
    21.     public static void Print(object message) {
    22.         if (quitting) return;       // don't try to print while quitting
    23.         if (!defaultPrinter) {
    24.             GameObject gob = GameObject.Find("Screen Printer");
    25.             if (!gob) gob = new GameObject("Screen Printer");
    26.             defaultPrinter = gob.GetComponent<ScreenPrinter>();
    27.             if (!defaultPrinter) defaultPrinter = gob.AddComponent<ScreenPrinter>();
    28.         }
    29.         defaultPrinter.LocalPrint(message);
    30.     }
    31.  
    32.     // member LocalPrint method: prints to this particular screen printer.
    33.     // Called LocalPrint because C# won't let us use the same name for both
    34.     // static and instance method.  Grr.  Argh.  >:(
    35.     public void LocalPrint(object message) {
    36.         if (quitting) return;       // don't try to print while quiting
    37.         newMessages.Add(message.ToString());
    38.     }
    39.  
    40.     void Awake() {
    41.         if (!guiText) {
    42.             gameObject.AddComponent("GUIText");
    43.             transform.position = Vector3.zero;
    44.             transform.localScale = new Vector3(0, 0, 1);
    45.         }
    46.         _anchorAt = anchorAt;
    47.         UpdatePosition();
    48.     }
    49.  
    50.     void OnApplicationQuitting() {
    51.         quitting = true;
    52.     }
    53.  
    54.     void Update() {
    55.         // if anchorAt or pixelOffset has changed while running, update the text position
    56.         if (_anchorAt!=anchorAt || _pixelOffset!=pixelOffset) {
    57.             _anchorAt = anchorAt;
    58.             _pixelOffset = pixelOffset;
    59.             UpdatePosition();
    60.         }
    61.            
    62.         //  if the message has changed, update the display
    63.         if (newMessages.Count > 0) {
    64.             for (int messageIndex = 0; messageIndex < newMessages.Count; messageIndex++) {
    65.                 messageHistory.Add(newMessages[messageIndex]);
    66.             }
    67.             if (messageHistory.Count > numberOfLines) {
    68.                 messageHistory.RemoveRange(0, messageHistory.Count - numberOfLines);
    69.             }
    70.  
    71.             //  create the multi-line text to display
    72.             guiText.text = string.Join("\n", messageHistory.ToArray());
    73.             newMessages.Clear();
    74.         }
    75.     }
    76.  
    77.     void UpdatePosition() {
    78.         switch(anchorAt) {
    79.         case TextAnchor.UpperLeft:
    80.             transform.position = new Vector3(0.0f, 1.0f, 0.0f);
    81.             guiText.anchor = anchorAt;
    82.             guiText.alignment = TextAlignment.Left;
    83.             guiText.pixelOffset = new Vector2(pixelOffset, -pixelOffset);
    84.             break;
    85.         case TextAnchor.UpperCenter:
    86.             transform.position = new Vector3(0.5f, 1.0f, 0.0f);
    87.             guiText.anchor = anchorAt;
    88.             guiText.alignment = TextAlignment.Center;
    89.             guiText.pixelOffset = new Vector2(0, -pixelOffset);
    90.             break;
    91.         case TextAnchor.UpperRight:
    92.             transform.position = new Vector3(1.0f, 1.0f, 0.0f);
    93.             guiText.anchor = anchorAt;
    94.             guiText.alignment = TextAlignment.Right;
    95.             guiText.pixelOffset = new Vector2(-pixelOffset, -pixelOffset);
    96.             break;
    97.         case TextAnchor.MiddleLeft:
    98.             transform.position = new Vector3(0.0f, 0.5f, 0.0f);
    99.             guiText.anchor = anchorAt;
    100.             guiText.alignment = TextAlignment.Left;
    101.             guiText.pixelOffset = new Vector2(pixelOffset, 0.0f);
    102.             break;
    103.         case TextAnchor.MiddleCenter:
    104.             transform.position = new Vector3(0.5f, 0.5f, 0.0f);
    105.             guiText.anchor = anchorAt;
    106.             guiText.alignment = TextAlignment.Center;
    107.             guiText.pixelOffset = new Vector2(0, 0);
    108.             break;
    109.         case TextAnchor.MiddleRight:
    110.             transform.position = new Vector3(1.0f, 0.5f, 0.0f);
    111.             guiText.anchor = anchorAt;
    112.             guiText.alignment = TextAlignment.Right;
    113.             guiText.pixelOffset = new Vector2(-pixelOffset, 0.0f);
    114.             break;
    115.         case TextAnchor.LowerLeft:
    116.             transform.position = new Vector3(0.0f, 0.0f, 0.0f);
    117.             guiText.anchor = anchorAt;
    118.             guiText.alignment = TextAlignment.Left;
    119.             guiText.pixelOffset = new Vector2(pixelOffset, pixelOffset);
    120.             break;
    121.         case TextAnchor.LowerCenter:
    122.             transform.position = new Vector3(0.5f, 0.0f, 0.0f);
    123.             guiText.anchor = anchorAt;
    124.             guiText.alignment = TextAlignment.Center;
    125.             guiText.pixelOffset = new Vector2(0, pixelOffset);
    126.             break;
    127.         case TextAnchor.LowerRight:
    128.             transform.position = new Vector3(1.0f, 0.0f, 0.0f);
    129.             guiText.anchor = anchorAt;
    130.             guiText.alignment = TextAlignment.Right;
    131.             guiText.pixelOffset = new Vector2(-pixelOffset, pixelOffset);
    132.             break;
    133.         }
    134.     }
    135. }
    I hope you find this useful, amusing, or suitable for wrapping fish.

    Cheers,
    - Joe
     
    tosiabunio likes this.
  17. FrankHHansen

    FrankHHansen

    Joined:
    Jan 14, 2014
    Posts:
    10
    Even though this was made some time ago, THANKS! this is so perfect and should totally get some more attention :D

    Sorry for bumping an old thread and if another tool like this, more known already is in use.
     
    Lysander and JoeStrout like this.