Buffalo Game Space



 
                       
Buffalo Game Space

Mark's Favorite Unity C# Extensions

  • Mark Zorn

For those of you using C# in your Unity projects (and at this point you really should be) extension methods allow you to add methods to existing types without having to create your own subtypes or change the original type in any way. They are very useful part of C#, something that is fairly unique to the language, and something you should be using to make your life easier!

There are many in depth explanations of extension methods just a quick Google search away, but here are a few things to remember:

  • All extension methods must be placed inside of a static class (I like to call mine Extensions)
  • The extension method must also be static
  • The first parameter to the method is preceded by the this keyword. The type of this parameter will be the type to which we are adding our extension method.

Contrived Example

What? You want a super contrived example you say? Well here you go.

Let's say you have to cube a bunch of integers many times. You would probably be doing it something like this:

int x = 3;  
int cubed = x*x*x;  

Now if you are doing this a lot, you may get tired of writing x*x*x - so let's simplify that and extend the int type by adding a Cubed method:

public static int Cubed(this int x){  
   return x * x * x;
}

Now - we can simply say:

int x = 3;  
int cubed = x.Cubed();  

Thus ends your super contrived example that you probably would never do in the real world.

General Purpose Unity Extension Methods

And here is what you actually came here for. The following are some of the extension methods that are more general purpose which I tend to include in every project I make. Note that I usually work in 2D, so anything that is specific to 3D may not be included - so make your own!

Transform Properties

Ever find yourself annoyed that you can't directly change a property on a transform, well prepare to be a little less annoyed!

Add a vector to the transforms position:

public static void AddVector(this Transform transform, Vector3 vector){  
    Vector3 position = transform.position;
    position += vector;
    transform.position = position;
}

Set the x, y, or z value of the position:

public static void SetXPosition(this Transform transform, float x){  
    Vector3 position = transform.position;
    position.x = x;
    transform.position = position;
}
public static void SetYPosition(this Transform transform, float y){  
    Vector3 position = transform.position;
    position.y = y;
    transform.position = position;
}

public static void SetZPosition(this Transform transform, float z){  
    Vector3 position = transform.position;
    position.z = z;
    transform.position = position;
}

Add a value to the x, y, or z value of the position:

public static void AddXPosition(this Transform transform, float x){  
    Vector3 position = transform.position;
    position.x += x;
    transform.position = position;
}

public static void AddYPosition(this Transform transform, float y){  
    Vector3 position = transform.position;
    position.y += y;
    transform.position = position;
}

public static void AddZPosition(this Transform transform, float z){  
    Vector3 position = transform.position;
    position.z += z;
    transform.position = position;
}

Set, adjust, or get the rotation of a 2D element:

public static void SetRotation(this Transform transform, float rotation){  
    Vector3 r = transform.localEulerAngles;
    r.z = rotation;
    transform.localEulerAngles = r;
}

public static void AddRotation(this Transform transform, float rotation){  
    Vector3 r = transform.localEulerAngles;
    r.z += rotation;
    transform.localEulerAngles = r;
}

public static float GetRotation(this Transform transform){  
    return transform.localEulerAngles.z;
}

As you can see, these are all very simple, but things I found myself doing a lot, so now I can do them in a single line of code.

Calculate Angles Between Vectors

Often in my games I will have enemies that track the player or other objects, updating it's rotation to face the player. This code assumes that 0 degrees is facing left. You may have to adjust if this is not the case for your sprites.

public static float GetAngleToVector(this Vector3 point, Vector2 vector){  
    float deltaY = vector.y - point.y;
    float deltaX = vector.x - point.x;

    //0 will be the vector directly to the left of the point
    return Mathf.Atan2(deltaY, deltaX) * 180 / Mathf.PI + 180;
}

public static float GetAngleToVector(this Transform point, Vector2 vector){  
    return point.transform.position.GetAngleToVector (vector);
}

public static float GetAngleToVector(this Transform point, Transform t){  
    return point.transform.position.GetAngleToVector (t.position);
}

Sprite Renderer Alpha Offsets

It is easy to get the bounds of a sprite via it's renderer, but more difficult to be able to tell where the first non-transparent pixel is on that sprite for precise placement of your objects - which is why I often use these methods.

public static float GetSpriteAlphaLeftXOffset(this SpriteRenderer spriteRenderer){  
    Vector4 padding = UnityEngine.Sprites.DataUtility.GetPadding (spriteRenderer.sprite);
    float leftPadding = (padding.x / spriteRenderer.sprite.pixelsPerUnit) * spriteRenderer.transform.localScale.x;
    float offset = spriteRenderer.bounds.size.x / 2 - leftPadding;

    return offset;
}

public static float GetSpriteAlphaRightXOffset(this SpriteRenderer spriteRenderer){  
    Vector4 padding = UnityEngine.Sprites.DataUtility.GetPadding (spriteRenderer.sprite);
    float rightPadding = (padding.z / spriteRenderer.sprite.pixelsPerUnit) * spriteRenderer.transform.localScale.x;
    float offset = spriteRenderer.bounds.size.x / 2 - rightPadding;

    return offset;
}

public static float GetSpriteAlphaBottomYOffset(this SpriteRenderer spriteRenderer){  
    Vector4 padding = UnityEngine.Sprites.DataUtility.GetPadding (spriteRenderer.sprite);
    float bottomPadding = (padding.y / spriteRenderer.sprite.pixelsPerUnit) * spriteRenderer.transform.localScale.x;
    float offset = spriteRenderer.bounds.size.y / 2 - bottomPadding;

    return offset;
}

public static float GetSpriteAlphaTopYOffset(this SpriteRenderer spriteRenderer){  
    Vector4 padding = UnityEngine.Sprites.DataUtility.GetPadding (spriteRenderer.sprite);
    float topPadding = (padding.w / spriteRenderer.sprite.pixelsPerUnit) * spriteRenderer.transform.localScale.x;
    float offset = spriteRenderer.bounds.size.y / 2 - topPadding;

    return offset;
}

Animator

Determine whether or not an animator has a given parameter. As an example of where you may want to use this, assume you have a single Enemy class that is added to many different enemies with different animations, some of which should transition to another animation on death, and others that do not. You can check the animator for a Dead parameter to determine what you should do upon death and instead of making different subclasses for the two behaviors.

public static bool HasParameter(this Animator animator, string paramName ){  
    for(int x = 0; x<animator.parameters.Length; x++){
        AnimatorControllerParameter param = animator.parameters [x];
        if (param.name == paramName)
            return true;
    }
    return false;
}

Lists/Arrays

Methods to randomly rearrange the contents of a collection.

public static List<T> Shuffle<T>(this List<T> list){  
    List<T> newList = new List<T> ();

    while (list.Count > 0) {
        T item = list [UnityEngine.Random.Range (0, list.Count)];
        list.Remove (item);

        newList.Add (item);
    }

    return newList;
}

public static void Shuffle<T>(this T[] arr){  
    System.Random _random = new System.Random ();

    T tempObject;

    int n = arr.Length;
    for (int i = 0; i < n; i++)
    {

        int r = i + (int)(_random.NextDouble() * (n - i));
        tempObject = arr[r];
        arr[r] = arr[i];
        arr[i] = tempObject;
    }
}

Components

Make a copy of a component, using reflection to copy all of the values of it's properties:

public static Component CopyComponent(this GameObject destination, Component original){  
    System.Type type = original.GetType();
    Component copy = destination.AddComponent(type);
    System.Reflection.FieldInfo[] fields = type.GetFields(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public); 

    for(int x = 0; x<fields.Length; x++){
        System.Reflection.FieldInfo field = fields [x];

        if (field.IsDefined (typeof(SerializeField), false)) {
            field.SetValue (copy, field.GetValue (original));
        }
    }

    return copy;
}

Misc

A method for finding all objects of a certain type that are within a specific distance from a Transform. Note that this does us GameObject.FindObjectsOfType so refrain from using this every frame. I find this useful for things like determining who is damaged within an explosion radius.

public static List<T> FindObjectsWithinProximity<T>(this Transform transform, float proximity) where T : MonoBehaviour{  
    List<T> objects = new List<T> ();

    T[] foundObjects = GameObject.FindObjectsOfType<T> ();
    for(int x = 0; x<foundObjects.Length; x++){
        T obj = foundObjects [x];
        if ((obj.transform.position - transform.position).magnitude <= proximity)
            objects.Add (obj);
    }

    return objects;
}

Determine if a layer is contained with a LayerMask. I used to always forget how to do this, so I made an extension and now I no longer have to remember:

public static bool IsLayerInMask(this LayerMask layerMask, int layer){  
    return layerMask.value == (layerMask.value | 1 << layer);
}