Tip of the day

From Unify Community Wiki
(Difference between revisions)
Jump to: navigation, search
(archived all tips from 2012 to it's own archived page Tip of the day entries for 2012)
m (Added tips 74 to 80)
Line 1: Line 1:
 +
== April 2015 ==
 +
* ''' Tip #80 – Unity5 Material Reference Charts '''
 +
 +
The unity 5 documentation has a couple of handy reference charts/Info Graphics 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 http://docs.unity3d.com/Manual/StandardShaderMaterialCharts.html]
 +
 +
[[File:StandardShaderCalibrationChartMetallic.png]]
 +
[[File:StandardShaderCalibrationChartSpecular.png]]
 +
 +
== March 2015 ==
 +
* ''' Tip #79 – Unity 5 Transform performance '''
 +
With the introduction to [http://www.unity3d.com/ Unity 5] there comes some [http://blogs.unity3d.com/2014/06/23/unity5-api-changes-automatic-script-updating/ 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.
 +
 +
[[File:Unity5GetComponentPerformanceTests.png]]
 +
 +
The code for the [http://www.createdbyx.com/post/2013/03/27/Code-Snippets-12-%E2%80%93-Performance-Testing.aspx PerformanceTesting class is available here].
 +
 +
TransformCachedGetComponentPerformance script
 +
 +
<syntaxhighlight lang="csharp">
 +
    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
 +
        }
 +
    }
 +
</syntaxhighlight>
 +
 +
TransformGetComponentPerformance script
 +
 +
<syntaxhighlight lang="csharp">
 +
    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
 +
        }
 +
    }
 +
</syntaxhighlight>
 +
 +
TransformFieldPerformance script
 +
 +
<syntaxhighlight lang="csharp">
 +
    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
 +
        }
 +
    }
 +
</syntaxhighlight>
 +
 +
== December 2014 ==
 +
* ''' Tip #78 – Lock the mouse cursor to the screen '''
 +
 +
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 [http://docs.unity3d.com/ScriptReference/Screen-lockCursor.html Screen.lockCursor].
 +
 +
<syntaxhighlight lang="csharp">
 +
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;
 +
        }
 +
    }
 +
}
 +
</syntaxhighlight>
 +
 +
* ''' Tip #77 – ToolTip Attribute for MonoBehaviour fields '''
 +
 +
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 [http://docs.unity3d.com/ScriptReference/TooltipAttribute.html ToolTip] attribute that you can apply to your MonoBehaviour’s fields!
 +
 +
<syntaxhighlight lang="csharp">
 +
using UnityEngine;
 +
using System.Collections;
 +
 +
public class ExampleClass : MonoBehaviour {
 +
    [Tooltip("Health value between 0 and 100.")]
 +
    public int health = 0;
 +
}
 +
</syntaxhighlight>
 +
 +
== November 2014 ==
 +
* ''' Tip #76 – Don’t Destroy On Scene Load '''
 +
 +
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 [http://docs.unity3d.com/ScriptReference/Object.DontDestroyOnLoad.html GameObject.DontDestroyOnLoad] and your game object will persist between scene changes.
 +
 +
* ''' Tip #75 – Get default Arial font at runtime using code '''
 +
 +
If you need to retrieve the default Arial font via code at run time you can do so using the Resources.GetBuiltInResource method.
 +
 +
<syntaxhighlight lang="csharp">
 +
var font = Resources.GetBuiltinResource(typeof(Font), "Arial.ttf") as Font;
 +
</syntaxhighlight>
 +
 +
* ''' Tip #74 – Change text field properties with GUISettings '''
 +
 +
If you need to stylize your gui you can use the [http://docs.unity3d.com/ScriptReference/GUISettings.html GUISettings] class to change, the cursor color, flash speed, the selection color for text fields, as well as double click behavior.
 +
 +
<syntaxhighlight lang="csharp">
 +
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();
 +
    }
 +
}
 +
</syntaxhighlight>
 +
 
== May 2014 ==
 
== May 2014 ==
 
* ''' Tip #73 – GUI Matrix '''
 
* ''' Tip #73 – GUI Matrix '''

Revision as of 00:13, 3 April 2015

Contents

April 2015

  • Tip #80 – Unity5 Material Reference Charts

The unity 5 documentation has a couple of handy reference charts/Info Graphics 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

StandardShaderCalibrationChartMetallic.png StandardShaderCalibrationChartSpecular.png

March 2015

  • Tip #79 – Unity 5 Transform performance

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.

Unity5GetComponentPerformanceTests.png

The code for the PerformanceTesting class is available 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
        }
    }

December 2014

  • Tip #78 – Lock the mouse cursor to the screen

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;
        }
    }
}
  • Tip #77 – ToolTip Attribute for MonoBehaviour fields

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

November 2014

  • Tip #76 – Don’t Destroy On Scene Load

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.

  • Tip #75 – Get default Arial font at runtime using code

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

 
var font = Resources.GetBuiltinResource(typeof(Font), "Arial.ttf") as Font;
  • Tip #74 – Change text field properties with GUISettings

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

May 2014

  • Tip #73 – GUI Matrix

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

The sample behaviour 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");
    }
}

August 2013

  • Tip #72 - PropertyDrawer

Ever wish you could draw properties for serializable classes and script variables using your own gui logic? Unity 4 introduces the PropertyDrawer base class to do just that!

  • Tip #71 – Right click “Create unity project”

Not a unity specific tip but still a handy unity helper for creating a unity project from file explorer. *Works only for windows platforms.* Download the CreateUnityProject.reg file below or open up Notepad and paste the snippet below then save the file with a *.reg file extension. Next navigate to the file and right click on it and select “Merge” from the popup menu. You should now be able to create a new unity project by right clicking on a empty folder in File Explorer and selecting “Create unity project”.

CreateUnityProject.reg

CreateUnityProjectRightClick.png

Windows Registry Editor Version 5.00
 
[HKEY_CLASSES_ROOT\Folder\shell\Create unity project]
 
[HKEY_CLASSES_ROOT\Folder\shell\Create unity project\command]
@="\"C:\\Program Files (x86)\\Unity\\Editor\\unity.exe\" -createProject \"%1\""

July 2013

  • Tip #70 – 50 Tips in one!

I came across this article while searching for a way of unit testing my unity GUI windows. A link to the article is available here 50 Tips for Working with Unity (Best Practices). It contains some very sound advice and is well worth the time to read through.

  • Tip #69 – Let it shine!

Seems like a no brainer but setting up a empty prefab with one or more lights in it can allow you to quickly light up your test scene quick and easy with predictable results. It also helps when you want to keep the same consistent lighting modal from scene to scene. Simply change the lighting in the prefab and hit apply to update all scenes that use it.

  • Tip #68 – Show Notification

Ever wonder how some unity extensions show a notification overlay? Check out the EditorWindow.ShowNotification method.

Notification.png

  • Tip #67 – Get and set build order of scenes

You can get access to the build order for scenes in your editor scripts by calling EditorBuildSettings.scenes. This allows you to control what scenes are included with the build through your editor code.

 
public class SceneDump
{
    [MenuItem("Codefarts/Dump Scenes")]
    public static void Dump()
    {
        var parts = from part in EditorBuildSettings.scenes
                    select string.Format("{0} - {1}", part.enabled, part.path);
 
        Debug.Log(string.Join("\r\n", parts.ToArray()));
    }
}

BuildScenes.png

May 2013

  • Tip #66 - QueueGameViewInputEvent

Need to send simulated user input to the game window? Unity provides the EditorGUIUtility.QueueGameViewInputEvent method for doing just that. Using this method you could construct a utility that captures and plays back user input as your game is running which would be handy for debugging and unit testing purposes.

  • Tip #65 – Get asset path of UnityEngine.Object

Below is a simple bit of code that will attempt to retrieve the asset path of the prefab stored on disk. The method will also try to walk up the prefab hierarchy if the prefab parameter appears to be a instance of the original.

 
/// <summary>
/// Gets the source path to a prefab if any.
/// </summary>
/// <param name="prefab">The prefab reference to get the asset path for.</param>
/// <returns>Returns a asset path for a prefab.</returns>
/// <remarks>This method will attempt to find the source asset of the given <see cref="UnityEngine.Object"/> by
/// walking up the parent prefab hierarchy.</remarks>
public static string GetSourcePrefab(UnityEngine.Object prefab)
{
    // if no prefab specified then return null
    if (prefab == null)
    {
        return null;
    }
 
    // attempt to get the path
    var path = AssetDatabase.GetAssetPath(prefab);
 
    // if no path returned it may be an instantiated prefab so try to get the parent prefab
    while (String.IsNullOrEmpty(path))
    {
        // try parent prefab
        var parent = PrefabUtility.GetPrefabParent(prefab);
 
        // no parent so must be generated through code so just exit loop
        if (parent == null)
        {
            break;
        }
 
        // attempt to get path for
        path = AssetDatabase.GetAssetPath(parent);
 
        // set prefab reference to parent for next loop
        prefab = parent;
    }
 
    // return the path if any
    return path;
}

April 2013

  • Tip #64 – Detect when unity compiles scripts

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 re-compiles your scripts the field will loose it’s reference and will be null again.

  • Tip #63 – Right click “Open with Unity”

Not a unity specific tip but still a handy unity helper for opening a unity project from file explorer. *Works only for windows platforms.* Download the OpenInUnity.reg file below or open up Notepad and paste the snippet below then save the file with a *.reg file extension. Next navigate to the file and right click on it and select “Merge” from the popup menu. You should now be able to open a unity project folder by right clicking on it in File Explorer and selecting “Open with Unity”.

File:OpenInUnity.zip (Contains OpenInUnity.reg file)

OpenWithUnity.png

 
Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\Folder\shell\Open with Unity\command]
@="\"C:\\Program Files (x86)\\Unity\\Editor\\unity.exe\" -projectPath \"%1\""
  • Tip #62 - Generate Unique Asset Path

Unity comes with a helper method for generating a unique asset paths called AssetDatabase.GenerateUniqueAssetPath. The documentation for this method is rather minimal but the code example below should help you understand how to use it a little better.

UniqueAssetPath.png

 
[MenuItem("CBX/Test")]
public static void Test()
{
    // outputs "Assets/Rules.xml" if "Assets/Rules.xml" file does not already exist.
    // if "Assets/Rules.xml" already exists it outputs "Assets/Rules 1.xml" 
    Debug.Log(AssetDatabase.GenerateUniqueAssetPath("Assets/Rules.xml"));
 
    // expects path to start with "Assets/" & outputs a console error in the console "pathName.find("assets/") == 0" 
    // then outputs "Rules.xml"
    Debug.Log(AssetDatabase.GenerateUniqueAssetPath("Rules.xml"));
}
  • Tip #61 – DrawGenericGrid & DrawCheckBoxGrid

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();

Selectiongrid.png

  • Tip #60 – Set mouse cursor for unity UI control

You can set the mouse cursor for a UI control by using EditorGUIUtility.AddCursorRect and specifying a MouseCursor enum.

if (GUILayout.Button("Add"))
{
 
}
 
// show the "Link" cursor when the mouse is hovering over this rectangle.
EditorGUIUtility.AddCursorRect(GUILayoutUtility.GetLastRect(), MouseCursor.Link);

March 2013

  • Tip #59 – PingObject

Ever wonder how unity makes assets in the project window pop out when you select a link to them in the inspector? See the EditorGUIUtility.PingObject method.

PingObject.png

[MenuItem("Examples/Ping Selected")]
static void Ping()
{
    if (!Selection.activeObject)
    {
        Debug.LogError("Select an object to ping");
        return;
    }
    EditorGUIUtility.PingObject(Selection.activeObject);
}
  • Tip #58 – LookLikeControls & LookLikeInspector

If you are writing UI code and you want the controls you are drawing to appear as if they are labels until they are in focus you can use the EditorGUIUtility.LookLikeInspector & EditorGUIUtility.LookLikeControls methods.

LookLikeControls.png

public class LookLikeControlsInspector : EditorWindow
{
    private int integer1;
    float float1 = 5.5f;
 
    [MenuItem("Examples/Look Like Controls - Inspector")]
    static void Init()
    {
        var window = GetWindow<LookLikeControlsInspector>();
        window.Show();
    }
 
    void OnGUI()
    {
        EditorGUIUtility.LookLikeInspector();
        EditorGUILayout.TextField("Text Field:", "Hello There");
        EditorGUILayout.IntField("Int Field:", integer1);
        EditorGUILayout.FloatField("Float Field:", float1);
        EditorGUILayout.Space();
        EditorGUIUtility.LookLikeControls();
        EditorGUILayout.TextField("Text Field", "Hello There");
        EditorGUILayout.IntField("Int Field:", integer1);
        EditorGUILayout.FloatField("Float Field:", float1);
    }
}
  • Tip #57 – Resource.Load/LoadAll Gotchas

I recently came across a strange behavior while loading text resource assets. In particular the Resources.LoadAll method does not accept Path.DirectorySeparatorChar characters in a path. In fact it only accepts Path.AltDirectorySeparatorChar characters. This behavior is different then standard .net file/folder methods that accept either Path.AltDirectorySeparatorChar or Path.DirectorySeparatorChar without distinction. What this means is that you can’t directly use Path.Combine to build a path and pass it to the Resources.LoadAll method you first have to replace any Path.DirectorySeparatorChar characters with Path.AltDirectorySeparatorChar characters.

The documentation for Resources.Load also does not mention this behavior.

I have submitted a bug report here.

 using System.IO;
 
using UnityEditor;
 
using UnityEngine;
 
/// <summary>
/// Handles settings registration.
/// </summary>
[InitializeOnLoad]
public class EditorInitialization
{
    /// <summary>
    /// Holds a value indicating whether the RunCallbacks method has been called at least once before.
    /// </summary>
    private static bool ranOnce;
 
    /// <summary>
    /// Initializes static members of the <see cref="EditorInitialization"/> class.
    /// </summary>
    static EditorInitialization()
    {
        EditorApplication.update += RunCallbacks;
    }
 
    private static void RunCallbacks()
    {
        if (!ranOnce)
        {
            // try to load resource
            var path = Path.Combine("Test/SubFolder", "testfile"); // result is Test/SubFolder\testfile
 
            // var data = Resources.LoadAll("Test/SubFolder/testfile", typeof(TextAsset)); // this line is successful
            var data = Resources.LoadAll(path, typeof(TextAsset));  // this line fails
 
            if (data != null && data.Length != 0)
            {
                Debug.Log("found it");
            }
            else
            {
                Debug.Log("not found! " + path);
            }
 
            ranOnce = true;
            return;
        }
 
        // do stuff
    }
}

Febuary 2013

  • Tip #56 – Get project folder & current directory

There is no Unity specific API for getting the project folder, but you can use System.IO.Directory.GetCurrentDirectory. The unity editor expects the current folder to be set to the project folder at all times.

If you are changing the current directory in your editor scripts using System.IO.Directory.SetCurrentDirectory you need to restore the directory back to the project folder when you are done. Otherwise the next time unity compiles your scripts it will prompt you with a dialog box and a message stating you need to restore the current directory back to the project directory and only gives you a “Quit” button to click on that will quit unity.

// save project folder
var projectFolder = System.IO.Directory.GetCurrentDirectory();
 
// set current directory to a folder of your choosing
System.IO.Directory.SetCurrentDirectory("c:\\some folder name");
 
// do what you need to do
// ...
 
// restore current folder back the the project folder
System.IO.Directory.SetCurrentDirectory(projectFolder);
  • Tip #55 – InitializeOnLoad & Resources.Load gotcha’s

I’ve recently come across what initially seems like a bug with the Unity 4 editor. it involves the use of the Resources.Load method from within a class marked InitializeOnLoad.

Take the fallowing example code …

namespace CBX.CoreProjectCode
{
    using UnityEditor;
 
    using UnityEngine;
 
    [InitializeOnLoad]
    public class EditorInitialization
    {
        static EditorInitialization()
        {
            LoadData(); // this will fail to see the test.txt file in the resource folder
        }     
 
        private static void LoadData()
        {
            // try to load data
            var data = Resources.Load("test", typeof(TextAsset)) as TextAsset;
            if (data == null)
            {
                Debug.LogWarning("No data found");
                return;
            }
 
            Debug.Log(data.text);
        }
    }
}

After Unity compiles the scripts it will invoke any static constructors of classes marked with InitializeOnLoad. But at this point I suspect that Unity has not yet identified assets you have within any Resources folders. When the class constructor calls the Resources.Load method it will fail to read the test.txt resource file and return a warning message out to the console.

What you could do at this point is right click the “Assets” folder in the project window and select “Reimport”. Unity will reimport the assets and again call classes marked with InitializeOnLoad. But this time Resources.load will successfully load the test.txt file resource and display it’s contents out to the Unity console window.

One work around to this is to hook into the EditorApplication.update callback and make a call to the LoadData method from there. An example is provided below …

namespace CBX.CoreProjectCode
{
    using UnityEditor;
 
    using UnityEngine;
 
    [InitializeOnLoad]
    public class EditorInitialization
    {
        private static bool ranOnce;
 
        static EditorInitialization()
        {
            EditorApplication.update += RunCallbacks;
        }
 
        private static void RunCallbacks()
        {
            if (!ranOnce)
            {
                // this will see the test.txt file in the Resource folder and display
                // its contents
                LoadData();
                ranOnce = true;
 
                // return or don't it’s up to you
                return;
            }
 
            // do some work here
        }
 
        private static void LoadData()
        {
            var data = Resources.Load("test", typeof(TextAsset)) as TextAsset;
            if (data == null)
            {
                Debug.LogWarning("No data found");
                return;
            }
 
            Debug.Log(data.text);
        }
    }
}

Again I am unsure whether this is an actual bug or not but I have submitted a Unity bug report anyway just in case. Here is a link to the my [bug report]

  • Tip #54 – ScaleTransform

The code below allows you to scale a GameObject transform to a specific size along the x & z axis by taking into account the GameObjects renderer bounds if a renderer component is attached.

/// <summary>
/// Scales a transform to specific dimensions along the x & z axis.
/// </summary>
/// <param name="transform">
/// Reference to the transform to scale.
/// </param>
/// <param name="width">The width along the x axis that represents the target size.</param>
/// <param name="height">The height along the z axis that represents the target size.</param>
public static void ScaleTransform(Transform transform, float width, float height)
{
    // get bounds of the prefab
    var bounds = new Bounds();
    var encapsulate = false;
    if (!Utilities.Helpers.GetBoundWithChildren(transform, ref bounds, ref encapsulate))
    {
        return;
    }
 
    // get minimum size from the size dimensions
    var min = Mathf.Min(width, height);
 
    // get the maximum x or z size of the transform
    var max = Mathf.Max(bounds.size.x, bounds.size.z);
 
    // calculate the scale factor 
    var scaleFactor = min / max;
 
    // apply scaling to the transform
    transform.localScale *= scaleFactor;
}
 
/// <summary>
/// Gets the rendering bounds of the transform.
/// </summary>
/// <param name="transform">The game object to get the bounding box for.</param>
/// <param name="pBound">The bounding box reference that will </param>
/// <param name="encapsulate">Used to determine if the first bounding box to be 
/// calculated should be encapsulated into the <see cref="pBound"/> argument.</param>
/// <returns>Returns true if at least one bounding box was calculated.</returns>
public static bool GetBoundWithChildren(Transform transform, ref Bounds pBound, ref bool encapsulate)
{
    var didOne = false;
 
    // get 'this' bound
    if (transform.gameObject.renderer != null)
    {
        var bound = transform.gameObject.renderer.bounds;
        if (encapsulate)
        {
            pBound.Encapsulate(bound.min);
            pBound.Encapsulate(bound.max);
        }
        else
        {
            pBound.min = bound.min;
            pBound.max = bound.max;
            encapsulate = true;
        }
 
        didOne = true;
    }
 
    // union with bound(s) of any/all children
    foreach (Transform child in transform)
    {
        if (GetBoundWithChildren(child, ref pBound, ref encapsulate))
        {
            didOne = true;
        }
    }
 
    return didOne;
}
  • Tip #53 – Undo.IncrementCurrentEventIndex

Using the Undo.IncrementCurrentEventIndex allows your editor scripts to undo a single action rather then have unity undo many actions that may have occurred in rapid succession. Place the code below into a “Editor” folder for it to run.

The “create object” button will create 10 cubes and call Undo.RegisterCreatedObjectUndo for each of them. Click this button then press ctrl+z to undo all of the objects that were created.

The second button “create single object” will also create 10 cubes but will also call the Undo.IncrementCurrentEventIndex method notifying unity that each object that is created should have a separate undo action. Click “create single object” to create 10 cubes then press ctrl+z to undo each object creation individually.

NOTE: At the time of this writing the unity documentation does not have information on the Undo.IncrementCurrentEventIndex method and searching for it also comes back with zero results.

    using UnityEngine;
    using UnityEditor;
 
    public class UndoTest : EditorWindow
    {
 
        public void OnGUI()
        {
            if (GUILayout.Button("create object"))
            {
                this.Create(false);
            }
            if (GUILayout.Button("create single object"))
            {
                this.Create(true);
            }
 
            GUILayout.Label("use ctrl+z to undo each action");
        }
 
        private void Create(bool single)
        {
            for (int i = 0; i < 10; i++)
            {
                if (single)
                {
                    Undo.IncrementCurrentEventIndex();
                }
 
                var obj = GameObject.CreatePrimitive(PrimitiveType.Cube);
                obj.transform.position = new Vector3(Random.Range(-10, 10), Random.Range(-10, 10), Random.Range(-10, 10));
 
                Undo.RegisterCreatedObjectUndo(obj, "create " + obj.name);
            }
        }
 
        [MenuItem("CBX/UndoTest")]
        public static void ShowWindow()
        {
            GetWindow<UndoTest>("UndoTest").Show();
        }
    }
  • Tip #52 – Providing proper links in version changes

If you are submitting packages to the asset store and are including version changes that contain html links, remember to specify target="_blank" in your link attributes. Failure to do so can result in the asset store window being replaced by the web page that your link was pointing to. In some occasions it can also cause unity to crash.

Unity101Tip52.png

  • Tip #51 – Texture Atlas support

I have been thinking that because texture atlases are so common Unity should have some build in support for generating them. Turns out it already does! Check out the Texture2D.PackTextures method.

  • Tip #50 – Ensure conditional compilation symbols are present

If your Unity code requires conditional compilation symbols to be present this bit of code may come in handy. After unity compiles your scripts it executes any classes that have the InitializeOnLoad attribute. You can call the SetupConditionalCompilation method provided in the code snippet below to ensure that the conditional compilation symbols persist in your unity project. If a symbol was not present and was added it will write out a notification in the unity console. ConditionalStartup.png

[InitializeOnLoad]
public class GridMappingSetup
{
    static GridMappingSetup()
    {
        var types = new[] { BuildTargetGroup.Standalone, BuildTargetGroup.WebPlayer };
        var toInclude = new[] { "CBXControls", "GridMapping", "QuickTools", "ToolService", "TileMaterialCreation" };
 
        SetupConditionalCompilation(types, toInclude);
    }
}
 
  public static void SetupConditionalCompilation(BuildTargetGroup[] platformTargets, string[] symbolsToInclude)
        {
            foreach (var type in platformTargets)
            {
                var hasEntry = new bool[symbolsToInclude.Length];
                var conditionals = PlayerSettings.GetScriptingDefineSymbolsForGroup(type).Trim();
                var parts = conditionals.Split(';');
                var changed = false;
 
                foreach (var part in parts)
                {
                    for (int i = 0; i < symbolsToInclude.Length; i++)
                    {
                        if (part.Trim() == symbolsToInclude[i].Trim())
                        {
                            hasEntry[i] = true;
                            break;
                        }
                    }
                }
 
                for (int i = 0; i < hasEntry.Length; i++)
                {
                    if (!hasEntry[i])
                    {
                        conditionals += (String.IsNullOrEmpty(conditionals) ? String.Empty : ";") + symbolsToInclude[i];
                        changed = true;
                    }
                }
 
                PlayerSettings.SetScriptingDefineSymbolsForGroup(type, conditionals);
 
                if (changed)
                {
                    Debug.Log(String.Format("Updated player conditional compilation symbols for {0}: {1}", type, conditionals));
                }
            }
        }

January 2013

  • Tip #49 – Visualizing Render Bounds

Here is a C# script for visualizing render bounds. Just attach it to a game object and it will draw the render bounds of the game object and all it’s children. Handy for debugging! RenderBoundsScript.png

    public class RendererBoundsGizmo : MonoBehaviour
    {
        public bool ShowCenter;
 
        public Color Color = Color.white;
 
        public bool DrawCube = true;
 
        public bool DrawSphere = false;
 
        /// <summary>
        /// When the game object is selected this will draw the gizmos
        /// </summary>
        /// <remarks>Only called when in the Unity editor.</remarks>
        private void OnDrawGizmosSelected()
        {
            Gizmos.color = this.Color;
 
            // get renderer bonding box
            var bounds = new Bounds();  
            var initBound = false;
            if (CBX.Utilities.Helpers.GetBoundWithChildren(this.transform, ref bounds, ref initBound))
            {
                if (this.DrawCube)
                {
                    Gizmos.DrawWireCube(bounds.center, bounds.size);
                }
                if (this.DrawSphere)
                {
                    Gizmos.DrawWireSphere(bounds.center, Mathf.Max(Mathf.Max(bounds.extents.x, bounds.extents.y), bounds.extents.z));
                }
            }
 
            if (this.ShowCenter)
            {
                Gizmos.DrawLine(new Vector3(bounds.min.x, bounds.center.y, bounds.center.z), new Vector3(bounds.max.x, bounds.center.y, bounds.center.z));
                Gizmos.DrawLine(new Vector3(bounds.center.x, bounds.min.y, bounds.center.z), new Vector3(bounds.center.x, bounds.max.y, bounds.center.z));
                Gizmos.DrawLine(new Vector3(bounds.center.x, bounds.center.y, bounds.min.z), new Vector3(bounds.center.x, bounds.center.y, bounds.max.z));
            }
 
            Handles.BeginGUI();
            var view = SceneView.currentDrawingSceneView;
            var pos = view.camera.WorldToScreenPoint(bounds.center);
            var size = GUI.skin.label.CalcSize(new GUIContent(bounds.ToString()));
            GUI.Label(new Rect(pos.x - (size.x / 2), -pos.y + view.position.height + 4, size.x, size.y), bounds.ToString());
            Handles.EndGUI();
        }
    }

And also the code for the GetBoundsWithChildren method.

        /// <summary>
        /// Gets the rendering bounds of the transform.
        /// </summary>
        /// <param name="transform">The game object to get the bounding box for.</param>
        /// <param name="pBound">The bounding box reference that will </param>
        /// <param name="encapsulate">Used to determine if the first bounding box to be calculated should be encapsulated into the <see cref="pBound"/> argument.</param>
        /// <returns>Returns true if at least one bounding box was calculated.</returns>
        public static bool GetBoundWithChildren(Transform transform, ref Bounds pBound, ref bool encapsulate)
        {
            var bound = new Bounds();
            var didOne = false;
 
            // get 'this' bound
            if (transform.gameObject.renderer != null)
            {
                bound = transform.gameObject.renderer.bounds;
                if (encapsulate)
                {
                    pBound.Encapsulate(bound.min);
                    pBound.Encapsulate(bound.max);
                }
                else
                {
                    pBound.min = bound.min; 
                    pBound.max = bound.max; 
                    encapsulate = true;
                }
 
                didOne = true;
            }
 
            // union with bound(s) of any/all children
            foreach (Transform child in transform)
            {
                if (GetBoundWithChildren(child, ref pBound, ref encapsulate))
                {
                    didOne = true;
                }
            }
 
            return didOne;
        }


  • Tip #48 – Refresh Inspector

Recently I needed to force the inspector to redraw itself from a piece of code outside of a Editor class. The solution I came up with was to call SetDirty on all selected objects of the type I was interested in. The inspector then picked up on the change and refreshed itself immediately. Although this code works under Unity 4 it is a hack and there may be a better way to force the inspector to redraw itself.

var objects = Selection.GetFiltered(typeof(GridMap), SelectionMode.TopLevel);
foreach (var o in objects)
{
    EditorUtility.SetDirty(o);
}

Archived Years

Older Tips

  • When move a GameObject, hold the Key V,will fast align.
  • A skinned character updates its vertices every frame, even when not animating. To freeze a skinned character in place without making it disappear or revert to a T-pose, set its SkinnedMeshRenderer.bones field to null.
  • There are two really nice hotkeys to insert or delete array elements in the inspector. SHIFT + DEL will delete the selected element and CTRL + D will duplicate it. SHIFT is only needed for reference types like GameObject[].
  • When you're using Debug.Log or related methods, provide the instance of the script so you can click the log output and have the relevant object selected in editor! Debug.Log("Test", this);
  • If you're placing objects in the screen and want them to "snap" into place (great for placing items on the ground) you can hold the Command key (Control on Windows) while dragging the object's axis (snap increments are defined in Edit->Snap Settings)
  • Importing classes in JavaScript. Tired of writing Debug.Log and SendMessageOptions.DontRequireReceiver? Did you know that static members of classess and enums can be imported just like importing namespaces. Simply add import UnityEngine.Debug; and import UnityEngine.SendMessageOptions and then you can write Log("my log string") and SendMessage("ApplyDamage",2.0,DontRequireReceiver);.
  • Deleting objects. Use Command + Backspace in the Scene, Hierarchy and Project views to remove objects. In the Scene and Hierarchy views this will just remove the selected instance of the object. In the Project view this will remove the selected object file and put it in your system Trash.
  • If you need some textures, like font textures, to not be affected by Texture.masterTextureLimit, disable Generate Mip Maps in the texture's import settings. On the down side, this can adversely affect clarity of rendering at smaller scales.
  • Boo has some handy built-in functions like shell(), join() and reversed(). And the cool part? They are available from JavaScript as well.
  • Every built-in component in the inspector has a small question mark in the right of it's heading. That's the quickest way to open the context-help for that component. It's your friend.
  • To remember which color means what on an axis in Unity, just remember the mnemonic: RGB = XYZ.
  • You can use keyboard shortcuts for Edit->Load Selection and Edit->Save Selection to speed up the selection of commonly used parts in your scene.
  • If you option-click an object in your hierarchy, the entire hierarchy within it will expand and unexpand.
  • You can write Editor scripts to greatly reduce project-specific tedium when making something with Unity. These are just as easy to write as regular scripts, and can save you a lot of hassle. See the Wizard Archive for some examples.
  • If you have the problem when importing models that Vector3.up points in the wrong direction (or for any of the other directions), make the game model in question the child of a blank game object, and orient it correctly in local space.
  • When assigning properties of components in the Inspector, you can click on the triangle to pop up a menu of choices. Type in the name of the thing you want and press enter to assign it. This can be a speedy alternative to using drag-and-drop.
  • When entering text in the Inspector view, you can press option-return to get a new line. This is useful for writing multiline things with GUIText objects.
  • If you only have one editor view and no game views in the Unity pane set up, the editor view will be automatically replaced with a game view when you enter play mode and back again when you press stop. This is a nice feature when you are working on a small monitor.
  • Use Tags to assign your own categories and attributes to game objects. Then scripts can refer to the tags. Example: if (contact.otherCollider.tag == "Enemy") or GameObject.FindWithTag ("Enemy")
  • If you want to move a file inside of a project use Unity's project view to move it and not the Finder. This way all references and import settings of the file will be maintained.

Note: Place your new tip at the top of the list, not at the bottom.

Personal tools
Namespaces

Variants
Actions
Navigation
Extras
Toolbox