With the introduction to Unity 5 there comes some api changes. Specifically this foot note was interesting “[2] in Unity5 we also cache the transform component on the c# side, so there should no longer be a performance reason to cache the transform component yourself.

I decided to test it out by writing a few performance test scripts and comparing performance numbers. Below is a screen shot of my results along with the scripts used.

As you can see caching a reference to the transform component in the Start method then using that reference is still faster then calling “this.transform” directly albeit only slightly by about 10-20 ticks. And calling “this.GetComponent<Transform>()” is almost twice as slow.

Unity5TransformPerformance

The code for the PerformanceTesting class is availible here.

TransformCachedGetComponentPerformance script

    using UnityEngine;

    public class TransformCachedGetComponentPerformance : MonoBehaviour
    {
        private Transform reference;

        /// <summary>
        /// Awake is called when the script instance is being loaded.
        /// </summary>
        public void Awake()
        {
#if PERFORMANCE
            var perf = PerformanceTesting.PerformanceTesting<string>.Instance;
            perf.Create("UnityTesting/TransformCachedGetComponentPerformance/Update");
#endif
        }

        /// <summary>
        /// Start is called just before any of the Update methods is called the first time.
        /// </summary>
        public void Start()
        {
            this.reference = this.GetComponent<Transform>();
        }

        /// <summary>
        /// Update is called every frame, if the MonoBehaviour is enabled.
        /// </summary>
        public void Update()
        {
#if PERFORMANCE
            var perf = PerformanceTesting.PerformanceTesting<string>.Instance;
            perf.Start("UnityTesting/TransformCachedGetComponentPerformance/Update");      
#endif

            var rnd = new System.Random();
            this.reference.localPosition = new Vector3(rnd.Next(-3, 3), rnd.Next(-3, 3), rnd.Next(-3, 3));

#if PERFORMANCE
            perf.Stop("UnityTesting/TransformCachedGetComponentPerformance/Update");
#endif
        }
    }

TransformGetComponentPerformance script

    using UnityEngine;

    public class TransformGetComponentPerformance : MonoBehaviour
    {
        /// <summary>
        /// Awake is called when the script instance is being loaded.
        /// </summary>
        public void Awake()
        {
#if PERFORMANCE
            var perf = PerformanceTesting.PerformanceTesting<string>.Instance;
            perf.Create("UnityTesting/TransformGetComponentPerformance/Update");
#endif
        }
        
        /// <summary>
        /// Update is called every frame, if the MonoBehaviour is enabled.
        /// </summary>
        public void Update()
        {
#if PERFORMANCE
            var perf = PerformanceTesting.PerformanceTesting<string>.Instance;
            perf.Start("UnityTesting/TransformGetComponentPerformance/Update");
#endif

            var rnd = new System.Random();
            this.GetComponent<Transform>().localPosition = new Vector3(rnd.Next(-3, 3), rnd.Next(-3, 3), rnd.Next(-3, 3));

#if PERFORMANCE
            perf.Stop("UnityTesting/TransformGetComponentPerformance/Update");
#endif
        }
    }

TransformFieldPerformance script

    using UnityEngine;

    public class TransformFieldPerformance : MonoBehaviour
    {
        /// <summary>
        /// Awake is called when the script instance is being loaded.
        /// </summary>
        public void Awake()
        {
#if PERFORMANCE
            var perf = PerformanceTesting.PerformanceTesting<string>.Instance;
            perf.Create("UnityTesting/TransformFieldPerformance/Update");
#endif
        }

        /// <summary>
        /// Update is called every frame, if the MonoBehaviour is enabled.
        /// </summary>
        public void Update()
        {
#if PERFORMANCE
            var perf = PerformanceTesting.PerformanceTesting<string>.Instance;
            perf.Start("UnityTesting/TransformFieldPerformance/Update");
#endif

            var rnd = new System.Random();
            this.transform.localPosition = new Vector3(rnd.Next(-3, 3), rnd.Next(-3, 3), rnd.Next(-3, 3));

#if PERFORMANCE
            perf.Stop("UnityTesting/TransformFieldPerformance/Update");
#endif
        }
    }

The fallowing code snip is designed to take in a flat list of file paths (or similar data) and produce a hierarchy of tree nodes representing those file paths.

        /// <summary>
        /// Constructs a nested hierarchy of types from a flat list of source types.
        /// </summary>
        /// <typeparam name="TSource">The source type of the flat list that is to be converted.</typeparam>
        /// <typeparam name="TReturn">The type that will be returned.</typeparam>
        /// <typeparam name="TPart">The type of the art type.</typeparam>
        /// <param name="sourceItems">The source items to be converted.</param>
        /// <param name="getParts">A callback function that returns a array of <see cref="TPart"/>.</param>
        /// <param name="comparePart">The compare part callback.</param>
        /// <param name="getChildren">The get children callback.</param>
        /// <param name="addChild">The add child callback.</param>
        /// <param name="createItem">The create item callback.</param>
        /// <returns>Returns an collection of <see cref="TReturn"/> representing the hierarchy.</returns>
        /// <exception cref="Exception">A delegate callback throws an exception. </exception>
        private static IEnumerable<TReturn> ToHierarchy<TSource, TReturn, TPart>(
          IEnumerable<TSource> sourceItems,
        Func<TSource, TPart[]> getParts,
        Func<TReturn, TPart, bool> comparePart,
        Func<TReturn, IEnumerable<TReturn>> getChildren,
        Action<IEnumerable<TReturn>, TReturn> addChild,
        Func<TPart[], int, TSource, TReturn> createItem)
        {
            var treeModels = new List<TReturn>();
            foreach (var keyName in sourceItems)
            {
                IEnumerable<TReturn> items = treeModels;
                var parts = getParts(keyName);
                for (var partIndex = 0; partIndex < parts.Length; partIndex++)
                {
                    var node = items.FirstOrDefault(x => comparePart(x, parts[partIndex]));
                    if (node != null)
                    {
                        items = getChildren(node);
                        continue;
                    }

                    var model = createItem(parts, partIndex, keyName);
                    addChild(items, model);
                    items = getChildren(model);
                }
            }

            return treeModels;
        }

An example of how one could use the ToHierarchy method would be like this …

        var separator = new[] { Path.AltDirectorySeparatorChar.ToString(CultureInfo.InvariantCulture) };
        var nodes = ToHierarchy<string, TreeViewNode, string>(
            paths.OrderBy(x => x),
            x => x.Split(separator, StringSplitOptions.RemoveEmptyEntries),
            (r, p) => string.CompareOrdinal(r.Name, p) == 0,
            r => r.Nodes,
            (r, c) => ((List<TreeViewNode>)r).Add(c),
            this.CreateTreeNode);

        private TreeViewNode CreateTreeNode(string[] parts, int index, string source)
        {
            var node = new TreeViewNode() { Name = parts[index] };
            node.Value = string.Join(Path.DirectorySeparatorChar.ToString(CultureInfo.InvariantCulture), parts, 0, index + 1);
            if (index == parts.Length - 1)
            {
                node.Name = Path.GetFileName(source);
            }

            node.IsFile = File.Exists(node.Value);
            return node;
        }

Where paths is a array of file paths from say Directory.GetFiles.


Source: http://stackoverflow.com/questions/616718/how-do-i-get-common-file-type-icons-in-c

    public static class FileIcon
    {
        [DllImport("shell32.dll")]
        private static extern IntPtr SHGetFileInfo(string pszPath, uint dwFileAttributes, ref SHFILEINFO psfi, uint cbSizeFileInfo, uint uFlags);

        [StructLayout(LayoutKind.Sequential)]
        private struct SHFILEINFO
        {
            public IntPtr hIcon;
            public IntPtr iIcon;
            public uint dwAttributes;

            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
            public string szDisplayName;

            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
            public string szTypeName;
        };

        private const uint SHGFI_ICON = 0x100;
        private const uint SHGFI_LARGEICON = 0x0; // 'Large icon
        private const uint SHGFI_SMALLICON = 0x1; // 'Small icon

        public static System.Drawing.Icon GetLargeIcon(string file)
        {
            var shinfo = new SHFILEINFO();
            var hImgLarge = SHGetFileInfo(file, 0, ref shinfo, (uint)Marshal.SizeOf(shinfo), FileIcon.SHGFI_ICON | FileIcon.SHGFI_LARGEICON);
            return System.Drawing.Icon.FromHandle(shinfo.hIcon);
        }

        public static System.Drawing.Icon GetSmallIcon(string file)
        {
            var shinfo = new SHFILEINFO();
            var hImgLarge = SHGetFileInfo(file, 0, ref shinfo, (uint)Marshal.SizeOf(shinfo), FileIcon.SHGFI_ICON | FileIcon.SHGFI_SMALLICON);
            return System.Drawing.Icon.FromHandle(shinfo.hIcon);
        }
    } 

There are some instances when you may need to lock the mouse cursor to the screen. For example rotating the camera where you would press down on the right mouse button then drag the mouse to rotate the camera. If you wish to hide the mouse cursor and lock it to the screen while dragging you can use Screen.lockCursor.

using UnityEngine;

public class lockCursorExample : MonoBehaviour
{
    void DidLockCursor()
    {
        Debug.Log("Locking cursor");
    }

    void DidUnlockCursor()
    {
        Debug.Log("Unlocking cursor");
    }

    private bool wasLocked;

    void Update()
    {
        if (!Screen.lockCursor && this.wasLocked)
        {
            this.wasLocked = false;
            this.DidUnlockCursor();
        }
        else if (Screen.lockCursor && !this.wasLocked)
        {
            this.wasLocked = true;
            this.DidLockCursor();
        }

        if (Input.GetMouseButtonDown(1))
        {
            Screen.lockCursor = true;
        }

        if (Input.GetMouseButtonUp(1))
        {
            Screen.lockCursor = false;
        }
    }
}

Ever wish you could have a tool tip appear when hovering your mouse over a control in the inspector? Well look no further then Unity’s ToolTip attribute that you can apply to your MonoBehaviour’s fields!

using UnityEngine;
using System.Collections;

public class ExampleClass : MonoBehaviour {
    [Tooltip("Health value between 0 and 100.")]
    public int health = 0;
}

Sometimes it is handy to keep a game object alive for as long as your game is running. In order to prevent your object from being destroyed when loading a new scene use GameObject.DontDestroyOnLoad and your game object will persist between scene changes.


If you need to retrieve the default Arial font via code at runtime you can do so using the Resources.GetBuiltInResource method.

var font = Resources.GetBuiltinResource(typeof(Font), "Arial.ttf") as Font;

If you need to stylize your gui you can use the GUISettings class to change, the cursor color, flash speed, the selection color for text fields, as well as double click behavior.

    public class GuiSettingsExample : MonoBehaviour
    {
        public Color cursorColor;     
        public float flashSpeed;      
        public bool doubleClickSelectWord;       
        public Color selectionColor;             
        public bool tripleCLickLine;             
        private string text = "test string";     
        private Vector2 scroll;                  
        public GUISkin skin;                     

        public void OnGUI()
        {
            GUI.skin = this.skin;
            var settings = GUI.skin.settings;
            settings.cursorColor = this.cursorColor;
            settings.cursorFlashSpeed = this.flashSpeed;
            settings.doubleClickSelectsWord = this.doubleClickSelectWord;
            settings.selectionColor = this.selectionColor;
            settings.tripleClickSelectsLine = this.tripleCLickLine;


            this.scroll = GUILayout.BeginScrollView(this.scroll, false, false, GUILayout.ExpandWidth(true), GUILayout.ExpandHeight(true));
            this.text = GUILayout.TextArea(this.text);
            GUILayout.EndScrollView();
        }
    }

// <copyright>
//   Copyright (c) 2012 Codefarts
//   All rights reserved.
//   contact@codefarts.com
//   http://www.codefarts.com
// </copyright>

namespace Codefarts.ObjectPooling
{
    using System;

    /// <summary>
    /// Provides a generic object pooling manager.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class ObjectPoolManager<T> where T : class
    {
        /// <summary>
        /// Holds a reference to a singleton instance.
        /// </summary>
        private static ObjectPoolManager<T> instance;

        /// <summary>
        /// Used to track the number of items that have been pushed to the pool.
        /// </summary>
        private int count;

        /// <summary>
        /// Holds the pooled item references.
        /// </summary>
        private T[] cachedItems = new T[10000];


        /// <summary>
        /// Gets or sets the creation callback.
        /// </summary>
        public Func<T> CreationCallback { get; set; }

        /// <summary>
        /// Gets the number of items in the pool.
        /// </summary>
        public int Count
        {
            get
            {
                return this.count;
            }
        }

        /// <summary>
        /// Pops a item from the pool.
        /// </summary>
        /// <returns>A pooled object reference.</returns>
        /// <exception cref="System.NullReferenceException">'CreationCallback' property must be set if you try to pop a item and there are no items available.</exception>
        public T Pop()
        {
            // lock here to prevent treading conflicts with array manipulation
            lock (this.cachedItems)
            {
                // check if there are any pooled objects
                if (this.count < 1)
                {
                    // check if creation callback is null
                    if (this.CreationCallback == null)
                    {
                        throw new NullReferenceException("'CreationCallback' property must be set if you try to pop a item and there are no items available.");
                    }

                    // there are no available objects so create a new one.
                    return this.CreationCallback();
                }

                // reduce the count
                this.count--;

                // retrieve the item and return it
                return this.cachedItems[this.count];
            }
        }

        /// <summary>
        /// Pushes the specified value.
        /// </summary>
        /// <param name="value">The value to push into the pool.</param>
        public void Push(T value)
        {
            // lock here to prevent treading conflicts with array manipulation
            lock (this.cachedItems)
            {
                // update the count
                this.count++;

                // if we need more room for storage increase the size of the cache array
                if (this.count > this.cachedItems.Length)
                {
                    Array.Resize(ref this.cachedItems, this.cachedItems.Length * 2);
                }

                // store the value 
                this.cachedItems[this.count - 1] = value;
            }
        }

        /// <summary>
        /// Gets the singleton instance of the class.
        /// </summary>
        public static ObjectPoolManager<T> Instance
        {
            get
            {
                return instance ?? (instance = new ObjectPoolManager<T>());
            }
        }
    }
}

And some unit tests to go along with it.

// <copyright>
//   Copyright (c) 2012 Codefarts
//   All rights reserved.
//   contact@codefarts.com
//   http://www.codefarts.com
// </copyright>

namespace Codefarts.Tests.ObjectPooling
{
    using System;

    using Codefarts.ObjectPooling;

    using Microsoft.VisualStudio.TestPlatform.UnitTestFramework;

    [TestClass]
    public class ObjectPoolingManagerTests
    {
        ObjectPoolManager<TestObject> manager;

        [TestInitialize]
        public void Setup()
        {
            this.manager = new ObjectPoolManager<TestObject>();
        }

        [TestCleanup]
        public void Cleanup()
        {
            this.manager = null;
        }

        public class TestObject
        {
            public string stringValue;
            public int intValue;
        }

        [TestMethod]
        public void Pop_With_Empty_Pool_NoCallback()
        {
            try
            {
                var item = this.manager.Pop();
                Assert.IsNotNull(item);
            }
            catch (Exception ex)
            {
                Assert.IsTrue(ex is NullReferenceException);
            }
        }

        [TestMethod]
        public void Pop_With_Empty_Pool_WithCallback()
        {
            try
            {
                this.manager.CreationCallback = this.Callback;
                var item = this.manager.Pop();
                Assert.IsNotNull(item);
                Assert.AreEqual(0, item.intValue);
                Assert.AreEqual(null, item.stringValue);
            }
            catch (Exception ex)
            {
                Assert.IsTrue(ex is NullReferenceException);
            }
        }

        private TestObject Callback()
        {
            return new TestObject();
        }

        [TestMethod]
        public void Push_Object()
        {
            try
            {
                Assert.AreEqual(0, this.manager.Count);
                this.manager.Push(new TestObject());
                Assert.AreEqual(1, this.manager.Count);
            }
            catch (Exception ex)
            {
                Assert.Fail(ex.ToString());
            }
        }

        [TestMethod]
        public void Push_Pop_Objects()
        {
            try
            {
                Assert.AreEqual(0, this.manager.Count);
                for (var i = 0; i < 3; i++)
                {
                    this.manager.Push(new TestObject() { stringValue = "Item" + i, intValue = i });
                }

                Assert.AreEqual(3, this.manager.Count);

                for (var i = 3 - 1; i >= 0; i--)
                {
                    var item = this.manager.Pop();
                    Assert.AreEqual(i, item.intValue);
                    Assert.AreEqual("Item" + i, item.stringValue);
                }

                Assert.AreEqual(0, this.manager.Count);
            }
            catch (Exception ex)
            {
                Assert.Fail(ex.ToString());
            }
        }
    }
}

Did you know that the GUI class has a matrix property that you can use to rotate and scale your gui elements.

The sample behavior that is provided below will scale and rotate a gui label in the center of the screen.

public class GuiMatrixDemo : MonoBehaviour
{
    private float rotation;

    /// <summary>
    /// OnGUI is called for rendering and handling GUI events.
    /// </summary>
    public void OnGUI()
    {
        var matrix = GUI.matrix;

        GUI.Label(new Rect(5, 5, 100, 20), "before matrix");

        this.rotation += 15f * Time.deltaTime;
        var scale = Mathf.Clamp((float)Math.Sin(Time.time) + 1 * 2, 1, 3);
        GUI.matrix = Matrix4x4.TRS(new Vector3(Screen.width / 2, Screen.height / 2, 0), Quaternion.Euler(0, 0, this.rotation), Vector3.one * scale);
        var size = GUI.skin.label.CalcSize(new GUIContent("test string"));
        var rect = new Rect((-size.x / 2f), (-size.y / 2f), size.x, size.y);
        GUI.Label(rect, "test string");
        GUI.matrix = matrix;
        GUI.Label(new Rect(5, 25, 100, 20), "after matrix");
    }
}

Created by: X

Just another personal website in this crazy online world

Name of author Dean Lunz (aka Created by: X)
Computer programming nerd, and tech geek.
About Me -- Resume