Skip to main content

Performance Optimization

Learn how to optimize SoapKit usage for better performance in your Unity projects.

Editor Performance Settings​

SoapKit includes performance optimization settings specifically designed to reduce editor impact during gameplay testing. These settings can be configured through the Settings Window.

Performance Modes​

Optimizes all editor tools for smooth gameplay:

  • Disables debug window auto-refresh during play mode
  • Stops performance monitoring real-time updates
  • Reduces refresh intervals to minimize impact
  • Maintains essential debugging capabilities

🔧 Development Mode (Full Debugging)​

Enables all debugging features:

  • Real-time variable monitoring
  • Live event tracking
  • High-frequency performance analysis
  • Immediate visual feedback

Automatic Optimizations​

SoapKit automatically applies performance optimizations based on your current development phase:

// Performance settings are automatically applied
if (Application.isPlaying && SOAPEditorSettings.DisableDebugDuringPlayMode)
{
// Debug tools reduce their update frequency
// Focus shifts to gameplay performance
}

Manual Configuration​

Access performance settings through multiple paths:

  • Window > SoapKit > Settings
  • Tools > SoapKit > ⚙️ Settings
  • Debug Window toolbar
  • Project Settings integration

Performance Impact Comparison​

SettingFrame Time ImpactMemory UsageUpdate Frequency
Performance Mode~0.1msLow3-5 seconds
Default Settings~0.3msMedium2 seconds
Development Mode~0.8msHigher1 second

Best Practices​

  1. Use Performance Mode during final testing

    • Ensures accurate performance measurements
    • Reduces editor interference with gameplay
  2. Switch to Development Mode for debugging

    • Immediate feedback on system changes
    • Real-time monitoring of edge cases
  3. Export and share team settings

    • Consistent performance profiles
    • Standardized debugging environments

Event System Performance​

Listener Management​

Efficiently manage event listeners to avoid performance issues:

public class OptimizedEventListener : MonoBehaviour
{
[SerializeField] private GameEvent targetEvent;

private void OnEnable()
{
// Only subscribe when component is active
targetEvent?.AddListener(HandleEvent);
}

private void OnDisable()
{
// Always unsubscribe to prevent memory leaks
targetEvent?.RemoveListener(HandleEvent);
}

private void HandleEvent()
{
// Keep event handlers lightweight
StartCoroutine(HandleEventAsync());
}

private IEnumerator HandleEventAsync()
{
// Move heavy operations to coroutines
yield return null;
// Heavy work here
}
}

Event Frequency Optimization​

Throttle high-frequency events:

public class ThrottledHealthSystem : MonoBehaviour
{
[SerializeField] private IntVariable playerHealth;
[SerializeField] private IntGameEvent onHealthChanged;
[SerializeField] private float updateInterval = 0.1f;

private int lastBroadcastHealth;
private float lastUpdateTime;

private void Update()
{
if (Time.time - lastUpdateTime >= updateInterval)
{
CheckHealthChange();
lastUpdateTime = Time.time;
}
}

private void CheckHealthChange()
{
if (playerHealth.Value != lastBroadcastHealth)
{
lastBroadcastHealth = playerHealth.Value;
onHealthChanged.Raise(playerHealth.Value);
}
}
}

Variable System Performance​

Efficient Value Setting​

Minimize unnecessary change notifications:

public class EfficientVariableUpdater : MonoBehaviour
{
[SerializeField] private FloatVariable targetValue;

public void UpdateValueEfficiently(float newValue)
{
// Only update if value actually changed
if (!Mathf.Approximately(targetValue.Value, newValue))
{
targetValue.SetValue(newValue);
}
}

public void BatchUpdateValues(float[] values)
{
// Temporarily disable notifications for batch operations
bool wasNotifying = targetValue.EnableChangeNotification;
targetValue.EnableChangeNotification = false;

foreach (float value in values)
{
targetValue.SetValue(value);
}

// Re-enable and trigger final notification
targetValue.EnableChangeNotification = wasNotifying;
if (wasNotifying)
{
targetValue.NotifyValueChanged();
}
}
}

Memory-Efficient Collections​

Use object pooling for frequently created objects:

public class PooledGameEventManager : MonoBehaviour
{
private static readonly Queue<GameEventArgs> eventArgsPool = new Queue<GameEventArgs>();

public static GameEventArgs GetEventArgs()
{
return eventArgsPool.Count > 0 ? eventArgsPool.Dequeue() : new GameEventArgs();
}

public static void ReturnEventArgs(GameEventArgs args)
{
args.Reset();
eventArgsPool.Enqueue(args);
}
}

public class GameEventArgs
{
public float FloatValue { get; set; }
public int IntValue { get; set; }
public string StringValue { get; set; }

public void Reset()
{
FloatValue = 0f;
IntValue = 0;
StringValue = string.Empty;
}
}

Asset Loading Performance​

Preload Critical Assets​

Load frequently used ScriptableObjects at startup:

public class AssetPreloader : MonoBehaviour
{
[SerializeField] private ScriptableObject[] criticalAssets;

private void Awake()
{
PreloadAssets();
}

private void PreloadAssets()
{
foreach (var asset in criticalAssets)
{
// Touch the asset to ensure it's loaded
_ = asset.name;
}
}
}

Lazy Loading​

Implement lazy loading for non-critical assets:

public class LazyAssetLoader : MonoBehaviour
{
private IntVariable _cachedHealth;

public IntVariable PlayerHealth
{
get
{
if (_cachedHealth == null)
{
_cachedHealth = Resources.Load<IntVariable>("Variables/PlayerHealth");
}
return _cachedHealth;
}
}
}

Profiling and Monitoring​

Built-in Performance Monitoring​

Use SoapKit's built-in performance tools:

public class PerformanceMonitor : MonoBehaviour
{
[SerializeField] private GameEvent[] eventsToMonitor;

private void Update()
{
foreach (var gameEvent in eventsToMonitor)
{
// Monitor listener count
if (gameEvent.ListenerCount > 10)
{
Debug.LogWarning($"Event {gameEvent.name} has {gameEvent.ListenerCount} listeners");
}

// Monitor event frequency
if (gameEvent.EventsRaisedThisFrame > 5)
{
Debug.LogWarning($"Event {gameEvent.name} raised {gameEvent.EventsRaisedThisFrame} times this frame");
}
}
}
}

Custom Performance Metrics​

Track your own performance metrics:

public static class SoapKitProfiler
{
private static readonly Dictionary<string, float> metrics = new Dictionary<string, float>();

public static void StartTiming(string operation)
{
metrics[operation] = Time.realtimeSinceStartup;
}

public static void EndTiming(string operation)
{
if (metrics.TryGetValue(operation, out float startTime))
{
float duration = Time.realtimeSinceStartup - startTime;
Debug.Log($"{operation} took {duration:F4}ms");
metrics.Remove(operation);
}
}
}

Best Practices​

Event System​

  1. Limit Listeners: Keep listener counts reasonable (< 20 per event)
  2. Lightweight Handlers: Keep event handlers fast and simple
  3. Async Operations: Move heavy work to coroutines or async methods
  4. Unsubscribe Properly: Always pair AddListener with RemoveListener

Variable System​

  1. Batch Updates: Group multiple variable changes when possible
  2. Check Before Set: Only update when values actually change
  3. Use Appropriate Types: Choose the right variable type for your data
  4. Validate Constraints: Use built-in constraints instead of manual validation

Asset Management​

  1. Preload Critical Assets: Load important assets at startup
  2. Use Asset References: Prefer asset references over Resources.Load
  3. Pool Objects: Reuse objects instead of constant allocation
  4. Monitor Memory: Use Unity Profiler to track ScriptableObject memory usage

Performance Testing​

Automated Performance Tests​

[TestFixture]
public class SoapKitPerformanceTests
{
[Test]
public void EventRaisePerformanceTest()
{
var gameEvent = ScriptableObject.CreateInstance<GameEvent>();
var stopwatch = System.Diagnostics.Stopwatch.StartNew();

for (int i = 0; i < 10000; i++)
{
gameEvent.Raise();
}

stopwatch.Stop();
Assert.Less(stopwatch.ElapsedMilliseconds, 100, "Event raising took too long");
}

[Test]
public void VariableUpdatePerformanceTest()
{
var variable = ScriptableObject.CreateInstance<IntVariable>();
var stopwatch = System.Diagnostics.Stopwatch.StartNew();

for (int i = 0; i < 10000; i++)
{
variable.SetValue(i);
}

stopwatch.Stop();
Assert.Less(stopwatch.ElapsedMilliseconds, 50, "Variable updates took too long");
}
}

Troubleshooting Performance Issues​

Common Performance Problems​

  1. Too Many Listeners: Check listener counts in the SoapKit Debug Window
  2. Frequent Events: Monitor event frequency using built-in tools
  3. Memory Leaks: Use Unity Profiler to detect growing memory usage
  4. Blocking Operations: Profile event handlers for long-running code

Using SoapKit Performance Analyzer​

The SoapKit Performance Analyzer provides real-time monitoring:

  1. Open Window > SoapKit > Performance Analyzer
  2. Monitor hotspots in real-time
  3. Get optimization recommendations
  4. Track system health over time