Skip to main content

QWR Utilities Overview

QWR Utilities is a foundation library that provides essential helper classes, singleton patterns, extension methods, and common utilities for QWR SDK development. This package serves as the backbone for the QWR Core SDK and can be used independently in any Unity project.

Package Structure

The QWR Utilities package is organized into several key components:

Runtime

The runtime components are organized into namespaces:

  • QWR.Utilities: Core utility classes and helpers
  • QWR.Utilities.Extensions: Extension methods for Unity objects
  • QWR.Utilities.Logging: Enhanced logging system
  • QWR.Utilities.Data: Data structures and serialization helpers
  • QWR.Utilities.Math: Mathematical utilities and helpers

Editor

Editor-specific utilities to enhance the Unity Editor experience:

  • QWR.Utilities.Editor: Editor extensions and custom inspectors
  • QWR.Utilities.Editor.Log: Editor logging tools
  • QWR.Utilities.Editor.ProjectSettings: Project settings utilities

Core Features

Singleton Patterns

QWR Utilities provides several singleton implementations for different use cases:

// MonoBehaviour singleton
public class GameManager : MonoBehaviourSingleton<GameManager>
{
protected override void Awake()
{
base.Awake(); // Important: Always call base.Awake()

// Your initialization code
Debug.Log("GameManager initialized");
}
}

// Usage
GameManager.Instance.DoSomething();

// ScriptableObject singleton
public class GameSettings : ScriptableSingleton<GameSettings>
{
public int difficulty = 1;
public float musicVolume = 0.8f;
}

// Usage
float volume = GameSettings.Instance.musicVolume;

// Persistent singleton (persists between scene loads)
public class SaveManager : PersistentSingleton<SaveManager>
{
protected override void Awake()
{
base.Awake();

// This manager will persist between scenes
Debug.Log("SaveManager initialized and will persist between scenes");
}
}

Logging Utilities

Enhanced logging system with categories, filtering, and file output:

// Configure logger
QWRLogger.SetLogLevel(LogLevel.Verbose);
QWRLogger.EnableFileLogging(true);

// Basic logging
QWRLogger.Log("Player initialized");
QWRLogger.LogWarning("Low memory detected");
QWRLogger.LogError("Failed to load asset");

// Categorized logging
QWRLogger.Log("Player movement updated", LogCategory.Gameplay);
QWRLogger.LogWarning("Frame time spike detected", LogCategory.Performance);
QWRLogger.LogError("Failed to load texture", LogCategory.AssetLoading);

// Conditional logging
QWRLogger.LogIf(condition, "This will only log if condition is true");

// Performance logging
using (QWRLogger.LogPerformance("ExpensiveOperation"))
{
// Code to measure
PerformExpensiveOperation();
}

Extension Methods

Useful extensions for common Unity types:

// Transform extensions
transform.ResetLocal(); // Reset position, rotation, and scale
transform.SetPositionX(5f); // Set only the X position
Vector3 worldPos = transform.TransformPointUnscaled(localPos); // Transform without scale

// GameObject extensions
GameObject obj = gameObject.GetOrAddComponent<Rigidbody>(); // Get component or add if missing
gameObject.SetLayerRecursively(LayerMask.NameToLayer("Player")); // Set layer for this object and all children
bool hasComponent = gameObject.HasComponent<Collider>(); // Check if component exists

// Collection extensions
List<int> list = new List<int> { 1, 2, 3, 4, 5 };
list.Shuffle(); // Randomize order
int randomItem = list.GetRandom(); // Get random item
list.ForEach((item, index) => Debug.Log($"Item {index}: {item}")); // Indexed forEach

// String extensions
string slug = "My Game Title".ToSlug(); // Converts to "my-game-title"
bool isMatch = "Hello World".IsMatch(@"Hello \w+"); // Regex match
string truncated = "Very long text".Truncate(10); // Truncate to "Very long..."

Data Structures

Specialized data structures for common needs:

// Object pool
var bulletPool = new QWRObjectPool<Bullet>(bulletPrefab, 20);
Bullet bullet = bulletPool.Get();
// Later
bulletPool.Release(bullet);

// Priority queue
var priorityQueue = new QWRPriorityQueue<Task>();
priorityQueue.Enqueue(new Task("High priority"), 10);
priorityQueue.Enqueue(new Task("Low priority"), 1);
Task nextTask = priorityQueue.Dequeue(); // Returns highest priority task

// Observable collections
var observableList = new QWRObservableList<int>();
observableList.OnItemAdded += item => Debug.Log($"Added: {item}");
observableList.OnItemRemoved += item => Debug.Log($"Removed: {item}");
observableList.Add(42); // Triggers OnItemAdded event

// Serializable dictionary
[Serializable]
public class ItemDatabase : QWRSerializableDictionary<string, Item> { }

// Weighted random selection
var weightedItems = new QWRWeightedList<string>();
weightedItems.Add("Common", 70);
weightedItems.Add("Uncommon", 20);
weightedItems.Add("Rare", 9);
weightedItems.Add("Legendary", 1);
string randomItem = weightedItems.GetRandom(); // Returns random item based on weights

Math Utilities

Mathematical helpers for common operations:

// Vector operations
Vector3 smoothedPos = QWRMath.SmoothDamp(current, target, ref velocity, smoothTime);
Vector3 bezierPoint = QWRMath.CubicBezier(p0, p1, p2, p3, t);
float angle = QWRMath.SignedAngle(v1, v2, axis);

// Random utilities
int randomInt = QWRMath.RandomRange(1, 10);
float randomFloat = QWRMath.RandomRange(0f, 1f);
Vector3 randomPointInSphere = QWRMath.RandomPointInSphere(radius);
Vector3 randomPointOnSphere = QWRMath.RandomPointOnSphere(radius);

// Easing functions
float eased = QWRMath.EaseInOut(0f, 1f, t);
float bounced = QWRMath.EaseBounce(0f, 1f, t);
float elastic = QWRMath.EaseElastic(0f, 1f, t);

Utility Classes

General purpose utilities:

// File I/O helpers
string json = QWRFileIO.ReadTextFile("Data/settings.json");
QWRFileIO.WriteTextFile("Data/settings.json", json);
byte[] data = QWRFileIO.ReadBinaryFile("Data/savedata.bin");

// Coroutine manager (works from non-MonoBehaviour classes)
QWRCoroutineManager.StartCoroutine(MyCoroutine());
QWRCoroutineManager.StopAllCoroutines();

// Timer utilities
QWRTimer timer = new QWRTimer(5f); // 5 second timer
timer.OnComplete += () => Debug.Log("Timer completed!");
timer.Start();
// Later
timer.Pause();
timer.Resume();
float remaining = timer.RemainingTime;

// Screenshot utility
QWRScreenshot.Capture("Screenshots/screenshot.png", 1920, 1080);

// Device utilities
bool isVR = QWRDeviceInfo.IsVRDevice;
string deviceModel = QWRDeviceInfo.DeviceModel;
float batteryLevel = QWRDeviceInfo.BatteryLevel;

Editor Utilities

Custom Inspectors

QWR Utilities includes custom inspectors for enhanced editor experience:

// Example of a custom inspector
[CustomEditor(typeof(MyComponent))]
public class MyComponentEditor : QWREditor<MyComponent>
{
protected override void OnInspectorGUI()
{
// Draw default inspector with enhancements
DrawDefaultInspector();

// Add custom GUI elements
if (GUIButton("Do Something"))
{
Target.DoSomething();
}
}
}

Editor Windows

Utility editor windows for common tasks:

// Example of a custom editor window
public class MyEditorWindow : QWREditorWindow
{
[MenuItem("QWR/My Window")]
public static void ShowWindow()
{
GetWindow<MyEditorWindow>("My Window");
}

protected override void OnGUI()
{
// Draw GUI elements
GUIHeader("My Custom Window");

if (GUIButton("Perform Action"))
{
PerformAction();
}
}
}

Project Settings

Utilities for managing project settings:

// Example of accessing project settings
QWRProjectSettings settings = QWRProjectSettings.Instance;
settings.SetValue("MyCategory", "MyKey", "MyValue");
string value = settings.GetValue<string>("MyCategory", "MyKey");

Integration with QWR Core

QWR Utilities is designed to work seamlessly with QWR Core, providing the foundation for more advanced features:

  • Singleton patterns are used throughout the Core SDK
  • Logging system provides detailed diagnostics for XR operations
  • Extension methods simplify common XR-related tasks
  • Data structures optimize performance for XR applications

Best Practices

Singleton Usage

  • Use singletons sparingly for true global systems
  • Consider using dependency injection where appropriate
  • Always call base.Awake() when overriding Awake in singleton classes

Logging

  • Use appropriate log levels for different message types
  • Add categories to logs for better filtering
  • In production builds, disable verbose logging

Extension Methods

  • Keep extension methods pure (no side effects)
  • Document the behavior clearly
  • Group related extensions in logical namespaces

Next Steps

Explore the individual components in more detail: