Jan 14 10

Optimising coroutine yielding in C#

by AngryAnt

When needing to yield a coroutine for a single frame in C#, most people – including myself usually go about it this way:

private IEnumerator DoFadeAlphaIn ()
{
    while (m_Alpha < 1.0f)
    {
        m_Alpha += Time.deltaTime;
        yield return 0;
    }
    m_Alpha = 1.0f;
}

However, as Rodrigo recently brought to my attention, this approach ofcourse performs unneeded memory allocation, given that the zero needs boxing and unboxing before its passed to the coroutine system. Using null in stead saves you that work and allocation.

This might not seem like a lot at first glance, but if you make this a habit every time you need to yield for a frame, I guarantee you that you’ll see results in larger scenarios.

The more optimal way of yielding for a single frame in C#. Minor difference, but allocation saved – which is always awesome:

private IEnumerator DoFadeAlphaIn ()
{
    while (m_Alpha < 1.0f)
    {
        m_Alpha += Time.deltaTime;
        yield return null;
    }
    m_Alpha = 1.0f;
}

An interesting test could be to, on a larger co-routine heavy project, do a project-wide find and replace on “yield return 0;” and “yield return null;” back and forth and measuring the performance difference with the profiler (for non-pro users, maybe the performance gain is even measurable using external monitoring tools).

Jan 5 10

Downloading the hydra

by AngryAnt

So I’ve seen some people asking how you would go about downloading executable code from an external location, reading it into memory and executing it in ones running application.

Now some readers might go "eh? why the heck would someone want to do that?". Well first off, its cool™, but seeing as that is rarely an argument which convinces decision makers, here’s a few others:

  • Patching. The ability to modify the behaviour of your downloaded executable after distribution, without requiring re-download.
  • Highly dynamic online content. Say you’re building a virtual world or something. You might want to have the ability to add more complex behaviours to downloaded content – more than what a data-driven approach allows for.
  • Or how about expanding the capabilities of user generated content?

There. Those are my alibis and I’m sticking to them.

Now this example is quick and simple so as to not bloat the post too much. Regardless of your use, you’ll likely be wanting to add in for instance some cache functionality for real-world use.

Right. So this example contains three pieces of eight, uh, code:

  • The loader utility – responsible for downloading and making available the remote code.
  • The assembly – you know, the one we’re downloading.
  • An example handler. For easy re-use, I’ve designed this example to allow for easy re-use of the loader utility by externalising handling of the assemblies via messages. This example handler shows some techniques for accessing the data of loaded assemblies.

Using the example:

  1. Download and save /Assets/WWWAssemblyLoader.cs, /Assets/NewBehaviourScript.cs and /MyAssembly.cs.
  2. Download and install the mono runtime (or the .net equivalent on Windnows – not tested, but should work just fine).
  3. Build the assembly from the terminal – using the build command supplied at the end of this post.
  4. Upload the assembly to some host (save the URL for later).
  5. Open up your Unity project.
  6. Add the WWWAssemblyLoader and NewBehaviourScript scripts to a GameObject and set the URL property of the first to that of your uploaded assembly.
  7. Press play.
  8. Profit!

And now the codes!

/Assets/WWWAssemblyLoader.cs

using UnityEngine;
using System.Collections;
using System.Reflection;

public class WWWAssemblyLoader : MonoBehaviour
{
    public string m_AssemblyURL;
    private string m_ErrorString = "";
    private WWW m_WWW;
    private bool m_Complete = true;

    public void Start ()
    {
        if (m_AssemblyURL != "")
        {
            ReloadAssembly (m_AssemblyURL);
        }
    }

    public string AssemblyURL
    {
        get
        {
            return m_AssemblyURL;
        }
        set
        {
            if (m_AssemblyURL != value)
            {
                ReloadAssembly (value);
            }
        }
    }

    public float Progress
    {
        get
        {
            return m_Complete ? 1.0f : m_WWW.progress;
        }
    }

    public string Error
    {
        get
        {
            return m_ErrorString;
        }
    }

    public void ReloadAssembly (string url)
    {
        m_Complete = false;
        m_ErrorString = "";
        m_AssemblyURL = url;
        m_WWW = new WWW (m_AssemblyURL);
    }

    public void Update ()
    {
        if (!m_Complete)
        {
            if (m_WWW.error != null)
            {
                m_ErrorString = m_WWW.error;
                m_Complete = true;
                SendMessage ("OnAssemblyLoadFailed", m_AssemblyURL);
            }
            else if (m_WWW.isDone)
            {
                Assembly assembly = LoadAssembly ();
                m_Complete = true;
                if (assembly != null)
                {
                    Debug.Log ("Done");
                    SendMessage ("OnAssemblyLoaded", new WWWAssembly (m_AssemblyURL, assembly));
                }
                else
                {
                    Debug.Log ("Failed");
                    SendMessage ("OnAssemblyLoadFailed", m_AssemblyURL);
                }
            }
        }
    }

    private Assembly LoadAssembly ()
    {
        try
        {
            return Assembly.Load (m_WWW.bytes);
        }
        catch (System.Exception e)
        {
            m_ErrorString = e.ToString ();
            return null;
        }
    }
}

public class WWWAssembly
{
    private string m_URL;
    private Assembly m_Assembly;

    public string URL
    {
        get
        {
            return m_URL;
        }
    }

    public Assembly Assembly
    {
        get
        {
            return m_Assembly;
        }
    }

    public WWWAssembly (string url, Assembly assembly)
    {
        m_URL = url;
        m_Assembly = assembly;
    }
}

/Assets/NewBehaviourScript.cs

using UnityEngine;
using System.Collections;
using System.Reflection;

public class NewBehaviourScript : MonoBehaviour
{
    private string m_MessageString = "Waiting for assembly";

    void OnAssemblyLoaded (WWWAssembly loadedAssembly)
    {
        m_MessageString = "Assembly " + loadedAssembly.URL + "\n";

        System.Type type = loadedAssembly.Assembly.GetType ("MyClass");

        FieldInfo field = type.GetField ("myString");
        m_MessageString += (field.GetValue (null) as string) + "\n";

        object instance = loadedAssembly.Assembly.CreateInstance ("MyClass");
        MethodInfo method = type.GetMethod ("LogMyString");
        m_MessageString += "Return value: " + method.Invoke (instance, null).ToString ();
    }

    void OnAssemblyLoadFailed (string url)
    {
        m_MessageString = "Failed to load assembly at " + url;
    }

    void OnGUI ()
    {
        GUILayout.BeginArea (new Rect (0.0f, 0.0f, Screen.width, Screen.height));
            GUILayout.FlexibleSpace ();
            GUILayout.BeginHorizontal ();
                GUILayout.FlexibleSpace ();
                GUILayout.Box (m_MessageString);
                GUILayout.FlexibleSpace ();
            GUILayout.EndHorizontal ();
            GUILayout.FlexibleSpace ();
        GUILayout.EndArea ();
    }
}

/MyAssembly.cs

using UnityEngine;

public class MyClass
{
    public static string myString = "This is my string from my class in my assembly";

    public int LogMyString ()
    {
        Debug.Log (myString);
        return 2 + 2;
    }
}

The assembly compile terminal command
mcs -target:library -out:MyAssembly.dll -r:/Applications/Unity/Unity.app/Contents/Frameworks/UnityEngine.dll MyAssembly.cs

Nov 15 09

New license of Path: GPL

by AngryAnt

Since I started at Unity, I haven’t really had the time to do much updating (read: none) of my private projects. In sheer size, Path is the largest of those, so a while ago I realised that maintaining that project is simply not feasible.

By adding the GPL license option to the existing two license options (the show-my-logo license displayed on download from angryant.com and the option for a custom negotiated one), users are able to test and modify the source of Path directly – before deciding on one of the other licenses.

Also, although very messy (yes – very), the source could also serve as a good learning resource for Unity editor scripting.

The project repository is available on github.

Nov 4 09

CopyInspector

by AngryAnt

Yea I know. It’s been a while. I do have two very good excuses though: 2.6 and Unite ‘09. If you enjoyed those then zip it and read on. If not, I’m really out of ammo and sorry for the delay.

At Unite, I attended the talk by John Grden of infrared5 on the special (free) Unity for flash users day. During this session he complained about wanting to be able to copy his runtime transform changes inside the IDE (he was copying by way of paper notes it seemed). I decided to take up the challenge when he continued with “… but someone is probably going to show me how to do that after this” and after his talk I handed him a custom inspector editor script for solving that specific problem.

Later at the conference I had some time to kill and did a rewrite to expand the script to be general for any component type. I didn’t have the time to hand John that version, but I suppose I’ll just email him a link to this post.

    So anyway – lets talk implementation:

  • Add CopyInspector.cs to Assets/Editor.
  • Add CopyTransformInspector.cs to Assets/Editor.
  • For each other component to be made copy/paste-able:
    • Add CopyYourComponentTypeInspector.cs to Assets/Editor.
    • Rename it appropriately.
    • Rename the class to match the file name.
    • Change YourComponentType in “[CustomEditor (typeof (YourComponentType))]” to the type of component you wish to affect.

You’ll notice that CopyTransformInspector has some additional code to it. This is due to the fact that this inspector is rendered non-standard and the code provided just replicates that.

The copy transform inspector

The copy transform inspector

Not really sure why syntax highlighting is not kicking in… Will have a look at that later.

Codes!

CopyInspector.cs

using UnityEngine;
using UnityEditor;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;

public class CopyInspector : Editor
{
    static System.Type m_OriginalType;
    static Dictionary <PropertyInfo, object> m_Values;

    private List <PropertyInfo> GetProperties (Component component)
    {
        List <string> ignoredProperties;
        List <PropertyInfo> properties;

        properties = new List <PropertyInfo> ();
        ignoredProperties = new List <string> ();
        foreach (PropertyInfo propertyInfo in typeof (Component).GetProperties ())
        {
            ignoredProperties.Add (propertyInfo.Name);
        }

        foreach (PropertyInfo propertyInfo in component.GetType ().GetProperties ())
        {
            if (ignoredProperties.Contains (propertyInfo.Name))
            {
                continue;
            }
            properties.Add (propertyInfo);
        }

        return properties;
    }

    public override void OnInspectorGUI ()
    {
        DrawDefaultInspector ();
        OnCopyInspectorGUI ();
    }

    public void OnCopyInspectorGUI ()
    {
        bool enabled;
        List <PropertyInfo> properties;
        Component component;

        component = target as Component;

        if (component == null)
        {
            return;
        }

        GUILayout.Space (10.0f);

        Color backgroundColor = GUI.backgroundColor;

        GUI.backgroundColor = new Color (0.8f, 0.8f, 0.8f);

        GUILayout.BeginVertical ("Toolbar");

            GUI.backgroundColor = backgroundColor;

            GUILayout.BeginHorizontal ();

                GUILayout.Space (10.0f);

                GUILayout.Label ("Copied: " + (m_OriginalType != null ? m_OriginalType.Name : "Nothing"), "MiniLabel");

                GUILayout.FlexibleSpace ();

                if (GUILayout.Button (new GUIContent ("Copy", "Copy component values"), "MiniLabel"))
                {
                    m_OriginalType = target.GetType ();

                    properties = GetProperties (component);

                    m_Values = new Dictionary <PropertyInfo, object> ();
                    foreach (PropertyInfo property in properties)
                    {
                        m_Values [property] = property.GetValue (component, null);
                    }
                }

                enabled = GUI.enabled;
                GUI.enabled = target.GetType () == m_OriginalType;

                GUILayout.Space (10.0f);

                if (GUILayout.Button (new GUIContent ("Paste", "Paste component values"), "MiniLabel"))
                {
                    properties = GetProperties (component);
                    foreach (PropertyInfo property in properties)
                    {
                        if (!property.CanWrite)
                        {
                            continue;
                        }

                        property.SetValue (component, m_Values [property], null);
                    }
                }

                GUILayout.Space (10.0f);

                GUI.enabled = enabled;

            GUILayout.EndHorizontal ();

        GUILayout.EndVertical ();

        GUILayout.Space (-2.0f);
    }
}

CopyTransformInspector.cs

using UnityEngine;
using UnityEditor;
using System.Collections;

[CustomEditor (typeof (Transform))]
public class CopyTransformInspector : CopyInspector
{
    public override void OnInspectorGUI ()
    {
        Transform transform;
        Vector3 localPosition, localScale;
        Quaternion localRotation;

        transform = target as Transform;

        localPosition = EditorGUILayout.Vector3Field ("Position", transform.localPosition);
        localRotation = Quaternion.Euler (EditorGUILayout.Vector3Field ("Rotation", transform.localRotation.eulerAngles));
        localScale = EditorGUILayout.Vector3Field ("Scale", transform.localScale);

        if (GUI.changed)
        {
            transform.localPosition = localPosition;
            transform.localRotation = localRotation;
            transform.localScale = localScale;
        }

        OnCopyInspectorGUI ();
    }
}

CopyYourComponentTypeInspector.cs

using UnityEngine;
using UnityEditor;
using System.Collections;

[CustomEditor (typeof (YourComponentType))]
public class CopyYourComponentTypeInspector : CopyInspector{}
Oct 4 09

Magnetic

by AngryAnt

You know how Little Big Planet, Kingdom Hearts and similar have various pickups move towards your character when you get up close? Yea I also find that’s a nice bit of polish.

One way of getting that up and running in Unity is via the code below. Setup:

  1. Add all objects you want to get attracted to the player to a special layer.
  2. Attach the script below to your player.
  3. Set the layer mask on the component to include the layer from step one.
  4. Tweak, play, repeat.

 

Magnetic.cs:

using UnityEngine;
using System.Collections;

public class Magnetic : MonoBehaviour
{
    public LayerMask m_MagneticLayers;
    public Vector3 m_Position;
    public float m_Radius;
    public float m_Force;

    void FixedUpdate ()
    {
        Collider[] colliders;
        Rigidbody rigidbody;

        colliders = Physics.OverlapSphere (transform.position + m_Position, m_Radius, m_MagneticLayers);
        foreach (Collider collider in colliders)
        {
            rigidbody = (Rigidbody) collider.gameObject.GetComponent (typeof (Rigidbody));
            if (rigidbody == null)
            {
                continue;
            }
            rigidbody.AddExplosionForce (m_Force * -1, transform.position + m_Position, m_Radius);
        }
    }

    void OnDrawGizmosSelected ()
    {
        Gizmos.color = Color.red;
        Gizmos.DrawWireSphere (transform.position + m_Position, m_Radius);
    }
}
Sep 18 09

GUI drag-drop

by AngryAnt

A lot of people have been asking for tips on how to implement drag-drop functionality in Unity GUI scripting, so I decided to put together a reusable script for the purpose.

Basically the solution requires that your data class derives from GUIDraggableObject and at some point in its OnGUI method call Drag( Rect ) – just like GUI.Window handles dragging.

Right. Codez. First off the GUIDraggableObject.cs file:
 

using UnityEngine;
using System.Collections;

public class GUIDraggableObject
{
    protected Vector2 m_Position;
    private Vector2 m_DragStart;
    private bool m_Dragging;

    public GUIDraggableObject (Vector2 position)
    {
        m_Position = position;
    }

    public bool Dragging
    {
        get
        {
            return m_Dragging;
        }
    }

    public Vector2 Position
    {
        get
        {
            return m_Position;
        }

        set
        {
            m_Position = value;
        }
    }

    public void Drag (Rect draggingRect)
    {
        if (Event.current.type == EventType.MouseUp)
        {
            m_Dragging = false;
        }
        else if (Event.current.type == EventType.MouseDown && draggingRect.Contains (Event.current.mousePosition))
        {
            m_Dragging = true;
            m_DragStart = Event.current.mousePosition - m_Position;
            Event.current.Use();
        }

        if (m_Dragging)
        {
            m_Position = Event.current.mousePosition - m_DragStart;
        }
    }
}

 
An example data class inheriting from GUIDraggableObject – DataObject.cs:
 

using UnityEngine;
using System.Collections;

public class DataObject : GUIDraggableObject
// This class just has the capability of being dragged in GUI - it could be any type of generic data class
{
    private string m_Name;
    private int m_Value;

    public DataObject (string name, int value, Vector2 position) : base (position)
    {
        m_Name = name;
        m_Value = value;
    }

    public void OnGUI ()
    {
        Rect drawRect = new Rect (m_Position.x, m_Position.y, 100.0f, 100.0f), dragRect;

        GUILayout.BeginArea (drawRect, GUI.skin.GetStyle ("Box"));
            GUILayout.Label (m_Name, GUI.skin.GetStyle ("Box"), GUILayout.ExpandWidth (true));

            dragRect = GUILayoutUtility.GetLastRect ();
            dragRect = new Rect (dragRect.x + m_Position.x, dragRect.y + m_Position.y, dragRect.width, dragRect.height);

            if (Dragging)
            {
                GUILayout.Label ("Wooo...");
            }
            else if (GUILayout.Button ("Yes!"))
            {
                Debug.Log ("Yes. It is " + m_Value + "!");
            }
        GUILayout.EndArea ();

        Drag (dragRect);
    }
}

 
And finally, this script demonstrates how you could have your data manager class use Unity GUI for data visualisation with drag-drop enabled – MyMonoBehaviour.cs:
 

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class MyMonoBehaviour : MonoBehaviour
{
    private List< DataObject > m_Data = new List< DataObject > ();
    private Rect dropTargetRect = new Rect (10.0f, 10.0f, 30.0f, 30.0f);

    void Awake ()
    {
        m_Data.Add (new DataObject ("One", 1, new Vector2 (20.0f * Random.Range (1.0f, 10.0f), 20.0f * Random.Range (1.0f, 10.0f))));
        m_Data.Add (new DataObject ("Two", 2, new Vector2 (20.0f * Random.Range (1.0f, 10.0f), 20.0f * Random.Range (1.0f, 10.0f))));
        m_Data.Add (new DataObject ("Three", 3, new Vector2 (20.0f * Random.Range (1.0f, 10.0f), 20.0f * Random.Range (1.0f, 10.0f))));
        m_Data.Add (new DataObject ("Four", 4, new Vector2 (20.0f * Random.Range (1.0f, 10.0f), 20.0f * Random.Range (1.0f, 10.0f))));
        m_Data.Add (new DataObject ("Five", 5, new Vector2 (20.0f * Random.Range (1.0f, 10.0f), 20.0f * Random.Range (1.0f, 10.0f))));
    }

    public void OnGUI ()
    {
        DataObject toFront, dropDead;
        Color color;

        GUI.Box(dropTargetRect, "Die");

        toFront = dropDead = null;
        foreach (DataObject data in m_Data)
        {
            color = GUI.color;

            if (data.Dragging)
            {
                GUI.color = dropTargetRect.Contains (Event.current.mousePosition) ? Color.red : color;
            }

            data.OnGUI ();

            GUI.color = color;

            if (data.Dragging)
            {
                if (m_Data.IndexOf (data) != m_Data.Count - 1)
                {
                    toFront = data;
                }
            }
        }

        if (toFront != null)
        // Move an object to front if needed
        {
            m_Data.Remove (toFront);
            m_Data.Add (toFront);
        }
    }
}

 
Ah yea and an example of how you could do the same in an editor window – MyEditorWindow.cs:
 

using UnityEngine;
using UnityEditor;
using System.Collections;
using System.Collections.Generic;

public class MyEditorWindow : EditorWindow
{
    private List< DataObject > m_Data = new List< DataObject > ();
    private bool doRepaint = false;
    private Rect dropTargetRect = new Rect (10.0f, 10.0f, 30.0f, 30.0f);

    public MyEditorWindow ()
    {
        m_Data.Add (new DataObject ("One", 1, new Vector2 (20.0f * Random.Range (1.0f, 10.0f), 20.0f * Random.Range (1.0f, 10.0f))));
        m_Data.Add (new DataObject ("Two", 2, new Vector2 (20.0f * Random.Range (1.0f, 10.0f), 20.0f * Random.Range (1.0f, 10.0f))));
        m_Data.Add (new DataObject ("Three", 3, new Vector2 (20.0f * Random.Range (1.0f, 10.0f), 20.0f * Random.Range (1.0f, 10.0f))));
        m_Data.Add (new DataObject ("Four", 4, new Vector2 (20.0f * Random.Range (1.0f, 10.0f), 20.0f * Random.Range (1.0f, 10.0f))));
        m_Data.Add (new DataObject ("Five", 5, new Vector2 (20.0f * Random.Range (1.0f, 10.0f), 20.0f * Random.Range (1.0f, 10.0f))));
    }

    [MenuItem ("Window/MyEditorWindow")]
    public static void Launch ()
    {
        GetWindow (typeof (MyEditorWindow)).Show ();
    }

    public void Update ()
    {
        if (doRepaint)
        {
            Repaint ();
        }
    }

    public void OnGUI ()
    {
        DataObject toFront, dropDead;
        bool previousState, flipRepaint;
        Color color;

        GUI.Box(dropTargetRect, "Die");

        toFront = dropDead = null;
        doRepaint = false;
        flipRepaint = false;
        foreach (DataObject data in m_Data)
        {
            previousState = data.Dragging;

            color = GUI.color;

            if (previousState)
            {
                GUI.color = dropTargetRect.Contains (Event.current.mousePosition) ? Color.red : color;
            }

            data.OnGUI ();

            GUI.color = color;

            if (data.Dragging)
            {
                doRepaint = true;

                if (m_Data.IndexOf (data) != m_Data.Count - 1)
                {
                    toFront = data;
                }
            }
            else if (previousState)
            {
                flipRepaint = true;

                if (dropTargetRect.Contains (Event.current.mousePosition))
                {
                    dropDead = data;
                }
            }
        }

        if (toFront != null)
        // Move an object to front if needed
        {
            m_Data.Remove (toFront);
            m_Data.Add (toFront);
        }

        if (dropDead != null)
        // Destroy an object if needed
        {
            m_Data.Remove (dropDead);
        }

        if (flipRepaint)
        // If some object just stopped being dragged, we should repaing for the state change
        {
            Repaint ();
        }
    }
}
Sep 17 09

Logging an entire GameObject

by AngryAnt

More stuff from the shadowy corners of my hard-drive. Don’t remember the context, but someone needed to log every single piece of information available on a particular GameObject. I suppose this could be useful for end-user “This GameObject Just Went Completely FUBAR” ™ scenarios.

Anyway – it has reflection in it which by definition makes it cool.

Le codez:

using UnityEngine;
using System.Reflection;

public class Utilities
{
    /* ... */

    static void LogGameObject( GameObject gameObject, bool children )
    {
        Component[] components = gameObject.GetComponents( typeof( Component ) );
        FieldInfo[] fields;
        PropertyInfo[] properties;

        Debug.Log( gameObject.name + ":" );

        foreach( Component component in components )
        {
            Debug.Log( " - " + component.GetType().Name + ":" );
            fields = component.GetType().GetFields();
            foreach( FieldInfo field in fields )
            {
                Debug.Log( " ." + field.Name + " = " + field.GetValue( component ) );
            }

            properties = component.GetType().GetProperties();
            foreach( PropertyInfo property in properties )
            {
                Debug.Log( " ." + property.Name + " = " + property.GetGetMethod().Invoke( component, null ) );
            }
        }

        if( children )
        {
            foreach( Transform transform in gameObject.transform )
            {
                Debug.Log( "->" );
                LogGameObject( transform.gameObject, children );
            }
        }
    }

    static void LogGameObject( GameObject gameObject )
    {
        LogGameObject( gameObject, false );
    }

    /* ... */
}
Sep 7 09

I bet you can’t type an A!

by AngryAnt

Keys got special meaning and shouldn’t be fed into parts of the GUI? This handy little snippet takes care of that job:

private string text = "I bet you can't type an A!"; 

static void DisableKeys( KeyCode[] keys )
{
    if( !Event.current.isKey )
    {
        return;
    }

    foreach( KeyCode key in keys )
    {
        if( Event.current.keyCode == key )
        {
            Event.current.Use();
        }
    }
}

static void DisableKey( KeyCode key )
{
    DisableKeys( new KeyCode[]{ key } );
}

void OnGUI()
{
    DisableKey( KeyCode.A );
    text = GUILayout.TextArea( text );
}
Aug 31 09

Where did that component go?

by AngryAnt

In larger Unity projects, components sometimes “get lost” – you refactor something and there’s an audio source still attached somewhere in your hierarchy and you simply cannot remember where.

You’re not the first one. A while ago I created a simple editor script to combat this issue as a response to a similar frustrated post on the Unity forums. Earlier this week I stumbled on the script again, picked it up and dusted it off a bit and decided to give it another chance to shine in some spotlight.

Use:

  • Save the script as ComponentLister.cs.
  • Place it in /Assets/Editor in your project.
  • Launch the window from the Component menu – should be the last item titled “Component lister”.
  • Click “Refresh” to list all components in your scene and the GameObjects to which they are attached.
  • To investigate, click a GameObject name in the list and it will be set as the active selection in the hierarchy.

Codes:

using UnityEngine;
using UnityEditor;
using System.Collections; 

public class ComponentLister : EditorWindow
{
    private Hashtable sets = new Hashtable();
    private Vector2 scrollPosition;

    [ MenuItem( "Component/Component lister" ) ]
    public static void Launch()
    {
        EditorWindow window = GetWindow( typeof( ComponentLister ) );
        window.Show();
    } 

    public void UpdateList()
    {
        Object[] objects; 

        sets.Clear(); 

        objects = FindObjectsOfType( typeof( Component ) );
        foreach( Component component in objects )
        {
            if( !sets.ContainsKey( component.GetType() ) )
            {
                sets[ component.GetType() ] = new ArrayList();
            } 

            ( ( ArrayList )sets[ component.GetType() ] ).Add( component.gameObject );
        }
    } 

    public void OnGUI()
    {
        GUILayout.BeginHorizontal( GUI.skin.GetStyle( "Box" ) );
            GUILayout.Label( "Components in scene:" );
            GUILayout.FlexibleSpace();

            if( GUILayout.Button( "Refresh" ) )
            {
                UpdateList();
            }
        GUILayout.EndHorizontal();

        scrollPosition = GUILayout.BeginScrollView(scrollPosition);

            foreach( System.Type type in sets.Keys )
            {
                GUILayout.Label( type.Name + ":" );
                foreach( GameObject gameObject in ( ArrayList )sets[ type ] )
                {
                    if( GUILayout.Button( gameObject.name ) )
                    {
                        Selection.activeObject = gameObject;
                    }
                }
            } 

        GUILayout.EndScrollView();
    }
}
Aug 19 09

UnitySteer

by AngryAnt

Ok so I’ve been quite busy lately and so I haven’t been updating the site as much as I should. In the beginning of July, UnitySteer was released. Ricardo Mendez of Arges Systems did the majority of the work and I did some early coding and design on the project.

UnitySteer is a collection of steering behaviours – enabling complex movement behaviour by combining the default behaviour set or creating new custom ones. The project is based on OpenSteerDotNet which is a .net port of OpenSteer. During the implementation of the Unity interface, however, a lot of the existing code base was either rewritten or discarded completely, so code based on the previous OpenSteer versions is not directly compatible.

For more information, check out Ricardo’s blog post and the library and examples repositories.

UnitySteer is open source, released under the MIT license.