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:
- Data Structures: Learn about specialized data structures
- Singleton Patterns: Discover different singleton implementations
- Logging: Explore the enhanced logging system