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.


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

If you have some editor scripts and you want to detect weather or not unity has compiled your scripts you can try this simple method.

Created a private class within your editor window and declare a field of that private class.

public class YourEditor : EditorWindow
{
    private RecompileClass recompile;

    private class RecompileClass
    {  
    }
}

In the OnGUI method (or where appropriate) perform a check to see if the field is null. If it’s null you can assume that unity compiled your scripts again so do what you need to do is create a new instance of the private class and assign it to the field.

public class YourEditor : EditorWindow
{
    private RecompileClass recompile;
      
    private class RecompileClass
    {
    }

    public void OnGUI()
    {
        // check if recompile variable is null 
        if (this.recompile == null  )
        {
            // if yes assume recompile then create a reference to a recompile class 
            this.recompile = new RecompileClass();

            // do what you need to do here after detecting a recompile  
        }

        // do regular stuff here
    }
}

The next time unity recompiles your scripts the field will loose it’s reference and will be null again.


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”.

OpenInUnity.reg (360.00 bytes)

OpenWithUnity64.reg (452.00 bytes)

OpenWithUnity

For x86 unity use the snippet below

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\""


For 64bit unity use the snippet below

Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\Folder\shell\Open with Unity\command]
@="\"C:\\Program Files\\Unity\\Editor\\unity.exe\" -projectPath \"%1\""

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.

GenerateUniqueAssetPath

[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")); 
}

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

ControlGrid


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


Unity 101 Tip #59 – PingObject

Published 3/25/2013 by createdbyx in Unity | News
Tags: ,

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

[MenuItem("Examples/Ping Selected")]
static void Ping()
{
    if (!Selection.activeObject)
    {
        Debug.LogError("Select an object to ping");
        return;
    }
    EditorGUIUtility.PingObject(Selection.activeObject);
}

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

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

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 –> https://fogbugz.unity3d.com/default.asp?533268_jgvrk2lbu1qm398e

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

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