Skip to main content

Extension Methods

QWR Utilities provides a comprehensive collection of extension methods for Unity objects and common data types. These extensions simplify common operations and reduce boilerplate code in your projects.

Extension Methods Overview​

Extension methods allow you to add functionality to existing types without modifying them. QWR Utilities includes extensions for:

  • Unity-specific types (Transform, GameObject, Component, etc.)
  • Collection types (List, Array, Dictionary, etc.)
  • String operations
  • Numeric types
  • Vector and Quaternion operations

Transform Extensions​

Position and Rotation​

using QWR.Utilities.Extensions;
using UnityEngine;

// Set individual position components
transform.SetPositionX(5f);
transform.SetPositionY(2f);
transform.SetPositionZ(3f);

// Get position components
float x = transform.GetPositionX();
float y = transform.GetPositionY();
float z = transform.GetPositionZ();

// Set local position components
transform.SetLocalPositionX(1f);
transform.SetLocalPositionY(2f);
transform.SetLocalPositionZ(3f);

// Reset transform
transform.ResetLocal(); // Reset position, rotation, and scale to identity/one
transform.ResetPosition(); // Reset position only
transform.ResetRotation(); // Reset rotation only
transform.ResetScale(); // Reset scale only

// Transform without scale
Vector3 worldPos = transform.TransformPointUnscaled(localPos);
Vector3 localPos = transform.InverseTransformPointUnscaled(worldPos);

// Look at with up direction
transform.LookAtWithUp(target.position, Vector3.up);

// Set rotation to face direction
transform.SetForwardDirection(direction);

Hierarchy Operations​

// Destroy all children
transform.DestroyChildren();

// Get all children
Transform[] children = transform.GetChildren();

// Get child by name (with optional recursive search)
Transform child = transform.FindChildByName("ChildName", recursive: true);

// Get or create child
Transform child = transform.GetOrCreateChild("NewChild");

// Set parent with world position/rotation preserved
transform.SetParentPreserveWorld(newParent);

// Get full hierarchy path
string path = transform.GetHierarchyPath();

// Get root parent
Transform root = transform.GetRootParent();

GameObject Extensions​

Component Operations​

using QWR.Utilities.Extensions;
using UnityEngine;

// Get or add component
Rigidbody rb = gameObject.GetOrAddComponent<Rigidbody>();

// Try get component
if (gameObject.TryGetComponent<Collider>(out var collider))
{
// Use collider
}

// Check if has component
bool hasAnimator = gameObject.HasComponent<Animator>();

// Get components in children including inactive
Component[] components = gameObject.GetComponentsInChildrenIncludingInactive<Collider>();

// Get component in parent
Canvas canvas = gameObject.GetComponentInParent<Canvas>(includeInactive: true);

Layer and Tag Operations​

// Set layer
gameObject.SetLayer(LayerMask.NameToLayer("Player"));

// Set layer recursively (for all children)
gameObject.SetLayerRecursively(LayerMask.NameToLayer("Player"));

// Check if in layer mask
bool isInMask = gameObject.IsInLayerMask(playerLayerMask);

// Check tag with null safety
bool isPlayer = gameObject.CompareTag("Player");

// Get all game objects with tag
GameObject[] enemies = GameObjectExtensions.FindGameObjectsWithTag("Enemy");

Hierarchy Operations​

// Get all children
GameObject[] children = gameObject.GetChildren();

// Get active children
GameObject[] activeChildren = gameObject.GetActiveChildren();

// Destroy all children
gameObject.DestroyChildren();

// Instantiate as child
GameObject newObj = gameObject.InstantiateAsChild(prefab);

// Get or create child
GameObject child = gameObject.GetOrCreateChild("ChildName");

// Get full path
string path = gameObject.GetFullPath();

Component Extensions​

using QWR.Utilities.Extensions;
using UnityEngine;

// Enable/disable component
GetComponent<Collider>().Enable();
GetComponent<Renderer>().Disable();

// Check if component is enabled
bool isEnabled = GetComponent<Light>().IsEnabled();

// Get component in parent or self
Rigidbody rb = transform.GetComponentInParentOrSelf<Rigidbody>();

// Get all components of type in scene
Renderer[] allRenderers = ComponentExtensions.FindObjectsOfTypeIncludingInactive<Renderer>();

// Get components with interface
IInteractable[] interactables = ComponentExtensions.FindObjectsOfTypeWithInterface<IInteractable>();

Collection Extensions​

List Extensions​

using QWR.Utilities.Extensions;
using System.Collections.Generic;

// Initialize with values
List<int> list = new List<int>().Initialize(5, i => i * 10); // [0, 10, 20, 30, 40]

// Shuffle list
list.Shuffle();

// Get random item
int randomItem = list.GetRandom();

// Remove and get item at index
int item = list.RemoveAndGet(2);

// Remove all null entries
list.RemoveNulls();

// Add range if condition
list.AddRangeIf(otherList, x => x > 10);

// Foreach with index
list.ForEach((item, index) => Debug.Log($"Item {index}: {item}"));

// Add unique items
list.AddUnique(5); // Only adds if not already in list

// Check if list contains all items from another collection
bool containsAll = list.ContainsAll(otherList);

// Get distinct elements by key
List<Person> uniquePeople = people.DistinctBy(p => p.Id).ToList();

Array Extensions​

using QWR.Utilities.Extensions;

// Shuffle array
int[] array = { 1, 2, 3, 4, 5 };
array.Shuffle();

// Get random item
int randomItem = array.GetRandom();

// Resize array (similar to List capacity)
array = array.Resize(10);

// Convert to list
List<int> list = array.ToList();

// Find index with predicate
int index = array.FindIndex(x => x > 3);

// Check if array contains all items from another collection
bool containsAll = array.ContainsAll(otherArray);

Dictionary Extensions​

using QWR.Utilities.Extensions;
using System.Collections.Generic;

Dictionary<string, int> dict = new Dictionary<string, int>();

// Get value or default
int value = dict.GetValueOrDefault("key", defaultValue: 0);

// Try add if key doesn't exist
dict.TryAdd("key", 42);

// Add or update
dict.AddOrUpdate("key", 10, existingValue => existingValue + 1);

// Get random key-value pair
KeyValuePair<string, int> random = dict.GetRandom();

// Get random key
string randomKey = dict.GetRandomKey();

// Get random value
int randomValue = dict.GetRandomValue();

// Merge dictionaries
dict.Merge(otherDict);

// Merge dictionaries with value resolver for conflicts
dict.Merge(otherDict, (key, value1, value2) => value1 + value2);

String Extensions​

Manipulation​

using QWR.Utilities.Extensions;

// Check if null or empty
bool isEmpty = "".IsNullOrEmpty();
bool isWhitespace = " ".IsNullOrWhiteSpace();

// Truncate with ellipsis
string truncated = "This is a very long text".Truncate(10); // "This is a..."

// Ensure starts/ends with
string path = "folder/file".EnsureStartsWith("/"); // "/folder/file"
string url = "example.com".EnsureEndsWith("/"); // "example.com/"

// Remove prefix/suffix
string withoutPrefix = "prefixText".RemovePrefix("prefix"); // "Text"
string withoutSuffix = "textSuffix".RemoveSuffix("Suffix"); // "text"

// To title case
string title = "hello world".ToTitleCase(); // "Hello World"

// To camel case
string camel = "HelloWorld".ToCamelCase(); // "helloWorld"

// To pascal case
string pascal = "hello_world".ToPascalCase(); // "HelloWorld"

// To slug
string slug = "Hello World!".ToSlug(); // "hello-world"

// Reverse string
string reversed = "Hello".Reverse(); // "olleH"

Validation and Matching​

// Check if valid email
bool isEmail = "[email protected]".IsValidEmail();

// Check if valid URL
bool isUrl = "https://example.com".IsValidUrl();

// Check if numeric
bool isNumeric = "123".IsNumeric();

// Check if alphabetic
bool isAlpha = "abc".IsAlphabetic();

// Check if alphanumeric
bool isAlphaNum = "abc123".IsAlphanumeric();

// Match regex pattern
bool isMatch = "Hello123".IsMatch(@"^[A-Za-z]+\d+$");

// Count occurrences
int count = "hello world".CountOccurrences("l"); // 3

// Contains any
bool contains = "hello world".ContainsAny("abc"); // true (contains 'a')

// Contains all
bool containsAll = "hello world".ContainsAll("helo"); // true

Vector Extensions​

Vector3 Operations​

using QWR.Utilities.Extensions;
using UnityEngine;

Vector3 v = new Vector3(1, 2, 3);

// Set individual components
Vector3 v2 = v.WithX(5);
Vector3 v3 = v.WithY(10);
Vector3 v4 = v.WithZ(15);

// Flip components
Vector3 flipped = v.Flip(); // (-1, -2, -3)

// Absolute values
Vector3 abs = v.Abs(); // (1, 2, 3)

// Clamp magnitude
Vector3 clamped = v.ClampMagnitude(1, 5);

// Round to nearest
Vector3 rounded = v.Round();
Vector3 floored = v.Floor();
Vector3 ceiled = v.Ceil();

// Direction to another vector
Vector3 dir = v.DirectionTo(target);

// Angle to another vector
float angle = v.AngleTo(target);

// Project onto plane
Vector3 projected = v.ProjectOnPlane(planeNormal);

// Reflect
Vector3 reflected = v.Reflect(normal);

Vector2 Operations​

using QWR.Utilities.Extensions;
using UnityEngine;

Vector2 v = new Vector2(1, 2);

// Set individual components
Vector2 v2 = v.WithX(5);
Vector2 v3 = v.WithY(10);

// Convert to Vector3
Vector3 v3a = v.ToVector3(z: 0);
Vector3 v3b = v.ToVector3XZ(y: 0); // X becomes X, Y becomes Z

// Rotate
Vector2 rotated = v.Rotate(45); // Rotate 45 degrees

// Perpendicular
Vector2 perp = v.Perpendicular();

// Angle
float angle = v.Angle();
float signedAngle = v.SignedAngle(other);

// Snap to grid
Vector2 snapped = v.SnapToGrid(1);

Quaternion Extensions​

using QWR.Utilities.Extensions;
using UnityEngine;

Quaternion q = Quaternion.identity;

// Get euler angles as Vector3
Vector3 euler = q.EulerAngles();

// Set individual euler components
Quaternion q2 = q.WithEulerX(45);
Quaternion q3 = q.WithEulerY(90);
Quaternion q4 = q.WithEulerZ(180);

// Smooth damp
Quaternion smoothed = q.SmoothDamp(target, ref velocity, smoothTime);

// Get forward/up/right directions
Vector3 forward = q.GetForwardDirection();
Vector3 up = q.GetUpDirection();
Vector3 right = q.GetRightDirection();

// Clamp rotation
Quaternion clamped = q.ClampRotation(minAngles, maxAngles);

Numeric Extensions​

using QWR.Utilities.Extensions;

// Remap value from one range to another
float remapped = 0.5f.Remap(0, 1, 0, 100); // 50

// Clamp value
float clamped = 15f.Clamp(0, 10); // 10

// Check if approximately equal
bool isEqual = 0.1f.Approximately(0.10001f); // true with default epsilon

// Check if in range
bool inRange = 5.IsInRange(0, 10); // true

// Round to decimal places
float rounded = 3.14159f.RoundToDecimals(2); // 3.14

// Sign (returns -1, 0, or 1)
int sign = (-5).Sign(); // -1

// Map to 01 range
float mapped = 5f.Map01(0, 10); // 0.5

// Wrap value
float wrapped = 15f.Wrap(0, 10); // 5

Color Extensions​

using QWR.Utilities.Extensions;
using UnityEngine;

Color color = Color.red;

// Set individual components
Color withAlpha = color.WithAlpha(0.5f);
Color withRed = color.WithRed(0.8f);
Color withGreen = color.WithGreen(0.3f);
Color withBlue = color.WithBlue(0.2f);

// Brighten/darken
Color brighter = color.Brighten(0.2f);
Color darker = color.Darken(0.2f);

// Invert
Color inverted = color.Invert();

// Convert to hex
string hex = color.ToHex(); // #FF0000

// Create from hex
Color fromHex = ColorExtensions.FromHex("#00FF00");

// HSV manipulation
Color shifted = color.ShiftHue(0.5f);
Color saturated = color.WithSaturation(0.8f);
Color valued = color.WithValue(0.7f);

Creating Your Own Extensions​

You can create your own extension methods to complement the QWR Utilities:

using QWR.Utilities.Extensions;
using UnityEngine;

// Create a new extension namespace
namespace MyGame.Extensions
{
// Create extension class
public static class MyExtensions
{
// GameObject extension
public static void HighlightObject(this GameObject obj, Color color, float duration)
{
Renderer renderer = obj.GetOrAddComponent<Renderer>();
Material originalMaterial = renderer.material;

Material highlightMaterial = new Material(originalMaterial);
highlightMaterial.color = color;
renderer.material = highlightMaterial;

MonoBehaviourExtensions.DelayedCall(() => {
if (renderer)
renderer.material = originalMaterial;
}, duration);
}

// Vector3 extension
public static Vector3 RandomOffset(this Vector3 vector, float maxOffset)
{
return vector + new Vector3(
Random.Range(-maxOffset, maxOffset),
Random.Range(-maxOffset, maxOffset),
Random.Range(-maxOffset, maxOffset)
);
}
}
}

// Usage
using MyGame.Extensions;

gameObject.HighlightObject(Color.yellow, 2.0f);
Vector3 randomPos = transform.position.RandomOffset(1.0f);

Best Practices​

Extension Method Design​

  • Keep extension methods pure (no side effects when possible)
  • Document behavior clearly with XML comments
  • Group related extensions in logical namespaces
  • Follow naming conventions that clearly describe the operation
  • Consider performance implications, especially for methods that may be called frequently

Namespace Organization​

Organize extension methods in appropriate namespaces:

// Unity-specific extensions
namespace QWR.Utilities.Extensions.Unity
{
// Unity-specific extension methods
}

// Collection extensions
namespace QWR.Utilities.Extensions.Collections
{
// Collection extension methods
}

// String extensions
namespace QWR.Utilities.Extensions.Strings
{
// String extension methods
}

Performance Considerations​

  • Be mindful of allocations in extension methods
  • Consider providing both allocating and non-allocating versions of methods
  • Use ref parameters for methods that modify the input
// Allocating version
public static Vector3[] GetWorldCorners(this RectTransform rectTransform)
{
Vector3[] corners = new Vector3[4];
rectTransform.GetWorldCorners(corners);
return corners;
}

// Non-allocating version
public static void GetWorldCorners(this RectTransform rectTransform, ref Vector3[] corners)
{
if (corners == null || corners.Length < 4)
corners = new Vector3[4];

rectTransform.GetWorldCorners(corners);
}

Next Steps​

Now that you understand extension methods, you can explore: