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): // Pass in simple text to display DebugStreamer.message = “Hello world!”; // Or send variables DebugStreamer.message = (“Current time is: “ + Time.time); // Gaps can be added into the stream by passing an empty string DebugStreamer.message = “”;
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 = "".
Nice! Thank you guys very usefull - especiatly that C# version. Is here any option how to let text disappear after for example 5seconds?
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.
Many thanks, this helped me catch a divide by zero error that only showed up on initialization, and only affected Android.
I updated the C# implementation to take multiple strings per update cycle. The following replaces the DebugStreamer.message = "text to display" semantic. Code (csharp): DebugStreamer.messages.Add("text to display");
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.
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
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!
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.
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.
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): using UnityEngine; using System.Collections.Generic; [RequireComponent (typeof (GUIText))] public class ScreenPrinter : MonoBehaviour { public TextAnchor anchorAt = TextAnchor.LowerLeft; public int numberOfLines = 5; public int pixelOffset = 5; static ScreenPrinter defaultPrinter = null; static bool quitting = false; List<string> newMessages = new List<string>(); TextAnchor _anchorAt; float _pixelOffset; List<string> messageHistory = new List<string>(); // static Print method: finds a ScreenPrinter in the project, // or creates one if necessary, and prints to that. public static void Print(object message) { if (quitting) return; // don't try to print while quitting if (!defaultPrinter) { GameObject gob = GameObject.Find("Screen Printer"); if (!gob) gob = new GameObject("Screen Printer"); defaultPrinter = gob.GetComponent<ScreenPrinter>(); if (!defaultPrinter) defaultPrinter = gob.AddComponent<ScreenPrinter>(); } defaultPrinter.LocalPrint(message); } // member LocalPrint method: prints to this particular screen printer. // Called LocalPrint because C# won't let us use the same name for both // static and instance method. Grr. Argh. >:( public void LocalPrint(object message) { if (quitting) return; // don't try to print while quiting newMessages.Add(message.ToString()); } void Awake() { if (!guiText) { gameObject.AddComponent("GUIText"); transform.position = Vector3.zero; transform.localScale = new Vector3(0, 0, 1); } _anchorAt = anchorAt; UpdatePosition(); } void OnApplicationQuitting() { quitting = true; } void Update() { // if anchorAt or pixelOffset has changed while running, update the text position if (_anchorAt!=anchorAt || _pixelOffset!=pixelOffset) { _anchorAt = anchorAt; _pixelOffset = pixelOffset; UpdatePosition(); } // if the message has changed, update the display if (newMessages.Count > 0) { for (int messageIndex = 0; messageIndex < newMessages.Count; messageIndex++) { messageHistory.Add(newMessages[messageIndex]); } if (messageHistory.Count > numberOfLines) { messageHistory.RemoveRange(0, messageHistory.Count - numberOfLines); } // create the multi-line text to display guiText.text = string.Join("\n", messageHistory.ToArray()); newMessages.Clear(); } } void UpdatePosition() { switch(anchorAt) { case TextAnchor.UpperLeft: transform.position = new Vector3(0.0f, 1.0f, 0.0f); guiText.anchor = anchorAt; guiText.alignment = TextAlignment.Left; guiText.pixelOffset = new Vector2(pixelOffset, -pixelOffset); break; case TextAnchor.UpperCenter: transform.position = new Vector3(0.5f, 1.0f, 0.0f); guiText.anchor = anchorAt; guiText.alignment = TextAlignment.Center; guiText.pixelOffset = new Vector2(0, -pixelOffset); break; case TextAnchor.UpperRight: transform.position = new Vector3(1.0f, 1.0f, 0.0f); guiText.anchor = anchorAt; guiText.alignment = TextAlignment.Right; guiText.pixelOffset = new Vector2(-pixelOffset, -pixelOffset); break; case TextAnchor.MiddleLeft: transform.position = new Vector3(0.0f, 0.5f, 0.0f); guiText.anchor = anchorAt; guiText.alignment = TextAlignment.Left; guiText.pixelOffset = new Vector2(pixelOffset, 0.0f); break; case TextAnchor.MiddleCenter: transform.position = new Vector3(0.5f, 0.5f, 0.0f); guiText.anchor = anchorAt; guiText.alignment = TextAlignment.Center; guiText.pixelOffset = new Vector2(0, 0); break; case TextAnchor.MiddleRight: transform.position = new Vector3(1.0f, 0.5f, 0.0f); guiText.anchor = anchorAt; guiText.alignment = TextAlignment.Right; guiText.pixelOffset = new Vector2(-pixelOffset, 0.0f); break; case TextAnchor.LowerLeft: transform.position = new Vector3(0.0f, 0.0f, 0.0f); guiText.anchor = anchorAt; guiText.alignment = TextAlignment.Left; guiText.pixelOffset = new Vector2(pixelOffset, pixelOffset); break; case TextAnchor.LowerCenter: transform.position = new Vector3(0.5f, 0.0f, 0.0f); guiText.anchor = anchorAt; guiText.alignment = TextAlignment.Center; guiText.pixelOffset = new Vector2(0, pixelOffset); break; case TextAnchor.LowerRight: transform.position = new Vector3(1.0f, 0.0f, 0.0f); guiText.anchor = anchorAt; guiText.alignment = TextAlignment.Right; guiText.pixelOffset = new Vector2(-pixelOffset, pixelOffset); break; } } } I hope you find this useful, amusing, or suitable for wrapping fish. Cheers, - Joe
Even though this was made some time ago, THANKS! this is so perfect and should totally get some more attention Sorry for bumping an old thread and if another tool like this, more known already is in use.