When writing editor scripts involving EditorWindow’s it is often important to initialize and cleanup your code when the window is shown and hidden. Specifically it is important to differentiate between the OnDisable and OnDestroy methods if you need to perform some kind of cleanup before the window is closed or disposed of. OnDestroy is called when the user closes the window, where as OnDisable is called after unity recompiles scripts. Think of the OnDestroy method as a close event for the window but the window still resides in memory, where the OnDisable method signals that the window is about to be unloaded from memory such as during a script recompile.

This differentiation is important when you need to save data to disk before the window is destroyed during a recompile. OnDestroy will not get called during recompile only OnDisable does. The OnEnable method is typically intended as a initialization method where you can load data related to the window.

I only wish the Unity team had made these methods more descriptive ala .NET window forms naming scheme. OnDestroy & OnDisable are not the most descriptive for what they do as well as being somewhat similar in spelling. But I digress.


When compiling your Unity games & applications to different platforms, there may be times when you need to get the location of special folder locations on the system that your app is running on.

The Environment.GetFolderPath method can be use to retrieve those specific folder locations. Below is a list of folder locations for both Windows 7 and Mac OS X. The source of this information is available here along with code examples.


Desktop (0)
Apple.gif Mac OS X 10.6.8 (Snow Leopard) /Users/yourname/Desktop
Windows7.gif Windows 7 C:\Users\yourname\Desktop

Programs (2)
Apple.gif Mac OS X 10.6.8 (Snow Leopard) n/a
Windows7.gif Windows 7 C:\Users\yourname\AppData\Roaming\Microsoft\Windows\Start Menu\Programs

Personal (5)
Apple.gif Mac OS X 10.6.8 (Snow Leopard) /Users/yourname
Windows7.gif Windows 7 C:\Users\yourname\Documents

MyDocuments (5)
Apple.gif Mac OS X 10.6.8 (Snow Leopard) /Users/yourname
Windows7.gif Windows 7 C:\Users\yourname\Documents

Favorites (6)
Apple.gif Mac OS X 10.6.8 (Snow Leopard) /Users/yourname/Library/Favorites
Windows7.gif Windows 7 C:\Users\yourname\Favorites

Startup (7)
Apple.gif Mac OS X 10.6.8 (Snow Leopard) n/a
Windows7.gif Windows 7 C:\Users\yourname\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup

Recent (8)
Apple.gif Mac OS X 10.6.8 (Snow Leopard) n/a
Windows7.gif Windows 7 C:\Users\yourname\AppData\Roaming\Microsoft\Windows\Recent

SendTo (9)
Apple.gif Mac OS X 10.6.8 (Snow Leopard) n/a
Windows7.gif Windows 7 C:\Users\yourname\AppData\Roaming\Microsoft\Windows\SendTo

StartMenu (11)
Apple.gif Mac OS X 10.6.8 (Snow Leopard) n/a
Windows7.gif Windows 7 C:\Users\yourname\AppData\Roaming\Microsoft\Windows\Start Menu

MyMusic (13)
Apple.gif Mac OS X 10.6.8 (Snow Leopard) /Users/yourname/Music
Windows7.gif Windows 7 C:\Users\yourname\Music

DesktopDirectory (16)
Apple.gif Mac OS X 10.6.8 (Snow Leopard) /Users/yourname/Desktop
Windows7.gif Windows 7 C:\Users\yourname\Desktop

MyComputer (17)
Apple.gif Mac OS X 10.6.8 (Snow Leopard) n/a
Windows7.gif Windows 7 n/a

Templates (21)
Apple.gif Mac OS X 10.6.8 (Snow Leopard) /Users/yourname/Templates
Windows7.gif Windows 7 C:\Users\yourname\AppData\Roaming\Microsoft\Windows\Templates

ApplicationData (26)
Apple.gif Mac OS X 10.6.8 (Snow Leopard) /Users/yourname/.config
Windows7.gif Windows 7 C:\Users\yourname\AppData\Roaming

LocalApplicationData (28)
Apple.gif Mac OS X 10.6.8 (Snow Leopard) /Users/username/.local/share (inside unity)
/Applications/Unity/MonoDevelop.app/Contents/MacOS/../Frameworks/Mono.framework/Versions/Current/share
Windows7.gif Windows 7 C:\Users\yourname\AppData\Local

InternetCache (32)
Apple.gif Mac OS X 10.6.8 (Snow Leopard) /Users/yourname/Library/Caches
Windows7.gif Windows 7 C:\Users\yourname\AppData\Local\Microsoft\Windows\Temporary Internet Files

Cookies (33)
Apple.gif Mac OS X 10.6.8 (Snow Leopard) n/a
Windows7.gif Windows 7 C:\Users\yourname\AppData\Roaming\Microsoft\Windows\Cookies

History (34)
Apple.gif Mac OS X 10.6.8 (Snow Leopard) n/a
Windows7.gif Windows 7 C:\Users\yourname\AppData\Local\Microsoft\Windows\History

CommonApplicationData (35)
Apple.gif Mac OS X 10.6.8 (Snow Leopard) /usr/share
Windows7.gif Windows 7 C:\ProgramData

System (37)
Apple.gif Mac OS X 10.6.8 (Snow Leopard) n/a
Windows7.gif Windows 7 C:\Windows\system32

ProgramFiles (38)
Apple.gif Mac OS X 10.6.8 (Snow Leopard) /Applications
Windows7.gif Windows 7 C:\Program Files
or C:\Program Files (x86)

MyPictures (39)
Apple.gif Mac OS X 10.6.8 (Snow Leopard) /Users/yourname/Pictures
Windows7.gif Windows 7 C:\Users\yourname\Pictures

CommonProgramFiles (43)
Apple.gif Mac OS X 10.6.8 (Snow Leopard) n/a
Windows7.gif Windows 7 C:\Program Files\Common Files
or C:\Program Files (x86)\Common Files

If you are writing custom scripts there may be situations where you don’t want the same component added more then once to a game object. For such situations Unity provides the DisallowMultipleComponent attribute that you can specify in your scripts code.


It’s not something people think about these days. Back in the 90’s when MS-DOS was king, alerting the user to a problem involved both a visual alert as well as a auditory alert in the form of a “beep”. It’s actually something I miss from those days. Luckily the unity editor has an API to alert the user and wouldn't you know it, it’s called “Beep”!


If you are developing unity editor extensions often it is necessary to know weather or not unity is compiling the projects scripts. To determine if unity is currently compiling you can simply check the EditorApplication.isCompiling flag in your code before deciding what to do. See older related tip and code example here.


ProjectAndHierarchyEnhancements

Unity provides hooks into the Project and Hierarchy windows OnGui event. This allows you to add additional flair to these windows. To learn how to enhance these windows see the fallowing documentation pages

  1. HierarchyWindowChanged
  2. HierarchyWindowItemOnGUI
  3. ProjectWindowItemOnGUI
  4. ProjectWindowChanged

If you want an example project to work off of and learn from there is an open source project called “UnityEditorEnhancements” that you can download as a zip file or you can fork the Mercurial repository.


The fallowing script allows you to control fog settings on a per camera basis, allowing you to use say green fog for one camera but red fog for another camera.

Unity 5 package demo is available here CameraFog.unitypackage (46.66 kb)

Camera Fog Screen Shot

CameraFog.cs script

// --------------------------------------------------------------------------------------------------------------------
// <copyright file="CameraFog.cs" company="Codefarts">
//   Copyright (c) 2012 Codefarts
//   All rights reserved.
//   contact@codefarts.com
//   http://www.codefarts.com
// </copyright>   
// --------------------------------------------------------------------------------------------------------------------

// Per-camera fog
// This is a simple class that, when added to a GameObject with a camera, allows you to control the fog settings for that camera separately from the global ones. 
// I'd love to hear from you if you do anything cool with this or have any suggestions :)
// Original author: http://wiki.unity3d.com/index.php/User:Tenebrous
// Author website as of 2015: www.tenebrous.co.uk
// Source: http://wiki.unity3d.com/index.php/CameraFog

namespace Codefarts.GeneralTools.Scripts.Camera
{
    using UnityEngine;

    /// <summary>
    /// Modifies a camera to allows you to control the fog settings for that camera separately from the global scene fog or other cameras. 
    /// </summary>
    [RequireComponent(typeof(Camera))]
    [ExecuteInEditMode]
    public class CameraFog : MonoBehaviour
    {
        /// <summary>
        /// The enabled state weather or not fog will be visible.
        /// </summary>
        public bool Enabled;

        /// <summary>
        /// The start distance from the camera where the fog will be drawn.
        /// </summary>
        public float StartDistance;

        /// <summary>
        /// The end distance from the camera where the fog will be drawn.
        /// </summary>
        public float EndDistance;

        /// <summary>
        /// The fog mode that controls how the fog is rendered.
        /// </summary>
        public FogMode Mode;

        /// <summary>
        /// The density of the fog that is rendered.
        /// </summary>
        public float Density;

        /// <summary>
        /// The fog color.
        /// </summary>
        public Color Color;

        /// <summary>
        /// Stores the pre-render state of the start distance.
        /// </summary>
        private float _startDistance;

        /// <summary>
        /// Stores the pre-render state of the end  distance.
        /// </summary>
        private float _endDistance;

        /// <summary>
        /// Stores the pre-render state of the fog mode.
        /// </summary>
        private FogMode _mode;

        /// <summary>
        /// Stores the pre-render state of the density.
        /// </summary>
        private float _density;

        /// <summary>
        /// Stores the pre-render state of the fog color.
        /// </summary>
        private Color _color;
        
        /// <summary>
        /// Stores the pre-render state wheather or not the fog is enabled.
        /// </summary>
        private bool _enabled;

        /// <summary>
        /// Event that is fired before any camera starts rendering.
        /// </summary>
        private void OnPreRender()
        {
            this._startDistance = RenderSettings.fogStartDistance;
            this._endDistance = RenderSettings.fogEndDistance;
            this._mode = RenderSettings.fogMode;
            this._density = RenderSettings.fogDensity;
            this._color = RenderSettings.fogColor;
            this._enabled = RenderSettings.fog;

            RenderSettings.fog = this.Enabled;
            RenderSettings.fogStartDistance = this.StartDistance;
            RenderSettings.fogEndDistance = this.EndDistance;
            RenderSettings.fogMode = this.Mode;
            RenderSettings.fogDensity = this.Density;
            RenderSettings.fogColor = this.Color;
        }

        /// <summary>
        /// Event that is fired after any camera finishes rendering.
        /// </summary>
        private void OnPostRender()
        {
            RenderSettings.fog = this._enabled;
            RenderSettings.fogStartDistance = this._startDistance;
            RenderSettings.fogEndDistance = this._endDistance;
            RenderSettings.fogMode = this._mode;
            RenderSettings.fogDensity = this._density;
            RenderSettings.fogColor = this._color;
        }
    }
}

The unity 5 documentation has a couple of handy reference charts for when you need to help determining what material values to set to get a particular look and feel.

http://docs.unity3d.com/Manual/StandardShaderMaterialCharts.html


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
        }
    }

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;
        }
    }
}

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