Hydrogen Framework  1.3.1
 All Classes Namespaces Functions Variables Enumerations Enumerator Properties Events
hDebug.cs
1 #region Copyright Notice & License Information
2 //
3 // hDebug.cs
4 //
5 // Author:
6 // Matthew Davey <matthew.davey@dotbunny.com>
7 // Robin Southern <betajaen@ihoed.com>
8 //
9 // Copyright (c) 2014 dotBunny Inc. (http://www.dotbunny.com)
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining a copy
12 // of this software and associated documentation files (the "Software"), to deal
13 // in the Software without restriction, including without limitation the rights
14 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 // copies of the Software, and to permit persons to whom the Software is
16 // furnished to do so, subject to the following conditions:
17 //
18 // The above copyright notice and this permission notice shall be included in
19 // all copies or substantial portions of the Software.
20 //
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27 // THE SOFTWARE.
28 #endregion
29 
30 using System.Collections.Generic;
31 using UnityEngine;
32 
33 /// <summary>
34 /// An in-game debugging system to make every developer happy.
35 /// </summary>
36 /// <remarks>This component does not follow our normal component/base model.</remarks>
37 /// TODO: This is getting massively reworked.
38 public class hDebug : MonoBehaviour
39 {
40  /// <summary>
41  /// Height of a character from our fontImage.
42  /// </summary>
43  public const int GlyphHeight = 8;
44  /// <summary>
45  /// Width of a character from our fontImage.
46  /// </summary>
47  public const int GlyphWidth = 8;
48  /// <summary>
49  /// The consoles output spacing between lines.
50  /// </summary>
51  const int ConsoleLineSpacing = 7;
52  /// <summary>
53  /// The max number of lines to display at any one time in the console.
54  /// </summary>
55  const int ConsoleMaxDisplayedLines = 24;
56  /// <summary>
57  /// The maximum number of lines stored in the consoles list.
58  /// </summary>
59  const int ConsoleMaxLines = 10000;
60  /// <summary>
61  /// How much space should be around the consoles output.
62  /// </summary>
63  const int ConsolePadding = 5;
64  /// <summary>
65  /// When outputting to Unity's debug log, what should entries be prefixed with.
66  /// </summary>
67  /// <remarks>This is used to prevent double dipping.</remarks>
68  const string ConsoleUnityPrefix = "[h] ";
69  /// <summary>
70  /// Debug Shader
71  /// </summary>
72  const string ShaderText = "Shader \"hDebug/Text\"\r\n{\r\nProperties\r\n{\r\n_MainTex (\"Main\", 2D) = \"white\" {}\r\n}\r\n\r\nCategory\r\n{\r\n Tags\r\n {\r\n \"Queue\" = \"Transparent\"\r\n }\r\n \r\n Blend SrcAlpha OneMinusSrcAlpha\r\n AlphaTest Greater .01\r\n ColorMask RGB\r\n Cull Off\r\n Lighting Off\r\n ZWrite On\r\n \r\n Fog\r\n {\r\n Color(0, 0, 0, 0)\r\n }\r\n \r\n BindChannels\r\n {\r\n Bind \"Color\", color\r\n Bind \"Vertex\", vertex\r\n Bind \"TexCoord\", texcoord\r\n }\r\n \r\n SubShader\r\n {\r\n Pass\r\n {\r\n SetTexture [_MainTex]\r\n {\r\n combine texture * primary\r\n }\r\n }\r\n }\r\n}\r\n}";
73  /// <summary>
74  /// The space around the boxed outputs in stats mode.
75  /// </summary>
76  const int StatsPadding = 10;
77  /// <summary>
78  /// The space between items when outputting in stats mode.
79  /// </summary>
80  const int StatsLineSpacing = 7;
81  /// <summary>
82  /// Should we monitor for input?
83  /// </summary>
84  /// <remarks>
85  /// Turn this off if your binding your own keys to the input methods.
86  /// </remarks>
87  public bool CheckForInput = true;
88  /// <summary>
89  /// Where should the console be displayed on the screen.
90  /// </summary>
91  /// <remarks>Top or Bottom?</remarks>
92  public DisplayLocation Location = DisplayLocation.Top;
93  /// <summary>
94  /// What should be displayed? Stats or Console?
95  /// </summary>
96  public DisplayMode Mode = DisplayMode.Off;
97  /// <summary>
98  /// What key should be used to toggle between modes.
99  /// </summary>
100  /// <remarks>Available where Keyboard input is received.</remarks>
101  public KeyCode ToggleKey = KeyCode.BackQuote;
102  /// <summary>
103  /// Internal fail safe to maintain instance across threads.
104  /// </summary>
105  /// <remarks>
106  /// Multithreaded Safe Singleton Pattern.
107  /// </remarks>
108  /// <description>
109  /// http://msdn.microsoft.com/en-us/library/ms998558.aspx
110  /// </description>
111  static readonly System.Object _syncRoot = new System.Object ();
112  /// <summary>
113  /// Internal reference to the static instance of the object pool.
114  /// </summary>
115  static volatile hDebug _staticInstance;
116  public static Color Shadow = Color.black;
117  public static bool EchoToUnityLog = true;
118  static readonly char[] _newLineCharacters = new char[] { '\n', '\r' };
119  /// <summary>
120  /// The font to be rendered to the screen, stored as a byte array.
121  /// </summary>
122  /// <remarks>It's a black and white image stored as a 1-bit PNG. Genius Robin! Genius!</remarks>
123  static readonly byte[] _fontImageData = {
124  0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52,
125  0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x01, 0x03, 0x00, 0x00, 0x00, 0xF9, 0xF0, 0xF3,
126  0x88, 0x00, 0x00, 0x00, 0x06, 0x50, 0x4C, 0x54, 0x45, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x55,
127  0x7C, 0xF5, 0x6C, 0x00, 0x00, 0x00, 0x01, 0x74, 0x52, 0x4E, 0x53, 0x00, 0x40, 0xE6, 0xD8, 0x66,
128  0x00, 0x00, 0x02, 0x15, 0x49, 0x44, 0x41, 0x54, 0x78, 0x5E, 0xED, 0xD2, 0x41, 0x8A, 0xD5, 0x40,
129  0x10, 0x06, 0xE0, 0x5A, 0xF5, 0xAA, 0x70, 0x5D, 0x60, 0x78, 0x27, 0x70, 0x51, 0x30, 0x12, 0x1F,
130  0x4C, 0x91, 0x8B, 0xB8, 0x29, 0x18, 0xF8, 0x5D, 0xBC, 0xF0, 0x66, 0x44, 0x18, 0x03, 0x36, 0xFD,
131  0x6E, 0xE0, 0x99, 0x04, 0x0F, 0xE0, 0x56, 0x9C, 0x4B, 0x78, 0x03, 0xAD, 0xEE, 0x24, 0x4E, 0x66,
132  0x46, 0x17, 0xCA, 0x2C, 0x5C, 0xCC, 0xBF, 0xC8, 0xE2, 0xE3, 0x4F, 0x77, 0x85, 0x0A, 0xFD, 0x2E,
133  0xF2, 0x00, 0xAE, 0xB6, 0x20, 0xE8, 0xED, 0xEB, 0x5E, 0x58, 0x1B, 0xA4, 0x1D, 0x19, 0x7A, 0x7C,
134  0x1B, 0x45, 0x64, 0x86, 0xB3, 0x80, 0x93, 0x5F, 0x57, 0xE8, 0x2B, 0xF1, 0x2B, 0x32, 0xEA, 0x95,
135  0xF7, 0xA4, 0xDC, 0x2D, 0x20, 0xD4, 0x8B, 0x78, 0xC0, 0xA9, 0x50, 0x21, 0x39, 0xAB, 0xC0, 0x7A,
136  0xB9, 0x36, 0xE4, 0x3C, 0x1E, 0xA7, 0x74, 0x44, 0x40, 0x3B, 0x43, 0xCF, 0x6B, 0x03, 0xAF, 0x2B,
137  0x10, 0x75, 0xD4, 0x69, 0x7B, 0xC5, 0x9E, 0x87, 0x4B, 0x03, 0xDF, 0x51, 0x44, 0x9E, 0x51, 0x83,
138  0x28, 0xF9, 0xAF, 0x6F, 0x61, 0x0D, 0xA4, 0xC7, 0x0A, 0x9B, 0x69, 0xE9, 0x8A, 0x19, 0x51, 0x22,
139  0xB7, 0x42, 0x1D, 0xA0, 0x2E, 0x09, 0xA8, 0xD7, 0x2A, 0x3E, 0x53, 0x06, 0x7A, 0x57, 0x06, 0xBA,
140  0x4E, 0x48, 0x02, 0x38, 0xA5, 0xDE, 0x33, 0x5F, 0x07, 0x68, 0x61, 0xFE, 0x42, 0xCC, 0x5D, 0x9F,
141  0x21, 0x86, 0x3A, 0x66, 0x92, 0x1B, 0x62, 0x49, 0x48, 0x90, 0xE3, 0x40, 0x14, 0x8D, 0x0A, 0x8A,
142  0x53, 0x82, 0x82, 0xEB, 0xE8, 0x42, 0xF1, 0x8A, 0x23, 0x71, 0x80, 0x74, 0x1D, 0x93, 0xCA, 0x27,
143  0xE2, 0x62, 0x69, 0x32, 0xB5, 0x7D, 0xD7, 0x25, 0x72, 0x39, 0x6D, 0x47, 0x7C, 0xB8, 0xD1, 0x47,
144  0x8B, 0x64, 0x9B, 0x4A, 0x31, 0x58, 0x82, 0x5F, 0x5C, 0x58, 0x26, 0x03, 0x46, 0x77, 0x40, 0x1A,
145  0x00, 0x84, 0xC8, 0x0C, 0xA3, 0xBF, 0x7F, 0xD7, 0xC0, 0x03, 0xBC, 0xC1, 0xE1, 0x43, 0x85, 0xEC,
146  0xC8, 0xD9, 0x8B, 0xA4, 0xC9, 0x0F, 0x97, 0xC8, 0x54, 0x5A, 0xE3, 0x38, 0x37, 0xDE, 0xC0, 0x6F,
147  0xCF, 0xC0, 0x18, 0x87, 0xCE, 0x30, 0xCE, 0x80, 0x05, 0xEA, 0x1C, 0x3E, 0xC0, 0x0C, 0x25, 0xE6,
148  0x70, 0x7A, 0xF4, 0x08, 0xED, 0xEF, 0x82, 0x05, 0x59, 0xB6, 0x02, 0x5C, 0x00, 0xC5, 0xDC, 0x40,
149  0x4C, 0x00, 0xA4, 0x41, 0x52, 0x67, 0x8A, 0x00, 0x5E, 0xE1, 0x65, 0x80, 0x2E, 0xA0, 0x01, 0x07,
150  0x01, 0xAF, 0x90, 0xA5, 0x81, 0x89, 0xCA, 0x0C, 0x23, 0x57, 0x78, 0x21, 0xBA, 0x02, 0x52, 0x40,
151  0x0F, 0x71, 0xE5, 0x05, 0x20, 0xB0, 0x0D, 0x18, 0x4C, 0x4C, 0x02, 0x8A, 0x26, 0xBE, 0xBB, 0xF3,
152  0x05, 0xD2, 0x2D, 0x10, 0xDF, 0xFF, 0x10, 0xA3, 0x1F, 0xF4, 0x17, 0x11, 0x5E, 0xC1, 0x29, 0xD1,
153  0x8E, 0x5C, 0xD8, 0xA7, 0x5B, 0xD0, 0xE0, 0xB0, 0x06, 0x96, 0x6D, 0x30, 0x1D, 0xF2, 0x64, 0x90,
154  0x92, 0x2D, 0x53, 0x42, 0x44, 0x01, 0x61, 0xC8, 0x01, 0x40, 0x80, 0x03, 0xA5, 0xC2, 0x38, 0xC3,
155  0x10, 0x50, 0x5A, 0x63, 0x9A, 0x01, 0x01, 0xAE, 0x9B, 0x06, 0x22, 0xBE, 0x3D, 0x63, 0x98, 0x6F,
156  0x41, 0xE1, 0xD8, 0x3E, 0x2C, 0xD3, 0x9C, 0xB4, 0xFE, 0xFB, 0xFE, 0x27, 0x88, 0xE6, 0xB4, 0xC0,
157  0xBF, 0x45, 0x67, 0x60, 0xD1, 0xB7, 0x77, 0x40, 0x44, 0x6E, 0x02, 0x06, 0x0C, 0xCB, 0xFF, 0x11,
158  0xF0, 0x31, 0x00, 0x47, 0x57, 0xE0, 0x00, 0xA4, 0x00, 0xAA, 0x70, 0xD5, 0xC0, 0xC0, 0xEB, 0xB2,
159  0xDD, 0x2A, 0x08, 0xC4, 0x25, 0xCD, 0x90, 0xE6, 0x86, 0xD6, 0xC6, 0x0A, 0xD6, 0x03, 0xBE, 0x9C,
160  0x31, 0x78, 0xDE, 0x0D, 0xD2, 0xC3, 0x4A, 0xC0, 0x76, 0xD9, 0x4C, 0xF7, 0x40, 0xEA, 0xA4, 0x5B,
161  0xF8, 0x5E, 0xED, 0xBF, 0xC8, 0x53, 0x9E, 0xF2, 0x13, 0xCE, 0xA7, 0xD4, 0x2D, 0x81, 0x1E, 0xBF,
162  0x8C, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, 0xAE, 0x42, 0x60, 0x82
163  };
164  static readonly Color _logBackgroundColor = new Color (0.1337f, 0.1337f, 0.1337f, 0.95f);
165  static readonly List<hDebug.LogMessage> _logMessages = new List<hDebug.LogMessage> ();
166  static readonly Color[] _logTypeColors = {
167  Hydrogen.Texture.ToColor (Hydrogen.Texture.WebColor.Red, 0.95f), // Error
168  Hydrogen.Texture.ToColor (Hydrogen.Texture.WebColor.Crimson, 0.95f), // Assert
169  Hydrogen.Texture.ToColor (Hydrogen.Texture.WebColor.Orange, 0.95f), // Warning
170  Hydrogen.Texture.ToColor (Hydrogen.Texture.WebColor.White, 0.95f), // Log
171  Hydrogen.Texture.ToColor (Hydrogen.Texture.WebColor.FireBrick, 0.95f), // Exception
172  };
173  static readonly List<hDebug.PrintedText> _printedText = new List<hDebug.PrintedText> ();
174  static readonly Color _statsBackgroundColor = new Color (0.1337f, 0.1337f, 0.1337f, 0.95f);
175  static readonly Color _statsForegroundColor = new Color (1f, 1f, 1f, 0.95f);
176  static readonly Dictionary<string, WatchedItem> _watchedItems = new Dictionary<string, WatchedItem> ();
177  static int _debugLogCount;
178  Camera _fontCamera;
179  Texture2D _fontImage;
180  Material _fontMaterial;
181  float[] _glyphLeft;
182  float[] _glyphRight;
183  float[] _glyphTop;
184  float[] _glyphBottom;
185  int _logOffsetH;
186  int _logOffsetV;
187  int _watchWidth = 100;
188  int _consoleHeight = 382;
189  hDebug.UVRectangle _whiteUV;
190 
191  public enum DisplayLocation
192  {
193  Top = 1,
194  Bottom = 2,
195  }
196 
197  public enum DisplayMode
198  {
199  Off = 0,
200  Stats = 1,
201  Console = 2
202  }
203 
204  /// <summary>
205  /// Gets the console instance, creating one if none is found.
206  /// </summary>
207  /// <value>
208  /// The Object Pool.
209  /// </value>
210  public static hDebug Instance {
211  get {
212  if (_staticInstance == null) {
213  lock (_syncRoot) {
214  _staticInstance = FindObjectOfType (typeof(hDebug)) as hDebug;
215 
216  // If we don't have it, lets make it!
217  if (_staticInstance == null) {
218  var go = GameObject.Find (Hydrogen.Components.DefaultSingletonName) ??
219  new GameObject (Hydrogen.Components.DefaultSingletonName);
220 
221  go.AddComponent<hDebug> ();
222  _staticInstance = go.GetComponent<hDebug> ();
223  }
224  }
225  }
226  return _staticInstance;
227  }
228  }
229 
230  public int ConsoleHeight {
231  get { return _consoleHeight; }
232  set { _consoleHeight = value; }
233  }
234 
235  /// <summary>
236  /// Does the Console already exist?
237  /// </summary>
238  public static bool Exists ()
239  {
240  return _staticInstance != null;
241  }
242 
243  public static void Error (object obj)
244  {
245  hDebug.PushLog (obj.ToString (), LogType.Error);
246  }
247 
248  public static void Error (string text)
249  {
250  hDebug.PushLog (text, LogType.Error);
251  }
252 
253  public static void Error (string text, params object[] args)
254  {
255  hDebug.PushLog (string.Format (text, args), LogType.Error);
256  }
257 
258  public static void Initialize ()
259  {
260  var foundConsole = FindObjectOfType (typeof(hDebug)) as hDebug;
261  if (foundConsole == null) {
262  var go = GameObject.Find (Hydrogen.Components.DefaultSingletonName) ??
263  new GameObject (Hydrogen.Components.DefaultSingletonName);
264  go.AddComponent<hDebug> ();
265  _staticInstance = go.GetComponent<hDebug> ();
266  }
267  }
268 
269  public static void Log (object obj)
270  {
271  hDebug.PushLog (obj.ToString (), LogType.Log);
272  }
273 
274  public static void Log (string text)
275  {
276  hDebug.PushLog (text, LogType.Log);
277  }
278 
279  public static void Log (string text, params object[] args)
280  {
281  hDebug.PushLog (string.Format (text, args), LogType.Log);
282  }
283 
284  public static void Print (int x, int y, object obj)
285  {
286  hDebug._printedText.Add (new hDebug.PrintedText (obj.ToString (), x, y, Color.white));
287  }
288 
289  public static void Print (int x, int y, string text)
290  {
291  hDebug._printedText.Add (new hDebug.PrintedText (text, x, y, Color.white));
292  }
293 
294  public static void Print (int x, int y, string text, params object[] args)
295  {
296  hDebug._printedText.Add (new hDebug.PrintedText (string.Format (text, args), x, y, Color.white));
297  }
298 
299  public static void Print (int x, int y, Color color, object obj)
300  {
301  hDebug._printedText.Add (new hDebug.PrintedText (obj.ToString (), x, y, color));
302  }
303 
304  public static void Print (int x, int y, Color color, string text)
305  {
306  hDebug._printedText.Add (new hDebug.PrintedText (text, x, y, color));
307  }
308 
309  public static void Print (int x, int y, Color color, string text, params object[] args)
310  {
311  hDebug._printedText.Add (new hDebug.PrintedText (string.Format (text, args), x, y, color));
312  }
313 
314  public static void Warn (object obj)
315  {
316  hDebug.PushLog (obj.ToString (), LogType.Warning);
317  }
318 
319  public static void Warn (string text)
320  {
321  hDebug.PushLog (text, LogType.Warning);
322  }
323 
324  public static void Warn (string text, params object[] args)
325  {
326  hDebug.PushLog (string.Format (text, args), LogType.Warning);
327  }
328 
329  public static void Watch (string key, bool value)
330  {
331  if (_watchedItems.ContainsKey (key) && _watchedItems [key].Type == WatchedItem.ItemType.Boolean) {
332  _watchedItems [key].Data = value.ToString ();
333 
334  } else {
335  _watchedItems [key] = new WatchedItem (WatchedItem.ItemType.Boolean, key, value.ToString ());
336  }
337  }
338 
339  public static void Watch (string key, string value)
340  {
341  if (_watchedItems.ContainsKey (key) && _watchedItems [key].Type == WatchedItem.ItemType.String) {
342  _watchedItems [key].Data = value;
343  } else {
344  _watchedItems [key] = new WatchedItem (WatchedItem.ItemType.String, key, value);
345  }
346  }
347 
348  public static void Watch (string key, int value)
349  {
350  if (_watchedItems.ContainsKey (key) && _watchedItems [key].Type == WatchedItem.ItemType.Integer) {
351  _watchedItems [key].Data = value.ToString ();
352  } else {
353  _watchedItems [key] = new WatchedItem (WatchedItem.ItemType.Integer, key, value.ToString ());
354  }
355  }
356 
357  public static void Watch (string key, float value)
358  {
359  if (_watchedItems.ContainsKey (key) && _watchedItems [key].Type == WatchedItem.ItemType.Float) {
360  _watchedItems [key].Data = value.ToString ();
361  } else {
362  _watchedItems [key] = new WatchedItem (WatchedItem.ItemType.Float, key, value.ToString ());
363  }
364  }
365 
366  public static void UnWatch (string key)
367  {
368  if (_watchedItems.ContainsKey (key))
369  _watchedItems.Remove (key);
370  }
371 
372  static void PushLog (string text, LogType type)
373  {
374 
375  if (EchoToUnityLog) {
376  if (text.Substring (0, ConsoleUnityPrefix.Length) == ConsoleUnityPrefix)
377  return;
378 
379  switch (type) {
380  case LogType.Assert:
381  case LogType.Exception:
382  case LogType.Error:
383  Debug.LogError (ConsoleUnityPrefix + text);
384  break;
385  case LogType.Warning:
386  Debug.LogWarning (ConsoleUnityPrefix + text);
387  break;
388  default:
389  Debug.Log (ConsoleUnityPrefix + text);
390  break;
391  }
392  }
393 
394  if (hDebug._logMessages.Count >= ConsoleMaxLines) {
395  hDebug._logMessages.RemoveAt (0);
396  }
397 
398  foreach (var lineItem in text.Split (_newLineCharacters)) {
399  hDebug._logMessages.Add (
400  new hDebug.LogMessage (
401  "[" + Time.time.ToString ().PadRight (9, '0') + "] " + lineItem, type));
402  hDebug._debugLogCount++;
403  }
404  }
405 
406  /// <summary>
407  /// Unity's Awake Event.
408  /// </summary>
409  void Awake ()
410  {
411  Application.RegisterLogCallback (new Application.LogCallback (HandleException));
412  }
413 
414  /// <summary>
415  /// Registered Callback for Unity's own Debug.Log call.
416  /// </summary>
417  /// <param name="condition">Condition.</param>
418  /// <param name="stackTrace">Stack trace.</param>
419  /// <param name="type">Type of log even being passed.</param>
420  static void HandleException (string condition, string stackTrace, LogType type)
421  {
422  switch (type) {
423  case LogType.Error:
424  hDebug.PushLog (stackTrace, LogType.Error);
425  hDebug.PushLog (condition, LogType.Error);
426  break;
427  case LogType.Exception:
428  hDebug.PushLog (stackTrace, LogType.Exception);
429  hDebug.PushLog (condition, LogType.Exception);
430  break;
431  default:
432  hDebug.PushLog (condition, type);
433  break;
434  }
435  }
436 
437  /// <summary>
438  /// Unity's OnPostRender Event
439  /// </summary>
440  /// <remarks>This is where we make everything show up like a boss.</remarks>
441  void OnPostRender ()
442  {
443  GL.PushMatrix ();
444  _fontMaterial.SetPass (0);
445  GL.LoadPixelMatrix ();
446  GL.Begin (4);
447 
448  foreach (hDebug.PrintedText current in hDebug._printedText) {
449  PrintString (current.Text, current.X, current.Y, current.Color);
450  }
451 
452  if (Mode == DisplayMode.Stats) {
453  int itemCount = _watchedItems.Count;
454  int watchHeight = (itemCount * GlyphHeight) + (itemCount * StatsLineSpacing) + (StatsPadding * 2);
455  int lineOffset = StatsPadding;
456  int lastFrameWidth = _watchWidth;
457 
458  if (Location == DisplayLocation.Top) {
459  // Output the stats background quad
460  SolidQuad (0, 0, 92, 50, _statsBackgroundColor);
461 
462  // Framerate
463  PrintString ((1 / Time.smoothDeltaTime).ToString ("#,##0.00") + " FPS", 10, 10, _statsForegroundColor);
464 
465  // Approximated Memory
466  PrintString (string.Format ("{0:#,##0.00} MB", (System.GC.GetTotalMemory (true) / 1048576f)), 10, 25, _statsForegroundColor);
467 
468 
469  // Output the watch list background quad using the calculated width based on content.
470  if (_watchedItems.Count > 0)
471  SolidQuad (Screen.width - lastFrameWidth, 0, Screen.width, watchHeight, _statsBackgroundColor);
472 
473  // Output all WatchedItems
474  foreach (KeyValuePair<string, WatchedItem> entry in _watchedItems) {
475 
476  // Determine the width of the background quad for the future.
477  if (entry.Value.OutputPaddedSize > _watchWidth) {
478  _watchWidth = entry.Value.OutputPaddedSize;
479  }
480 
481  // Output WatchedItem
482  PrintString (entry.Value.Output, Screen.width - lastFrameWidth + StatsPadding, lineOffset, _statsForegroundColor);
483 
484  // Increment Line Offset
485  lineOffset += StatsLineSpacing + GlyphHeight;
486  }
487  } else {
488  // Output the stats background quad
489  SolidQuad (0, Screen.height - 50, 92, Screen.height, _statsBackgroundColor);
490 
491  // Framerate
492  PrintString ((1 / Time.smoothDeltaTime).ToString ("#,##0.00") + " FPS", 10, Screen.height - 40, _statsForegroundColor);
493 
494  // Approximated Memory
495  PrintString (string.Format ("{0:#,##0.00} MB", (System.GC.GetTotalMemory (true) / 1048576f)), 10, Screen.height - 25, _statsForegroundColor);
496 
497  // Determine a height offset to start outputing the watches
498  lineOffset = Screen.height - watchHeight + StatsPadding;
499 
500  // Output the watch list background quad using the calculated width based on content.
501  if (_watchedItems.Count > 0)
502  SolidQuad (Screen.width - lastFrameWidth, Screen.height - watchHeight, Screen.width, Screen.height, _statsBackgroundColor);
503 
504  // Output all WatchedItems
505  foreach (KeyValuePair<string, WatchedItem> entry in _watchedItems) {
506 
507  // Determine the width of the background quad for the future.
508  if (entry.Value.OutputPaddedSize > _watchWidth) {
509  _watchWidth = entry.Value.OutputPaddedSize;
510  }
511 
512  // Output WatchedItem
513  PrintString (entry.Value.Output, Screen.width - lastFrameWidth + StatsPadding, lineOffset, _statsForegroundColor);
514 
515  // Increment Line Offset
516  lineOffset += StatsLineSpacing + GlyphHeight;
517  }
518  }
519  } else if (Mode == DisplayMode.Console) {
520 
521  // Sorta dynamic console ... will redo
522  if (hDebug._logMessages.Count < ConsoleMaxDisplayedLines) {
523  ConsoleHeight = (ConsolePadding * 2) + (15 * hDebug._logMessages.Count) + 1;
524  } else {
525 
526  ConsoleHeight = (ConsolePadding * 2) + (15 * ConsoleMaxDisplayedLines) - 1;
527  }
528 
529 
530  int num = ConsolePadding;
531  int num2 = hDebug._logMessages.Count - _logOffsetV;
532  if (num2 > hDebug._debugLogCount) {
533  num2 = hDebug._debugLogCount;
534  } else {
535  if (num2 < ConsoleMaxDisplayedLines) {
536  num2 = ConsoleMaxDisplayedLines;
537  }
538  }
539  int num3 = num2 - ConsoleMaxDisplayedLines;
540  if (num3 < 0) {
541  num3 = 0;
542  }
543 
544  if (Location == DisplayLocation.Top) {
545  SolidQuad (0f, -4f, (float)Screen.width, ConsoleHeight, _logBackgroundColor);
546 
547  for (int i = num3; i < num2; i++) {
548  if (i >= 0 && i < hDebug._logMessages.Count) {
549  hDebug.LogMessage logMessage = hDebug._logMessages [i];
550  PrintString (logMessage.Message, ConsolePadding + (_logOffsetH * 8), num, _logTypeColors [(int)logMessage.Type]);
551  num += 15;
552  }
553  }
554 
555  string text = string.Format ("{0}-{1}", num3, num2);
556  PrintString (text, Screen.width - ConsolePadding - (text.Length * 8), ConsolePadding, Color.gray);
557  } else {
558  SolidQuad (0f, Screen.height - ConsoleHeight, (float)Screen.width, Screen.height + 4, _logBackgroundColor);
559 
560  num = Screen.height - ConsoleHeight + StatsPadding;
561  for (int i = num3; i < num2; i++) {
562  if (i >= 0 && i < hDebug._logMessages.Count) {
563  hDebug.LogMessage logMessage = hDebug._logMessages [i];
564  PrintString (logMessage.Message, ConsolePadding + _logOffsetH * 8, num, _logTypeColors [(int)logMessage.Type]);
565  num += 15;
566  }
567  }
568  string text = string.Format ("{0}-{1}", num3, num2);
569 
570  PrintString (text, Screen.width - ConsolePadding - (text.Length * 8), (Screen.height - ConsoleHeight) + ConsolePadding, Color.gray);
571  }
572 
573  }
574  GL.End ();
575  GL.PopMatrix ();
576  hDebug._printedText.Clear ();
577 
578  }
579 
580  void PrintString (string text, int x, int y, Color colour)
581  {
582  int num = x;
583  for (int i = 0; i < text.Length; i++) {
584  char c = text [i];
585  int num2 = (int)c;
586  if (num2 == 32) {
587  num += 8;
588  } else {
589  if (num2 < 33 || num2 > 127) {
590  num2 = 34;
591  }
592  num2 -= 33;
593  float num3 = (float)num;
594  int num4 = num + 8;
595  int num5 = Screen.height - y;
596  int num6 = Screen.height - (y + 15);
597  if (colour == hDebug.Shadow) {
598  GL.Color (Color.black);
599  GL.TexCoord2 (_glyphLeft [num2], _glyphBottom [num2]);
600  GL.Vertex3 (num3, (float)num6, 0f);
601  GL.Color (Color.black);
602  GL.TexCoord2 (_glyphLeft [num2], _glyphTop [num2]);
603  GL.Vertex3 (num3, (float)num5, 0f);
604  GL.Color (Color.black);
605  GL.TexCoord2 (_glyphRight [num2], _glyphTop [num2]);
606  GL.Vertex3 ((float)num4, (float)num5, 0f);
607  GL.Color (Color.black);
608  GL.TexCoord2 (_glyphLeft [num2], _glyphBottom [num2]);
609  GL.Vertex3 (num3, (float)num6, 0f);
610  GL.Color (Color.black);
611  GL.TexCoord2 (_glyphRight [num2], _glyphTop [num2]);
612  GL.Vertex3 ((float)num4, (float)num5, 0f);
613  GL.Color (Color.black);
614  GL.TexCoord2 (_glyphRight [num2], _glyphBottom [num2]);
615  GL.Vertex3 ((float)num4, (float)num6, 0f);
616  GL.Color (Color.white);
617  GL.TexCoord2 (_glyphLeft [num2], _glyphBottom [num2]);
618  GL.Vertex3 (num3 - 1f, (float)(num6 + 1), 0f);
619  GL.Color (Color.white);
620  GL.TexCoord2 (_glyphLeft [num2], _glyphTop [num2]);
621  GL.Vertex3 (num3 - 1f, (float)(num5 + 1), 0f);
622  GL.Color (Color.white);
623  GL.TexCoord2 (_glyphRight [num2], _glyphTop [num2]);
624  GL.Vertex3 ((float)(num4 - 1), (float)(num5 + 1), 0f);
625  GL.Color (Color.white);
626  GL.TexCoord2 (_glyphLeft [num2], _glyphBottom [num2]);
627  GL.Vertex3 (num3 - 1f, (float)(num6 + 1), 0f);
628  GL.Color (Color.white);
629  GL.TexCoord2 (_glyphRight [num2], _glyphTop [num2]);
630  GL.Vertex3 ((float)(num4 - 1), (float)(num5 + 1), 0f);
631  GL.Color (Color.white);
632  GL.TexCoord2 (_glyphRight [num2], _glyphBottom [num2]);
633  GL.Vertex3 ((float)(num4 - 1), (float)(num6 + 1), 0f);
634  } else {
635  GL.Color (colour);
636  GL.TexCoord2 (_glyphLeft [num2], _glyphBottom [num2]);
637  GL.Vertex3 (num3, (float)num6, 0f);
638  GL.Color (colour);
639  GL.TexCoord2 (_glyphLeft [num2], _glyphTop [num2]);
640  GL.Vertex3 (num3, (float)num5, 0f);
641  GL.Color (colour);
642  GL.TexCoord2 (_glyphRight [num2], _glyphTop [num2]);
643  GL.Vertex3 ((float)num4, (float)num5, 0f);
644  GL.Color (colour);
645  GL.TexCoord2 (_glyphLeft [num2], _glyphBottom [num2]);
646  GL.Vertex3 (num3, (float)num6, 0f);
647  GL.Color (colour);
648  GL.TexCoord2 (_glyphRight [num2], _glyphTop [num2]);
649  GL.Vertex3 ((float)num4, (float)num5, 0f);
650  GL.Color (colour);
651  GL.TexCoord2 (_glyphRight [num2], _glyphBottom [num2]);
652  GL.Vertex3 ((float)num4, (float)num6, 0f);
653  }
654  num += 8;
655  }
656  }
657  }
658 
659  void SolidQuad (float left, float top, float right, float bottom, Color color)
660  {
661  top = (float)Screen.height - top;
662  bottom = (float)Screen.height - bottom;
663  GL.Color (color);
664  GL.TexCoord2 (_whiteUV.U0, _whiteUV.V0);
665  GL.Vertex3 (left, bottom, 0f);
666  GL.Color (color);
667  GL.TexCoord2 (_whiteUV.U0, _whiteUV.V0);
668  GL.Vertex3 (left, top, 0f);
669  GL.Color (color);
670  GL.TexCoord2 (_whiteUV.U0, _whiteUV.V0);
671  GL.Vertex3 (right, top, 0f);
672  GL.Color (color);
673  GL.TexCoord2 (_whiteUV.U0, _whiteUV.V0);
674  GL.Vertex3 (left, bottom, 0f);
675  GL.Color (color);
676  GL.TexCoord2 (_whiteUV.U0, _whiteUV.V0);
677  GL.Vertex3 (right, top, 0f);
678  GL.Color (color);
679  GL.TexCoord2 (_whiteUV.U0, _whiteUV.V0);
680  GL.Vertex3 (right, bottom, 0f);
681  }
682 
683  void Start ()
684  {
685  _fontImage = new Texture2D (128, 128);
686  _fontImage.LoadImage (_fontImageData);
687  _fontImage.filterMode = 0;
688  _fontImage.wrapMode = (TextureWrapMode)1;
689  _fontImage.hideFlags = (HideFlags)13;
690  var material = new Material ("Shader \"hDebug/Text\"\r\n {\r\n Properties\r\n {\r\n _MainTex (\"Main\", 2D) = \"white\" {}\r\n }\r\n \r\n Category\r\n {\r\n Tags\r\n {\r\n \"Queue\" = \"Transparent\"\r\n }\r\n \r\n Blend SrcAlpha OneMinusSrcAlpha\r\n AlphaTest Greater .01\r\n ColorMask RGB\r\n Cull Off\r\n Lighting Off\r\n ZWrite On\r\n \r\n Fog\r\n {\r\n Color(0, 0, 0, 0)\r\n }\r\n \r\n BindChannels\r\n {\r\n Bind \"Color\", color\r\n Bind \"Vertex\", vertex\r\n Bind \"TexCoord\", texcoord\r\n }\r\n \r\n SubShader\r\n {\r\n Pass\r\n {\r\n SetTexture [_MainTex]\r\n {\r\n combine texture * primary\r\n }\r\n }\r\n }\r\n }\r\n }");
691  material.mainTexture = _fontImage;
692  material.hideFlags = (HideFlags)13;
693  material.shader.hideFlags = (HideFlags)13;
694  _fontMaterial = material;
695  _fontCamera = (Camera)gameObject.AddComponent ("Camera");
696  _fontCamera.nearClipPlane = 0.1f;
697  _fontCamera.farClipPlane = 1f;
698  _fontCamera.clearFlags = (CameraClearFlags)4;
699  _fontCamera.depth = 99f;
700  _fontCamera.cullingMask = 0;
701  _fontCamera.useOcclusionCulling = false;
702  _fontCamera.isOrthoGraphic = true;
703  _glyphLeft = new float[94];
704  _glyphTop = new float[94];
705  _glyphRight = new float[94];
706  _glyphBottom = new float[94];
707  int num = 0;
708  int num2 = 0;
709  int width = _fontImage.width;
710  float num3 = 1f / (float)_fontImage.width;
711  float num4 = 1f / (float)_fontImage.height;
712  for (int i = 0; i < 94; i++) {
713  _glyphLeft [i] = (float)num;
714  _glyphTop [i] = (float)num2;
715  _glyphRight [i] = (float)(num + 8);
716  _glyphBottom [i] = (float)(num2 + 15);
717  _glyphLeft [i] *= num3;
718  _glyphRight [i] *= num3;
719  _glyphTop [i] *= num4;
720  _glyphBottom [i] *= num4;
721  _glyphTop [i] = 1f - _glyphTop [i];
722  _glyphBottom [i] = 1f - _glyphBottom [i];
723  num += 8;
724  if (num == width) {
725  num = 0;
726  num2 += 15;
727  }
728  }
729  _whiteUV = hDebug.UVRectangle.CreateFromTexture (3, 5, 1, 1, _fontImage.width, _fontImage.height);
730 
731  }
732 
733  public void ToggleMode ()
734  {
735  switch (Mode) {
736  case DisplayMode.Off:
737  Mode = DisplayMode.Stats;
738  break;
739  case DisplayMode.Stats:
740  Mode = DisplayMode.Console;
741  break;
742  case DisplayMode.Console:
743  Mode = DisplayMode.Off;
744  break;
745  }
746  }
747 
748  public void PageUp ()
749  {
750  _logOffsetV += ConsoleMaxDisplayedLines;
751  int num = hDebug._logMessages.Count - ConsoleMaxDisplayedLines;
752  if (_logOffsetV > num) {
753  _logOffsetV = num;
754  }
755  }
756 
757  public void PageDown ()
758  {
759  _logOffsetV -= ConsoleMaxDisplayedLines;
760  if (_logOffsetV < 0) {
761  _logOffsetV = 0;
762  }
763  }
764 
765  public void ScrollLeft ()
766  {
767  _logOffsetH--;
768 
769  }
770 
771  public void ScrollRight ()
772  {
773  _logOffsetH++;
774  if (_logOffsetH > 0)
775  _logOffsetH = 0;
776  }
777 
778  /// <summary>
779  /// Unity's Update Event
780  /// </summary>
781  void Update ()
782  {
783  // Do we want to monitor for input, or let the user issue their own calls to our methods.
784  if (!CheckForInput)
785  return;
786 
787  // Check for Console Mode adjustments
788  if ((Input.GetKey (KeyCode.LeftShift) || Input.GetKey (KeyCode.RightShift)) && Input.GetKeyDown (ToggleKey)) {
789  Mode = DisplayMode.Console;
790  } else {
791  if (Input.GetKeyDown (ToggleKey)) {
792  ToggleMode ();
793  }
794  }
795 
796  // If we're displaying the console we should also watch for moving around it.
797  // TODO: This would be where we would capture other input as well.
798  if (Mode == DisplayMode.Console) {
799  if (Input.GetKeyDown (KeyCode.UpArrow)) {
800  PageUp ();
801  } else if (Input.GetKeyDown (KeyCode.DownArrow)) {
802  PageDown ();
803  } else if (Input.GetKey (KeyCode.RightArrow)) {
804  ScrollLeft ();
805  } else if (Input.GetKey (KeyCode.LeftArrow)) {
806  ScrollRight ();
807  }
808  }
809  }
810 
811  struct PrintedText
812  {
813  public readonly Color Color;
814  public readonly string Text;
815  public readonly int X;
816  public readonly int Y;
817 
818  public PrintedText (string text, int x, int y, Color color)
819  {
820  Text = text;
821  X = x;
822  Y = y;
823  Color = color;
824  }
825  }
826 
827  struct UVRectangle
828  {
829  public float U0;
830  public float U1;
831  public float V0;
832  public float V1;
833 
834  public UVRectangle (float u0, float v0, float u1, float v1)
835  {
836  U0 = u0;
837  V0 = v0;
838  U1 = u1;
839  V1 = v1;
840  }
841 
842  public static hDebug.UVRectangle CreateFromTexture (int left, int top, int width, int height, int sourceWidth, int sourceHeight)
843  {
844  float num = 1f / (float)sourceWidth;
845  float num2 = 1f / (float)sourceHeight;
846  return new hDebug.UVRectangle {
847  U0 = (float)left * num,
848  V0 = 1f - (float)top * num2,
849  U1 = (float)(left + width) * num,
850  V1 = 1f - (float)(top + height) * num2
851  };
852  }
853  }
854 
855  public class WatchedItem
856  {
857  /// <summary>
858  /// The index used to store this WatchedItem in the WatchedItems dictionary.
859  /// </summary>
860  /// <remarks>
861  /// This is whats used to identify the value displayed on the watch list.
862  /// </remarks>
863  public string Key;
864  /// <summary>
865  /// What type of item is the data "supposed" to be, as it is stored as a string for output purposes.
866  /// </summary>
867  public ItemType Type;
868  /// <summary>
869  /// The actual data referenced.
870  /// </summary>
871  string _data;
872 
873  public WatchedItem (ItemType type, string key, string data)
874  {
875  Type = type;
876  Key = key;
877  Data = data;
878  }
879 
880  public enum ItemType
881  {
882  String = 0,
883  Integer = 1,
884  Float = 2,
885  Boolean = 3
886  }
887 
888  public string Data {
889  get { return _data; }
890  set {
891  if (_data != value) {
892  _data = value;
893  OutputPaddedSize = (Output.Length * GlyphWidth) + (StatsPadding * 2);
894  }
895  }
896  }
897 
898  public string Output {
899  get {
900  return Key + ": " + Data;
901  }
902  }
903 
904  public int OutputPaddedSize { get; private set; }
905  }
906 
907  /// <summary>
908  /// A log entry.
909  /// </summary>
910  class LogMessage
911  {
912  /// <summary>
913  /// The Message
914  /// </summary>
915  public readonly string Message;
916  /// <summary>
917  /// The Message Type
918  /// </summary>
919  public readonly LogType Type;
920 
921  /// <summary>
922  /// Initializes a new instance of the LogMesssage class.
923  /// </summary>
924  /// <param name="message">Message.</param>
925  /// <param name="type">Unity LogType</param>
926  public LogMessage (string message, LogType type)
927  {
928  Message = message;
929  Type = type;
930  }
931  }
932 }
static bool Exists()
Does the Console already exist?
Definition: hDebug.cs:238
ItemType Type
What type of item is the data &quot;supposed&quot; to be, as it is stored as a string for output purposes...
Definition: hDebug.cs:867
static void Print(int x, int y, string text)
Definition: hDebug.cs:289
static void Warn(object obj)
Definition: hDebug.cs:314
const int GlyphHeight
Height of a character from our fontImage.
Definition: hDebug.cs:43
static void Log(object obj)
Definition: hDebug.cs:269
static void Error(object obj)
Definition: hDebug.cs:243
static void Warn(string text, params object[] args)
Definition: hDebug.cs:324
static void Watch(string key, float value)
Definition: hDebug.cs:357
static void Print(int x, int y, object obj)
Definition: hDebug.cs:284
void ScrollLeft()
Definition: hDebug.cs:765
static bool EchoToUnityLog
Definition: hDebug.cs:117
static hDebug Instance
Gets the console instance, creating one if none is found.
Definition: hDebug.cs:210
void PageUp()
Definition: hDebug.cs:748
static void Print(int x, int y, Color color, string text, params object[] args)
Definition: hDebug.cs:309
void ToggleMode()
Definition: hDebug.cs:733
static void Print(int x, int y, string text, params object[] args)
Definition: hDebug.cs:294
DisplayLocation
Definition: hDebug.cs:191
string Key
The index used to store this WatchedItem in the WatchedItems dictionary.
Definition: hDebug.cs:863
static void Log(string text, params object[] args)
Definition: hDebug.cs:279
KeyCode ToggleKey
What key should be used to toggle between modes.
Definition: hDebug.cs:101
DisplayMode Mode
What should be displayed? Stats or Console?
Definition: hDebug.cs:96
int OutputPaddedSize
Definition: hDebug.cs:904
WatchedItem(ItemType type, string key, string data)
Definition: hDebug.cs:873
static void Watch(string key, string value)
Definition: hDebug.cs:339
An in-game debugging system to make every developer happy.
Definition: hDebug.cs:38
static void Error(string text, params object[] args)
Definition: hDebug.cs:253
static void Initialize()
Definition: hDebug.cs:258
static void Print(int x, int y, Color color, string text)
Definition: hDebug.cs:304
const int GlyphWidth
Width of a character from our fontImage.
Definition: hDebug.cs:47
static Color Shadow
Definition: hDebug.cs:116
static void UnWatch(string key)
Definition: hDebug.cs:366
static void Watch(string key, int value)
Definition: hDebug.cs:348
int ConsoleHeight
Definition: hDebug.cs:230
static void Warn(string text)
Definition: hDebug.cs:319
static void Error(string text)
Definition: hDebug.cs:248
static void Log(string text)
Definition: hDebug.cs:274
static void Watch(string key, bool value)
Definition: hDebug.cs:329
DisplayMode
Definition: hDebug.cs:197
bool CheckForInput
Should we monitor for input?
Definition: hDebug.cs:87
void ScrollRight()
Definition: hDebug.cs:771
void PageDown()
Definition: hDebug.cs:757
DisplayLocation Location
Where should the console be displayed on the screen.
Definition: hDebug.cs:92
static void Print(int x, int y, Color color, object obj)
Definition: hDebug.cs:299