Calendar
| Mo | Tu | We | Th | Fr | Sa | Su |
|---|
| 29 | 30 | 1 | 2 | 3 | 4 | 5 | | 6 | 7 | 8 | 9 | 10 | 11 | 12 | | 13 | 14 | 15 | 16 | 17 | 18 | 19 | | 20 | 21 | 22 | 23 | 24 | 25 | 26 | | 27 | 28 | 29 | 30 | 31 | 1 | 2 | | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
Archive
- 2002
- 2003
- 2004
- 2005
- 2006
- 2007
- 2008
- 2009
- 2010
- 2011
- 2012
- 2013
|
If you have some editor scripts and you want to detect weather or not unity has compiled your scripts you can try this simple method. Created a private class within your editor window and declare a field of that private class. public class YourEditor : EditorWindow
{
private RecompileClass recompile;
private class RecompileClass
{
}
}
In the OnGUI method (or where appropriate) perform a check to see if the field is null. If it’s null you can assume that unity compiled your scripts again so do what you need to do is create a new instance of the private class and assign it to the field.
public class YourEditor : EditorWindow
{
private RecompileClass recompile;
private class RecompileClass
{
}
public void OnGUI()
{
// check if recompile variable is null
if (this.recompile == null )
{
// if yes assume recompile then create a reference to a recompile class
this.recompile = new RecompileClass();
// do what you need to do here after detecting a recompile
}
// do regular stuff here
}
}
The next time unity recompiles your scripts the field will loose it’s reference and will be null again.
7a47664e-c7f9-40c0-863a-031034f1e57e|0|.0
using System;
using System.Collections.Generic;
/// <summary>
/// Implements a generic equality comparer that uses a callback to perform the comparison.
/// </summary>
/// <typeparam name="T">The type we want to compare.</typeparam>
public class EqualityComparerCallback<T> : IEqualityComparer<T>
{
/// <summary>
/// Holds a reference to the callback that handles comparisons.
/// </summary>
private readonly Func<T, T, bool> function;
/// <summary>
/// Holds a reference to the callback used to calculate hash codes.
/// </summary>
private Func<T, int> hashFunction;
/// <summary>
/// Initializes a new instance of the <see cref="EqualityComparerCallback{T}"/> class.
/// </summary>
/// <param name="func">
/// The callback function that will do the comparing.
/// </param>
/// <exception cref="ArgumentNullException">
/// Throws a <see cref="ArgumentNullException"/> if the <see cref="func"/> parameter is null.
/// </exception>
public EqualityComparerCallback(Func<T, T, bool> func)
{
if (func == null)
{
throw new ArgumentNullException("func");
}
this.function = func;
}
/// <summary>
/// Initializes a new instance of the <see cref="EqualityComparerCallback{T}"/> class.
/// </summary>
/// <param name="func">
/// The callback function that will do the comparing.
/// </param>
/// <param name="hash">
/// The callback function that will return the hash code.
/// </param>
public EqualityComparerCallback(Func<T, T, bool> func, Func<T, int> hash)
: this(func)
{
this.hashFunction = hash;
}
/// <summary>
/// Gets or sets the callback function to use to compute the hash.
/// </summary>
public Func<T, int> HashFunction
{
get
{
return this.hashFunction;
}
set
{
this.hashFunction = value;
}
}
/// <summary>
/// Provides a static method for returning a <see cref="EqualityComparerCallback{T}"/> type.
/// </summary>
/// <param name="func">
/// The callback function that will do the comparing.
/// </param>
/// <returns>Returns a new instance of a <see cref="EqualityComparerCallback{T}"/> type.</returns>
public static IEqualityComparer<T> Compare(Func<T, T, bool> func)
{
return new EqualityComparerCallback<T>(func);
}
/// <summary>
/// Determines whether the specified object instances are considered equal.
/// </summary>
/// <param name="x">The first object to compare.</param>
/// <param name="y">The second object to compare.</param>
/// <returns>True if the objects are considered equal otherwise, false. If both objA and objB are null, the method returns true.</returns>
public bool Equals(T x, T y)
{
return this.function(x, y);
}
/// <summary>
/// The get hash code for a object.
/// </summary>
/// <param name="obj">
/// The object whose hash code will be computed.
/// </param>
/// <returns>
/// Returns a <see cref="int"/> value representing the hash code.
/// </returns>
/// <remarks>If no callback function was set for the <see cref="HashFunction"/> the object default <see cref="GetHashCode"/> method will be used.</remarks>
public int GetHashCode(T obj)
{
if (this.hashFunction == null)
{
return obj.GetHashCode();
}
return this.hashFunction(obj);
}
}
A usage scenario
// build a list of unique categories
var list = new List<string>();
foreach (var rule in this.rules)
{
if (string.IsNullOrEmpty(rule.Category ?? string.Empty))
{
continue;
}
// check if already exists
if (list.Contains(rule.Category, new EqualityComparerCallback<string>((a, b) => a.Trim() == b.Trim())))
{
continue;
}
}
return list.ToArray();
333443f0-1633-4575-a8cf-c61d4e92f924|0|.0
Simple solution to get a MIME type from a file extension. Source –> http://stackoverflow.com/a/7161265/341706
public static class MIMEAssistant
{
private static readonly Dictionary<string, string> MIMETypesDictionary = new Dictionary<string, string>
{
{"ai", "application/postscript"},
{"aif", "audio/x-aiff"},
{"aifc", "audio/x-aiff"},
{"aiff", "audio/x-aiff"},
{"asc", "text/plain"},
{"atom", "application/atom+xml"},
{"au", "audio/basic"},
{"avi", "video/x-msvideo"},
{"bcpio", "application/x-bcpio"},
{"bin", "application/octet-stream"},
{"bmp", "image/bmp"},
{"cdf", "application/x-netcdf"},
{"cgm", "image/cgm"},
{"class", "application/octet-stream"},
{"cpio", "application/x-cpio"},
{"cpt", "application/mac-compactpro"},
{"csh", "application/x-csh"},
{"css", "text/css"},
{"dcr", "application/x-director"},
{"dif", "video/x-dv"},
{"dir", "application/x-director"},
{"djv", "image/vnd.djvu"},
{"djvu", "image/vnd.djvu"},
{"dll", "application/octet-stream"},
{"dmg", "application/octet-stream"},
{"dms", "application/octet-stream"},
{"doc", "application/msword"},
{"docx","application/vnd.openxmlformats-officedocument.wordprocessingml.document"},
{"dotx", "application/vnd.openxmlformats-officedocument.wordprocessingml.template"},
{"docm","application/vnd.ms-word.document.macroEnabled.12"},
{"dotm","application/vnd.ms-word.template.macroEnabled.12"},
{"dtd", "application/xml-dtd"},
{"dv", "video/x-dv"},
{"dvi", "application/x-dvi"},
{"dxr", "application/x-director"},
{"eps", "application/postscript"},
{"etx", "text/x-setext"},
{"exe", "application/octet-stream"},
{"ez", "application/andrew-inset"},
{"gif", "image/gif"},
{"gram", "application/srgs"},
{"grxml", "application/srgs+xml"},
{"gtar", "application/x-gtar"},
{"hdf", "application/x-hdf"},
{"hqx", "application/mac-binhex40"},
{"htm", "text/html"},
{"html", "text/html"},
{"ice", "x-conference/x-cooltalk"},
{"ico", "image/x-icon"},
{"ics", "text/calendar"},
{"ief", "image/ief"},
{"ifb", "text/calendar"},
{"iges", "model/iges"},
{"igs", "model/iges"},
{"jnlp", "application/x-java-jnlp-file"},
{"jp2", "image/jp2"},
{"jpe", "image/jpeg"},
{"jpeg", "image/jpeg"},
{"jpg", "image/jpeg"},
{"js", "application/x-javascript"},
{"kar", "audio/midi"},
{"latex", "application/x-latex"},
{"lha", "application/octet-stream"},
{"lzh", "application/octet-stream"},
{"m3u", "audio/x-mpegurl"},
{"m4a", "audio/mp4a-latm"},
{"m4b", "audio/mp4a-latm"},
{"m4p", "audio/mp4a-latm"},
{"m4u", "video/vnd.mpegurl"},
{"m4v", "video/x-m4v"},
{"mac", "image/x-macpaint"},
{"man", "application/x-troff-man"},
{"mathml", "application/mathml+xml"},
{"me", "application/x-troff-me"},
{"mesh", "model/mesh"},
{"mid", "audio/midi"},
{"midi", "audio/midi"},
{"mif", "application/vnd.mif"},
{"mov", "video/quicktime"},
{"movie", "video/x-sgi-movie"},
{"mp2", "audio/mpeg"},
{"mp3", "audio/mpeg"},
{"mp4", "video/mp4"},
{"mpe", "video/mpeg"},
{"mpeg", "video/mpeg"},
{"mpg", "video/mpeg"},
{"mpga", "audio/mpeg"},
{"ms", "application/x-troff-ms"},
{"msh", "model/mesh"},
{"mxu", "video/vnd.mpegurl"},
{"nc", "application/x-netcdf"},
{"oda", "application/oda"},
{"ogg", "application/ogg"},
{"pbm", "image/x-portable-bitmap"},
{"pct", "image/pict"},
{"pdb", "chemical/x-pdb"},
{"pdf", "application/pdf"},
{"pgm", "image/x-portable-graymap"},
{"pgn", "application/x-chess-pgn"},
{"pic", "image/pict"},
{"pict", "image/pict"},
{"png", "image/png"},
{"pnm", "image/x-portable-anymap"},
{"pnt", "image/x-macpaint"},
{"pntg", "image/x-macpaint"},
{"ppm", "image/x-portable-pixmap"},
{"ppt", "application/vnd.ms-powerpoint"},
{"pptx","application/vnd.openxmlformats-officedocument.presentationml.presentation"},
{"potx","application/vnd.openxmlformats-officedocument.presentationml.template"},
{"ppsx","application/vnd.openxmlformats-officedocument.presentationml.slideshow"},
{"ppam","application/vnd.ms-powerpoint.addin.macroEnabled.12"},
{"pptm","application/vnd.ms-powerpoint.presentation.macroEnabled.12"},
{"potm","application/vnd.ms-powerpoint.template.macroEnabled.12"},
{"ppsm","application/vnd.ms-powerpoint.slideshow.macroEnabled.12"},
{"ps", "application/postscript"},
{"qt", "video/quicktime"},
{"qti", "image/x-quicktime"},
{"qtif", "image/x-quicktime"},
{"ra", "audio/x-pn-realaudio"},
{"ram", "audio/x-pn-realaudio"},
{"ras", "image/x-cmu-raster"},
{"rdf", "application/rdf+xml"},
{"rgb", "image/x-rgb"},
{"rm", "application/vnd.rn-realmedia"},
{"roff", "application/x-troff"},
{"rtf", "text/rtf"},
{"rtx", "text/richtext"},
{"sgm", "text/sgml"},
{"sgml", "text/sgml"},
{"sh", "application/x-sh"},
{"shar", "application/x-shar"},
{"silo", "model/mesh"},
{"sit", "application/x-stuffit"},
{"skd", "application/x-koan"},
{"skm", "application/x-koan"},
{"skp", "application/x-koan"},
{"skt", "application/x-koan"},
{"smi", "application/smil"},
{"smil", "application/smil"},
{"snd", "audio/basic"},
{"so", "application/octet-stream"},
{"spl", "application/x-futuresplash"},
{"src", "application/x-wais-source"},
{"sv4cpio", "application/x-sv4cpio"},
{"sv4crc", "application/x-sv4crc"},
{"svg", "image/svg+xml"},
{"swf", "application/x-shockwave-flash"},
{"t", "application/x-troff"},
{"tar", "application/x-tar"},
{"tcl", "application/x-tcl"},
{"tex", "application/x-tex"},
{"texi", "application/x-texinfo"},
{"texinfo", "application/x-texinfo"},
{"tif", "image/tiff"},
{"tiff", "image/tiff"},
{"tr", "application/x-troff"},
{"tsv", "text/tab-separated-values"},
{"txt", "text/plain"},
{"ustar", "application/x-ustar"},
{"vcd", "application/x-cdlink"},
{"vrml", "model/vrml"},
{"vxml", "application/voicexml+xml"},
{"wav", "audio/x-wav"},
{"wbmp", "image/vnd.wap.wbmp"},
{"wbmxl", "application/vnd.wap.wbxml"},
{"wml", "text/vnd.wap.wml"},
{"wmlc", "application/vnd.wap.wmlc"},
{"wmls", "text/vnd.wap.wmlscript"},
{"wmlsc", "application/vnd.wap.wmlscriptc"},
{"wrl", "model/vrml"},
{"xbm", "image/x-xbitmap"},
{"xht", "application/xhtml+xml"},
{"xhtml", "application/xhtml+xml"},
{"xls", "application/vnd.ms-excel"},
{"xml", "application/xml"},
{"xpm", "image/x-xpixmap"},
{"xsl", "application/xml"},
{"xlsx","application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"},
{"xltx","application/vnd.openxmlformats-officedocument.spreadsheetml.template"},
{"xlsm","application/vnd.ms-excel.sheet.macroEnabled.12"},
{"xltm","application/vnd.ms-excel.template.macroEnabled.12"},
{"xlam","application/vnd.ms-excel.addin.macroEnabled.12"},
{"xlsb","application/vnd.ms-excel.sheet.binary.macroEnabled.12"},
{"xslt", "application/xslt+xml"},
{"xul", "application/vnd.mozilla.xul+xml"},
{"xwd", "image/x-xwindowdump"},
{"xyz", "chemical/x-xyz"},
{"zip", "application/zip"}
};
public static string GetMIMEType(string fileName)
{
if (Path.GetExtension(fileName).Length > 1 && MIMETypesDictionary.ContainsKey(Path.GetExtension(fileName).Remove(0, 1)))
{
return MIMETypesDictionary[Path.GetExtension(fileName).Remove(0, 1)];
}
return "unknown/unknown";
}
}
c6353f07-f077-44d3-9f38-8db47d14e14a|0|.0
I needed a quick file caching solution and here is what I came up with … using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
/// <summary>
/// Provides a file model used by the <see cref="FileCache"/> type.
/// </summary>
public class FileModel
{
/// <summary>
/// Gets or sets information about the file.
/// </summary>
public FileInfo Info { get; set; }
/// <summary>
/// Gets or sets the last time that the file had it's contents cached.
/// </summary>
public DateTime LastUpdate { get; set; }
/// <summary>
/// Gets or sets the file content for the file.
/// </summary>
public StringBuilder Data { get; set; }
}
/// <summary>
/// Provides a caching for file content based on file extensions.
/// </summary>
public class FileCache
{
/// <summary>
/// Holds a singleton instance of a <see cref="FileCache"/> type.
/// </summary>
private static FileCache singleton;
/// <summary>
/// Used to hold the contents of the files.
/// </summary>
private readonly Dictionary<string, FileModel> files;
/// <summary>
/// Holds the list of extensions for files that will have there contents cached.
/// </summary>
private readonly List<string> extensions;
/// <summary>
/// Gets or sets a time value used to determine if a files content should be read again.
/// </summary>
public TimeSpan CacheTime { get; set; }
/// <summary>
/// Gets a list of file extension that will have there contents cached.
/// </summary>
public List<string> Extensions
{
get
{
return this.extensions;
}
}
/// <summary>
/// Default constructor.
/// </summary>
public FileCache()
{
this.files = new Dictionary<string, FileModel>();
this.extensions = new List<string>();
this.extensions.AddRange(new[] { ".htm" });
this.CacheTime = TimeSpan.FromSeconds(2);
}
/// <summary>
/// Provides a helper method for retrieving a file.
/// </summary>
/// <param name="path">The path to the file.</param>
/// <returns>Returns a <see cref="FileModel"/> type containing information about the file.</returns>
public static FileModel GetFile(string path)
{
return Instance.Get(path);
}
/// <summary>
/// Provides a helper method for retrieving a file.
/// </summary>
/// <param name="path">The path to the file.</param>
/// <returns>Returns a <see cref="FileModel"/> type containing information about the file.</returns>
public FileModel Get(string path)
{
// if file does not exit just return null
if (!File.Exists(path))
{
return null;
}
// check if file already exists in the cache
FileModel model;
if (this.files.ContainsKey(path))
{
// get existing entry
model = this.files[path];
}
else
{
// add new entry
model = new FileModel { LastUpdate = DateTime.Now, Info = new FileInfo(path) };
this.files.Add(path, model);
}
// check to cache file data
if (this.extensions.Contains(model.Info.Extension))
{
// not data has been read yet OR (the current time has surpassed the last update time plus the cache time AND
// the current time is greater then the last write time ) we can read the contents of the file
if (model.Data == null || (DateTime.Now > model.LastUpdate + this.CacheTime && DateTime.Now > model.Info.LastWriteTime))
{
model.Data = new StringBuilder(File.ReadAllText(model.Info.FullName));
}
}
// return the modal data
return model;
}
/// <summary>
/// Gets a singleton instance of a <see cref="FileCache"/> type.
/// </summary>
public static FileCache Instance
{
get
{
return singleton ?? (singleton = new FileCache());
}
}
}
38133126-9626-4b25-9266-91f4b3b6ce51|0|.0
Below is a code sample containing drawing methods for drawing check boxes or any control type in a grid based layout similar to the SelectionGrid.
using System;
using UnityEngine;
public class ControlGrid
{
/// <summary>
/// Draw a grid of check boxes similar to SelectionGrid.
/// </summary>
/// <param name="checkedValues">Specifies the checked values of the check boxes.</param>
/// <param name="text">The content for each individual check box.</param>
/// <param name="columns">The number of columns in the grid.</param>
/// <param name="style">The style to be applied to each check box.</param>
/// <param name="options">Specifies layout options to be applied to each check box.</param>
/// <returns>Returns the checked state for each check box.</returns>
/// <remarks><p>Check boxes are drawn top to bottom, left to right.</p> </remarks>
/// <exception cref="IndexOutOfRangeException">Can occur if the size of the array is too small.</exception>
public static bool[] DrawCheckBoxGrid(bool[] checkedValues, string[] text, int columns, GUIStyle style, params GUILayoutOption[] options)
{
// convert string content into gui content
var content = new GUIContent[text.Length];
for (var i = 0; i < content.Length; i++)
{
content[i] = new GUIContent(text[i]);
}
return DrawCheckBoxGrid(checkedValues, content, columns, style, options);
}
/// <summary>
/// Draw a grid of check boxes similar to SelectionGrid.
/// </summary>
/// <param name="checkedValues">Specifies the checked values of the check boxes.</param>
/// <param name="textures">The content for each individual check box.</param>
/// <param name="columns">The number of columns in the grid.</param>
/// <param name="style">The style to be applied to each check box.</param>
/// <param name="options">Specifies layout options to be applied to each check box.</param>
/// <returns>Returns the checked state for each check box.</returns>
/// <remarks><p>Check boxes are drawn top to bottom, left to right.</p> </remarks>
/// <exception cref="IndexOutOfRangeException">Can occur if the size of the array is too small.</exception>
public static bool[] DrawCheckBoxGrid(bool[] checkedValues, Texture2D[] textures, int columns, GUIStyle style, params GUILayoutOption[] options)
{
// convert texture content into gui content
var content = new GUIContent[textures.Length];
for (var i = 0; i < content.Length; i++)
{
content[i] = new GUIContent(string.Empty, textures[i]);
}
return DrawCheckBoxGrid(checkedValues, content, columns, style, options);
}
/// <summary>
/// Draw a grid of check boxes similar to SelectionGrid.
/// </summary>
/// <param name="checkedValues">Specifies the checked values of the check boxes.</param>
/// <param name="content">The content for each individual check box.</param>
/// <param name="columns">The number of columns in the grid.</param>
/// <param name="style">The style to be applied to each check box.</param>
/// <param name="options">Specifies layout options to be applied to each check box.</param>
/// <returns>Returns the checked state for each check box.</returns>
/// <remarks><p>Check boxes are drawn top to bottom, left to right.</p> </remarks>
/// <exception cref="IndexOutOfRangeException">Can occur if the size of the array is too small.</exception>
public static bool[] DrawCheckBoxGrid(bool[] checkedValues, GUIContent[] content, int columns, GUIStyle style, params GUILayoutOption[] options)
{
return DrawGenericGrid((e, i, s, o) => GUILayout.Toggle(e[i], content[i], style, options), checkedValues, content, columns, style, options);
}
/// <summary>
/// Draw a grid of controls using a draw callback similar to SelectionGrid.
/// </summary>
/// <param name="drawCallback">Specifies a draw callback that is responsible for performing the actual drawing.</param>
/// <param name="values">Specifies the values of the controls.</param>
/// <param name="content">The content for each individual control.</param>
/// <param name="columns">The number of columns in the grid.</param>
/// <param name="style">The style to be applied to each control.</param>
/// <param name="options">Specifies layout options to be applied to each control.</param>
/// <returns>Returns the value for each control.</returns>
/// <remarks><p>Controls are drawn top to bottom, left to right.</p> </remarks>
/// <exception cref="IndexOutOfRangeException">Can occur if the size of the array is too small.</exception>
/// <exception cref="ArgumentNullException">If the drawCallback is null.</exception>
public static T[] DrawGenericGrid<T>(Func<T[], int, GUIStyle, GUILayoutOption[], T> drawCallback, T[] values, GUIContent[] content, int columns, GUIStyle style, params GUILayoutOption[] options)
{
if (drawCallback == null)
{
throw new ArgumentNullException("drawCallback");
}
GUILayout.BeginVertical();
var rowIndex = 0;
var columnIndex = 0;
var index = rowIndex * columns + columnIndex;
GUILayout.BeginHorizontal();
while (index < values.Length)
{
// draw control
values[index] = drawCallback(values, index, style, options);
// move to next column
columnIndex++;
// if passed max columns move down to next row and set to first column
if (columnIndex > columns - 1)
{
columnIndex = 0;
rowIndex++;
// remember to start a new horizontal layout
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
}
// re-calculate the index
index = rowIndex * columns + columnIndex;
}
GUILayout.EndHorizontal();
GUILayout.EndVertical();
return values;
}
}
Here is a simple example of how to use the draw methods
var emptyContent = new[] { GUIContent.none, GUIContent.none, GUIContent.none,
GUIContent.none, GUIContent.none, GUIContent.none,
GUIContent.none, GUIContent.none, GUIContent.none };
GUILayout.BeginVertical(GUILayout.MinWidth(128));
rule.IgnoreUpper = GUILayout.Toggle(rule.IgnoreUpper, "Upper Neighbors");
rule.NeighborsUpper = ControlGrid.DrawCheckBoxGrid(rule.NeighborsUpper, emptyContent, 3, GUI.skin.button, GUILayout.MaxWidth(32), GUILayout.MaxHeight(32));
GUILayout.EndVertical();

b55e85cb-8172-4da5-8ad2-359b5ebb0ee3|0|.0
MEFHelpers provides a helper class that provides a static method to make MEF composition easier. /// <summary>
/// Provides a helper class for MEF composition.
/// </summary>
public sealed class MEFHelpers
{
/// <summary>
/// Composes MEF parts.
/// </summary>
/// <param name="parts">The composable object parts.</param>
public static void Compose(params object[] parts)
{
Compose(parts, new string[0]);
}
/// <summary>
/// Composes MEF parts.
/// </summary>
/// <param name="searchFolders">Provides a series of search folders to search for *.dll files.</param>
/// <param name="parts">The composable object parts.</param>
public static void Compose(IEnumerable<string> searchFolders, params object[] parts)
{
// setup composition container
var catalog = new AggregateCatalog();
// check if folders were specified
if (searchFolders != null)
{
// add search folders
foreach (var folder in searchFolders.Where(System.IO.Directory.Exists))
{
catalog.Catalogs.Add(new DirectoryCatalog(folder, "*.dll"));
}
}
// compose and create plug ins
var composer = new CompositionContainer(catalog);
composer.ComposeParts(parts);
}
}
A usage scenario is provided below
public class KissCSMEFComposer
{
[ImportMany(typeof(IProcessor))]
public List<Lazy<IProcessor>> StringProcessors;
[ImportMany(typeof(ICommandRepository))]
public List<Lazy<ICommandRepository>> CommandRepository;
}
var composer = new KissCSMEFComposer();
// try to connect with MEF types
try
{
MEFHelpers.Compose(this.SearchFolders, composer);
}
catch (Exception ex)
{
// ERR: handle error
}
// register string processors
foreach (var processor in composer.StringProcessors)
{
StringProcessorRepository.Instance.Register(processor.Value);
}
// etc ...
12475fb8-8d55-450f-8018-946f135dffa0|0|.0
The code below can be used to log performance of your code.
using System.Collections.Generic;
using System.Diagnostics;
/// <summary>
/// Provides a simple performance testing class that utilizes <see cref="Stopwatch"/>.
/// </summary>
/// <typeparam name="T">The type that will be used as the indexer.</typeparam>
public class PerformanceTesting<T>
{
/// <summary>
/// Provides a model that contains timer information.
/// </summary>
private class TimerModel
{
/// <summary>
/// Used to record performance timings.
/// </summary>
public readonly Stopwatch Timer;
/// <summary>
/// Used to record how many times the <see cref="Timer"/> has been started.
/// </summary>
public int Count;
/// <summary>
/// Used to store the enabled state.
/// </summary>
private bool enabled;
/// <summary>
/// Gets or sets a value whether or not this timer if enabled.
/// </summary>
/// <remarks>If <see cref="Timer"/> has been started and Enabled is set to false the timer will be stopped.</remarks>
public bool Enabled
{
get
{
return this.enabled;
}
set
{
this.enabled = value;
// be sure to stop the timer if disabled
if (!value && this.Timer.IsRunning)
{
this.Timer.Stop();
}
}
}
/// <summary>
/// Default constructor.
/// </summary>
public TimerModel()
{
this.Timer = new Stopwatch();
this.enabled = true;
}
}
/// <summary>
/// Holds a reference to a singleton instance.
/// </summary>
private static PerformanceTesting<T> singleton;
/// <summary>
/// Used to store various timer information.
/// </summary>
private readonly Dictionary<T, TimerModel> timers;
/// <summary>
/// Default constructor.
/// </summary>
public PerformanceTesting()
{
this.timers = new Dictionary<T, TimerModel>();
}
/// <summary>
/// Creates a new timer.
/// </summary>
/// <param name="key">The unique key for the timer.</param>
/// <remarks>It is best to create the timer at the start of the application getting it ready for use.</remarks>
public void Create(T key)
{
this.timers.Add(key, new TimerModel());
}
/// <summary>
/// Creates a new timer for each key.
/// </summary>
/// <param name="keys">The unique keys for the timers.</param>
/// <remarks>It is best to create the timer at the start of the application getting it ready for use.</remarks>
public void Create(T[] keys)
{
foreach (var key in keys)
{
this.timers.Add(key, new TimerModel());
}
}
/// <summary>
/// Returns the total ticks that this timer has observed.
/// </summary>
/// <param name="key">The key to the timer information.</param>
/// <returns>Returns a time value in ticks.</returns>
public long TotalTicks(T key)
{
var model = this.timers[key];
return model.Timer.ElapsedTicks;
}
/// <summary>
/// Returns the total ticks that this timer has observed.
/// </summary>
/// <param name="keys">The keys to the timers information.</param>
/// <returns>Returns the sum of the time values in ticks.</returns>
public long TotalTicks(T[] keys)
{
long total = 0;
foreach (var key in keys)
{
var model = this.timers[key];
total += model.Timer.ElapsedTicks;
}
return total;
}
/// <summary>
/// Gets the start count.
/// </summary>
/// <param name="key">The key to the timer information.</param>
/// <returns>The number of times the timer has started.</returns>
public int GetStartCount(T key)
{
var model = this.timers[key];
return model.Count;
}
/// <summary>
/// Gets the start count for specified timers.
/// </summary>
/// <param name="keys">The keys to the timer information.</param>
/// <returns>The sum total of times all specified the timers have started.</returns>
public int GetStartCount(T[] keys)
{
int total = 0;
foreach (var key in keys)
{
var model = this.timers[key];
total += model.Count;
}
return total;
}
/// <summary>
/// Returns the total ticks that this timer has observed.
/// </summary>
/// <param name="key">The key to the timer information.</param>
/// <returns>Returns a time value in milliseconds.</returns>
public long TotalMilliseconds(T key)
{
var model = this.timers[key];
return model.Timer.ElapsedMilliseconds;
}
/// <summary>
/// Returns the total ticks that this timer has observed.
/// </summary>
/// <param name="keys">The keys to the timers information.</param>
/// <returns>Returns the sum of the time values in milliseconds.</returns>
public long TotalMilliseconds(T[] keys)
{
long total = 0;
foreach (var key in keys)
{
var model = this.timers[key];
total += model.Timer.ElapsedMilliseconds;
}
return total;
}
/// <summary>
/// Calculates the average time in ticks that elapsed while this timer was recording.
/// </summary>
/// <param name="key">The key to the timer information.</param>
/// <returns>Returns the average time in ticks that elapsed between each start and stop.</returns>
public long AverageTicks(T key)
{
var model = this.timers[key];
return model.Timer.ElapsedTicks / model.Count;
}
/// <summary>
/// Calculates the total average time in ticks that elapsed while the specified timers were recording.
/// </summary>
/// <param name="keys">The keys to the timer information.</param>
/// <returns>Returns the total average time in ticks that elapsed between each start and stop for all the specified timers.</returns>
public long AverageTicks(T[] keys)
{
long total = 0;
var count = 0;
foreach (var key in keys)
{
var model = this.timers[key];
total += model.Timer.ElapsedTicks;
count += model.Count;
}
return total / count;
}
/// <summary>
/// Calculates the average time in milliseconds that elapsed while this timer was recording.
/// </summary>
/// <param name="key">The key to the timer information.</param>
/// <returns>Returns the average time in milliseconds that elapsed between each start and stop.</returns>
public long AverageMilliseconds(T key)
{
var model = this.timers[key];
if (model.Count == 0)
{
return 0;
}
return model.Timer.ElapsedMilliseconds / model.Count;
}
/// <summary>
/// Calculates the total average time in milliseconds that elapsed while the specified timers were recording.
/// </summary>
/// <param name="keys">The keys to the timer information.</param>
/// <returns>Returns the total average time in milliseconds that elapsed between each start and stop for all the specified timers.</returns>
public long AverageMilliseconds(T[] keys)
{
long total = 0;
var count = 0;
foreach (var key in keys)
{
var model = this.timers[key];
total += model.Timer.ElapsedMilliseconds;
count += model.Count;
}
if (count == 0)
{
return 0;
}
return total / count;
}
/// <summary>
/// Removes the specified timers.
/// </summary>
/// <param name="keys">The keys to the timers that will be removed.</param>
public void Remove(T[] keys)
{
foreach (var key in keys)
{
this.timers.Remove(key);
}
}
/// <summary>
/// Removes a timer.
/// </summary>
/// <param name="key">The key to the timer information.</param>
public void Remove(T key)
{
this.timers.Remove(key);
}
/// <summary>
/// Resets all the timers.
/// </summary>
public void ResetAll()
{
foreach (var pair in this.timers)
{
pair.Value.Timer.Reset();
}
}
/// <summary>
/// Resets all the timers.
/// </summary>
/// <param name="resetCounts">If true will set each timer start count to 0.</param>
public void ResetAll(bool resetCounts)
{
foreach (var pair in this.timers)
{
pair.Value.Timer.Reset();
if (resetCounts)
{
pair.Value.Count = 0;
}
}
}
/// <summary>
/// Gets an array of timer keys.
/// </summary>
/// <returns>Returns an array of timer keys.</returns>
public T[] GetKeys()
{
var keys = new T[this.timers.Count];
this.timers.Keys.CopyTo(keys, 0);
return keys;
}
/// <summary>
/// Sets the enabled state of the timer.
/// </summary>
/// <param name="key">The key to the timer information.</param>
/// <param name="enabled">If true the timer will be enabled. If false the timer will be stopped if it is running.</param>
public void SetEnabled(T key, bool enabled)
{
var model = this.timers[key];
model.Enabled = enabled;
}
/// <summary>
/// Sets the enabled state of the specified timers.
/// </summary>
/// <param name="keys">The keys to the timer information.</param>
/// <param name="enabled">If true the timers will be enabled. If false the timers will be stopped if they are running.</param>
public void SetEnabled(T[] keys, bool enabled)
{
foreach (var key in keys)
{
var model = this.timers[key];
model.Enabled = enabled;
}
}
/// <summary>
/// Gets the enabled state of the timer.
/// </summary>
/// <param name="key">The key to the timer information.</param>
public bool IsEnabled(T key)
{
var model = this.timers[key];
return model.Enabled;
}
/// <summary>
/// Gets the enabled state of the specified timers.
/// </summary>
/// <param name="keys">The keys to the timer information.</param>
/// <param name="enabled">Will contain the enabled state for each specified key.</param>
public void IsEnabled(T[] keys, out bool[] enabled)
{
var enabledStates = new bool[keys.Length];
for (int i = 0; i < keys.Length; i++)
{
var model = this.timers[keys[i]];
enabledStates[i] = model.Enabled;
}
enabled = enabledStates;
}
/// <summary>
/// Starts the timer.
/// </summary>
/// <param name="key">The key to the timer information.</param>
/// <remarks>Will not start if the enabled state is false.</remarks>
public void Start(T key)
{
var model = this.timers[key];
if (!model.Enabled)
{
return;
}
model.Count++;
model.Timer.Start();
}
/// <summary>
/// Starts the timers.
/// </summary>
/// <param name="keys">The keys to the timer information.</param>
/// <remarks>Will not start if the timers enabled state is false.</remarks>
public void Start(T[] keys)
{
foreach (var key in keys)
{
var model = this.timers[key];
if (!model.Enabled)
{
continue;
}
model.Count++;
model.Timer.Start();
}
}
/// <summary>
/// Stops the timer.
/// </summary>
/// <param name="key">The key to the timer information.</param>
public void Stop(T key)
{
var model = this.timers[key];
if (!model.Enabled)
{
return;
}
model.Timer.Stop();
}
/// <summary>
/// Stops the timers.
/// </summary>
/// <param name="keys">The keys to the timer information.</param>
public void Stop(T[] keys)
{
foreach (var key in keys)
{
var model = this.timers[key];
if (!model.Enabled)
{
continue;
}
model.Timer.Stop();
}
}
/// <summary>
/// Resets the timer.
/// </summary>
/// <param name="key">The key to the timer information.</param>
public void Reset(T key)
{
this.Reset(key, false);
}
/// <summary>
/// Resets the timers.
/// </summary>
/// <param name="keys">The keys to the timer information.</param>
public void Reset(T[] keys)
{
this.Reset(keys, false);
}
/// <summary>
/// Resets the timer.
/// </summary>
/// <param name="key">The key to the timer information.</param>
/// <param name="resetCount">If true the start count for the timer will be set to 0.</param>
public void Reset(T key, bool resetCount)
{
var model = this.timers[key];
model.Timer.Reset();
if (resetCount)
{
model.Count = 0;
}
}
/// <summary>
/// Resets the timers.
/// </summary>
/// <param name="keys">The keys to the timer information.</param>
/// <param name="resetCounts">If true the start count for the timers will be set to 0.</param>
public void Reset(T[] keys, bool resetCounts)
{
foreach (var key in keys)
{
var model = this.timers[key];
model.Timer.Reset();
if (resetCounts)
{
model.Count = 0;
}
}
}
/// <summary>
/// Resets the timer start count to 0.
/// </summary>
/// <param name="key">The key to the timer information.</param>
public void ResetCount(T key)
{
var model = this.timers[key];
model.Count = 0;
}
/// <summary>
/// Resets each timer start count to 0.
/// </summary>
/// <param name="keys">The keys to the timer information.</param>
public void ResetCount(T[] keys)
{
foreach (var key in keys)
{
var model = this.timers[key];
model.Count = 0;
}
}
/// <summary>
/// Restarts the timer.
/// </summary>
/// <param name="key">The key to the timer information.</param>
/// <param name="resetCount">If true the start count for the timer will be set to 0.</param>
/// <remarks>The timer will be reset then started again.</remarks>
public void Restart(T key, bool resetCount)
{
var model = this.timers[key];
model.Timer.Reset();
if (resetCount)
{
model.Count = 0;
}
this.Start(key);
}
/// <summary>
/// Restarts the specified timers.
/// </summary>
/// <param name="keys">The keys to the timer information.</param>
/// <param name="resetCounts">If true the start count for the timers will be set to 0.</param>
/// <remarks>Each timer will be reset then started again.</remarks>
public void Restart(T[] keys, bool resetCounts)
{
foreach (var key in keys)
{
var model = this.timers[key];
model.Timer.Reset();
if (resetCounts)
{
model.Count = 0;
}
}
this.Start(keys);
}
/// <summary>
/// Gets a singleton instance of the <see cref="PerformanceTesting{T}"/> class.
/// </summary>
public static PerformanceTesting<T> Instance
{
get
{
return singleton ?? (singleton = new PerformanceTesting<T>());
}
}
}
Setting up a performance counter
#if PERFORMANCE
var perf = PerformanceTesting<string>.Instance;
perf.Create("ScanLines");
#endif
A sample usage
#if PERFORMANCE
var perf = PerformanceTesting<string>.Instance;
perf.Reset("ScanLines");
perf.Start("ScanLines");
#endif
this.scanner.Calculate(this.points);
#if PERFORMANCE
perf.Stop("ScanLines");
#endif
How to report the results
#if PERFORMANCE
public static void ReportPerformanceTimes()
{
var perf = PerformanceTesting<string>.Instance;
foreach (var value in perf.GetKeys())
{
Debug.Log(string.Format("{0} - Total: {1}ms Average: {2} Count: {3}", value, perf.TotalMilliseconds(value), perf.AverageMilliseconds(value), perf.GetStartCount(value)));
}
Debug.Log(string.Format("Total Performance Times - Total: {0}ms", perf.TotalMilliseconds("ScanLines")));
}
#endif
67b32830-abeb-4028-a84c-0f87c797909b|0|.0
Some .net/mono frameworks do not support the IsNullOrWhiteSpace method. The code below provides a simple replacement. /// <summary>
/// Indicates whether a specified string is null, empty, or consists only of white-space characters.
/// </summary>
/// <param name="value">value: The string to test.</param>
/// <returns>true if the value parameter is null or System.String.Empty, or if value consists exclusively of white-space characters.</returns>
public static bool IsNullOrWhiteSpace(this string value)
{
if (value == null)
{
return true;
}
var index = 0;
while (index < value.Length)
{
if (char.IsWhiteSpace(value[index]))
{
index++;
}
else
{
return false;
}
}
return true;
}
232f867b-1708-435c-b8c6-e46cae4b37e3|0|.0
Some .net/mono frameworks do not support the 3 argument GetDirectories method. The code below provides a simple replacement. /// <summary>
/// Builds an array of folders & sub folders.
/// </summary>
/// <param name="path">The path to search.</param>
/// <param name="pattern">The search string to match against the names of files in path. The parameter cannot end in two periods
/// ("..") or contain two periods ("..") followed by System.IO.Path.DirectorySeparatorChar or System.IO.Path.AltDirectorySeparatorChar,
/// nor can it contain any of the characters in System.IO.Path.InvalidPathChars.
///</param>
/// <param name="searchOptions">One of the System.IO.SearchOption values that specifies whether the search operation should include
/// all subdirectories or only the current directory.
///</param>
/// <returns>A String array of directories that match the search pattern.</returns>
public static string[] GetDirectories(string path, string pattern, SearchOption searchOptions)
{
// check if searching for all directories
if (searchOptions == SearchOption.AllDirectories)
{
// add start paths to list
var list = Directory.GetDirectories(path, pattern);
var index = 0;
var count = list.Length;
// process list and add folders to end of list
while (index < count)
{
var directories = Directory.GetDirectories(list[index++], pattern);
if (directories.Length > 0)
{
// check if we need more space to store the directories
if (count + directories.Length > list.Length - 1)
{
Array.Resize(ref list, list.Length + directories.Length + 1000);
}
// add directories to end of the list
foreach (var directory in directories)
{
list[count++] = directory;
}
}
// trim unused index from end of the array
if (list.Length > count)
{
Array.Resize(ref list, count);
}
}
return list;
}
// just return initial list of folder with no sub folders
return Directory.GetDirectories(path, pattern);
}
8c908bd4-0488-4e96-bd3d-1f3d057e612b|0|.0
/// <summary>
/// Provides a game object that automatically self terminates.
/// </summary>
[ExecuteInEditMode]
public class SelfTerminatingObject : MonoBehaviour
{
/// <summary>
/// Holds a reference to a callback that will be called just before termination.
/// </summary>
public Action Callback;
/// <summary>
/// Holds the time delay in seconds before object self terminates.
/// </summary>
public float Delay;
/// <summary>
/// Holds a time value used to determine whether it's time to self terminate.
/// </summary>
private DateTime lastTime;
/// <summary>
/// Called by Unity to update the object.
/// </summary>
public void Update()
{
this.PerformCheck();
}
/// <summary>
/// Called by Unity to draw the object's GUI.
/// </summary>
public void OnGui()
{
this.PerformCheck();
}
/// <summary>
/// Determines whether it's time to terminate.
/// </summary>
private void PerformCheck()
{
// check if specified time span has elapsed
if (DateTime.Now > this.lastTime + TimeSpan.FromSeconds(this.Delay))
{
// run callback is specified
if (this.Callback != null)
{
this.Callback();
}
// destroy object
#if UNITY_EDITOR
DestroyImmediate(this.gameObject, true);
#else
Destroy(this.gameObject);
#endif
}
}
/// <summary>
/// Creates a self terminating game object.
/// </summary>
/// <param name="callback">The method to be called before the object terminates.</param>
/// <returns>Returns a reference to the game object that will self terminate.</returns>
public static GameObject CreateUpdateCallback(Action callback)
{
return CreateUpdateCallback(callback, 0);
}
/// <summary>
/// Creates a self terminating game object.
/// </summary>
/// <param name="callback">The method to be called before the object terminates.</param>
/// <param name="delay">Specifies a delay value in seconds before the object will self terminate.</param>
/// <returns>Returns a reference to the game object that will self terminate.</returns>
public static GameObject CreateUpdateCallback(Action callback, float delay)
{
var obj = new GameObject();
var com = obj.AddComponent<SelfTerminatingObject>();
com.lastTime = DateTime.Now;
com.Delay = delay;
com.Callback = callback;
return obj;
}
}
fac90666-3035-4d9e-8e38-b3533a7b83bb|0|.0
|
|