/extensions

A personal collection of Unity Engine tools, extensions, and helpers.

Primary LanguageC#MIT LicenseMIT

Extensions

A personal collection of Unity Engine tools, extensions, and helpers.

Installation

OpenUPM

This package is available on the OpenUPM registry. Add the package via the openupm-cli:

openupm add com.mygamedevtools.extensions

Installing from Git (requires Git installed and added to the PATH)

  1. Open Edit/Project Settings/Package Manager.
  2. Click +.
  3. Select Add package from git URL....
  4. Paste com.mygamedevtools.extensions into the name.
  5. Click Add.

Usage

This package is focused on 3 main areas: Extensions, Tooling, and Attributes. The purpose of these tools, is to improve the overall Unity Editor experience with simple, reliable, and maintainable solutions.

🔷 Attributes

We have a total of 3 attributes available:

🔶 ReadOnly

The [ReadOnly] attribute prevents changing the field's value in the Unity Inspector

public class MyClass : MonoBehaviour
{
    [SerializeField, ReadOnly]
    float myValue = 5.5f;
}

🔶 LabeledArray

The [LabeledArray] attribute allows you to specify names for array elements, instead of the default Element #n. You can either use a string array or an Enum to provide the names. If the array element count passes the provided names count, the following element names will fall to the default naming

public class MyClass : MonoBehaviour
{
    [SerializeField, LabeledArray(new string[]{ "First", "Second", "Third" })]
    float[] myValueArray;
}

🔶 SceneField

The [SceneField] attribute allows you to drag scene assets to fields in the inspector. You can only use this attribute on int fields since it will store the scene's build index. It does not have any relation with the scene itself, so if you change the build settings it will not update its value and will reference a different scene. You can add the scene to the build settings via the inspector after dragging the scene to the field if it hasn't been yet

public class MyClass : MonoBehaviour
{
    [SerializeField, SceneField]
    int mySceneIndex;
}

🔷 Tooling

We have two tools available:

🔶 Group Game Objects

Simply press Ctrl + G to group the selected game objects. It creates a parent game object in the center of the selected objects.

🔶 Scripting Define Symbols Helper

You can use the ScriptingDefineSymbolsHelper to easily add or remove scripting define symbols to your project. For example:

using MyGameDevTools.Extensions;
using UnityEditor;

public static class CheatsEnabler
{
    const string _cheatsDefine = "ENABLE_CHEATS";

    [MenuItem("Tools/Add Cheats")]
    public static void AddCheats()
    {
        ScriptingDefineSymbolsHelper.AddScriptingDefineSymbol(_cheatsDefine);
    }

    [MenuItem("Tools/Add Cheats", validate = true)]
    public static bool AddCheatsValidate()
    {
        return !ScriptingDefineSymbolsHelper.HasScriptingDefineSymbol(_cheatsDefine);
    }

    [MenuItem("Tools/Remove Cheats")]
    public static void RemoveCheats()
    {
        ScriptingDefineSymbolsHelper.RemoveScriptingDefineSymbol(_cheatsDefine);
    }

    [MenuItem("Tools/Remove Cheats", validate = true)]
    public static bool RemoveCheatsValidate()
    {
        return ScriptingDefineSymbolsHelper.HasScriptingDefineSymbol(_cheatsDefine);
    }
}

🔷 Extensions

We have a total of 3 extensions available:

🔶 Debug Extensions

Allows debugging collections and jagged arrays.

public class MyClass : MonoBehaviour
{
    // Can also be a List or any IEnumerable type
    [SerializeField]
    int[] myValueArray;
    
    int[][] myJaggedArray = new int[5][];
    
    void Start()
    {
        for (int i = 0; i < myJaggedArray.Length; i++)
            myJaggedArray[i] = new int[5];
            
        DebugExtensions.LogJaggedArray(myJaggedArray);        
        DebugExtensions.LogCollection(myValueArray);
    }
}

🔶 Game Object Extensions

A collection of some helpful game object operations

💠 Set Children Layer Recursively

Sets all game objects in the given transform's hierarchy to the provided layer

public class MyClass : MonoBehaviour
{
    void Start()
    {
        transform.SetChildrenLayerRecursively(LayerMask.NameToLayer("MyLayer"));
    }
}
💠 Destroy Components

Destroys the given component type instances in a UnityEngine.GameObject or its children.

public class MyClass : MonoBehaviour
{
    [SerializeField]
    GameObject[] myObjectsToDestroy;

    void Start()
    {        
        gameObject.DestroyAllComponentsOfType<BoxCollider>();
        gameObject.DestroyAllChildrenComponentsOfType<SphereCollider>();
    }
}
💠 Get Root Game Objects

Gets the root game objects of a scene, or optionally all scenes.

public class MyClass : MonoBehaviour
{
    void Start()
    {
        List<GameObject> allRootObjects = GameObjectExtensions.GetRootGameObjects(true);
    }
}
💠 Has Layer

Checks if a LayerMask contains the given layer index. Useful for physics validations.

public class MyClass : MonoBehaviour
{
    [SerializeField]
    LayerMask _mask;

    void Start()
    {
        Debug.Log(_mask.HasLayer(gameObject.layer));
    }
}
💠 Delay Call

Delays an Action after a given amount of time. You can delay in seconds, frames, or physics (fixed) frames.

public class MyClass : MonoBehaviour
{
    void Start()
    {
        Debug.Log("Hello there");
        // In seconds
        DelayCall(6, MyAction);
        // In frames
        DelayCallInFrames(6, MyAction);
        // In physics frammes
        DelayCallInPhysicsFrames(6, MyAction);
    }
    
    void MyAction()
    {
        Debug.Log("General Kenobi")
    }
}

🔶 Object Extensions

💠 Destroy Objects

Destroy a given array of UnityEngine.Object, which could be GameObjects, Components, MonoBehaviours, and any type that inherits from UnityEngine.Object. Keep in mind that the object is destroyed between the end of the current frame and the beginning of the next one.

public class MyClass : MonoBehaviour
{
    [SerializeField]
    Object[] _objectsToDestroy;

    void Start()
    {
        ObjectExtensions.DestroyObjects(_objectsToDestroy);
    }
}
💠 Find Implementation(s) of Type

This is meant to find interface implementations. It works like a FindObjectOfType, but for interface. You can also use the TryFindImplementationOfType to return a bool. Keep in mind that the interface implementation will also need to inherit from MonoBehaviour to be found since it will be searched within GameObject's components.

public interface IActionable {}

public class MyAction : MonoBehaviour, IActionable {}

public class MyClass : MonoBehaviour
{
    void Start()
    {
        // Common usage
        IActionable implementation = this.FindImplementationOfType<IActionable>();
        // Explicit usage
        IActionable implementation = ObjectExtensions.FindImplementationOfType<IActionable>();
        // Try usage
        bool implementationExists = ObjectExtensions.TryFindImplementationOfType<IActionable>(out IActionable implementation);

        // For multiple implementations
        List<IActionable> implementations = ObjectExtensions.FindImplementationsOfType<IActionable>();
    }
}

Don't hesitate to create issues for suggestions and bugs. Have fun!