Skip to content

Resource Management System

Overview

The Resource Management System is a robust framework for loading, caching, and managing game assets in Unity projects built with the LoL Engine. It provides a unified interface for handling different asset types and loading methods, efficient memory management, object pooling, and optional asset versioning and updates.

Key Features

  • Unified Asset Loading API - Load assets from Resources, AssetBundles, or Addressables with a single API
  • AssetReference Support - Type-safe asset loading with Unity's AssetReference system
  • Thread-Safe Operations - Production-ready thread safety with ReaderWriterLockSlim for concurrent access
  • Handle Leak Prevention - Comprehensive Addressables handle tracking prevents memory leaks
  • Addressables Integration - First-class support for Unity Addressables with automatic initialization
  • Asynchronous Loading - Full support for asynchronous operations using Tasks
  • Reference Counting - Intelligent asset caching with reference counting
  • Memory Management - Budget-based memory management with asset unloading
  • Object Pooling - Efficient GameObject recycling to reduce instantiation overhead
  • Asset Versioning - Track asset versions and dependencies
  • Update System - Download and manage asset updates at runtime
  • Event Notifications - Integration with the engine's event system
  • Editor Tools - Asset versioning and management utilities

AssetReference Features

  • Type Safety: Compile-time validation of asset references
  • Inspector Integration: Drag-and-drop asset assignment in Unity Inspector
  • Automatic Validation: RuntimeKeyIsValid() checks prevent invalid references
  • Seamless Integration: Works with existing ResourceService caching and events
  • Memory Management: Proper release handling for AssetReference-loaded assets

Thread Safety & Memory Management

  • Thread-Safe Handle Tracking: All Addressables AsyncOperationHandle objects are tracked thread-safely
  • Leak Prevention: Comprehensive handle lifecycle management prevents memory leaks
  • Concurrent Access: ReaderWriterLockSlim enables multiple concurrent readers for better performance
  • Resource Cleanup: Automatic handle cleanup during service shutdown
  • Handle Monitoring: Debug handle counts and detect potential leaks early

Architecture

The Resource Management System consists of several key components:

  1. Resource Service - The main entry point for loading and managing assets
  2. Resource Loaders - Strategy pattern for different loading methods
  3. Resource Pool - GameObject pooling for efficient reuse
  4. Version Catalog - Tracks asset versions and updates
  5. Asset Updater - Handles downloading and applying updates
  6. Configuration - Configurable settings for resource management

Getting Started

Quick Start

  1. Enable Resource Service in your ServiceConfiguration asset:
  2. [x] Enable Resource Service

  3. Create ResourceManagementConfig asset:

  4. Right-click in Project → Create → LoLEngine → Config → Resource Management Config
  5. Save at: Assets/[YourProject]/Resources/Configs/DefaultResourceManagementConfig.asset
  6. CRITICAL: Must be in Resources/Configs/ folder for auto-discovery

  7. Configure ResourceManagementConfig Settings:

  8. enableAddressables (true) - Use Addressables as primary loading system
  9. preferredSource (Addressables) - Which loader to try first
  10. fallbackToResources (true) - Fall back to Resources folder if Addressables fails
  11. maxMemoryBudgetMB (512f) - Maximum cached assets memory in MB
    • VR RECOMMENDATION: Set to 256MB or lower for VR projects
  12. remoteCatalogUrl - URL for version catalog (for asset updates)
  13. assetDownloadSubPath - Where to download updated assets

  14. Add ImprovedGameInitializer to your first scene:

  15. Assign ServiceConfiguration asset
  16. Services auto-register via ConfigurableServiceInitializer

Configuration Details

ResourceManagementConfig Fields (actual implementation):

[Header("Asset Updater Settings")]
public string remoteCatalogUrl = "https://moomoo.games/LoLEngine/version.json";
public string localCatalogFileName = "version_catalog.json";
public string assetDownloadSubPath = "UpdatedAssets";

[Header("Addressables Settings")]
public bool enableAddressables = true;
public ResourceSource preferredSource = ResourceSource.Addressables;
public bool fallbackToResources = true;

[Header("Resource Service Settings")]
public float maxMemoryBudgetMB = 512f;  // Set to 0 or negative for unlimited

Path Requirements: - Config MUST be at: Assets/Resources/Configs/DefaultResourceManagementConfig.asset - Or configure custom path in ResourcePathConfig asset - Engine auto-loads config from Resources folder on initialization

Loading Assets

String-Based Loading (Traditional)

using LoLEngine.Core.ResourceManagement.Interfaces;
using LoLEngine.Core.ServiceManagement.Service;
using LoLEngine.Runtime;
using UnityEngine;

public class MyGameController : MonoBehaviour
{
    private IResourceService _resourceService;
    private bool _isInitialized = false;

    private void Start()
    {
        if (ServiceAwaiter.AreServicesReady())
            OnServicesReady();
        else
            ServiceAwaiter.WaitForServices(this, OnServicesReady, OnServicesTimeout);
    }

    private void OnServicesReady()
    {
        _resourceService = ServiceLocator.Instance.Get<IResourceService>();
        _isInitialized = true;
    }

    private void OnServicesTimeout()
    {
        Debug.LogError("Timed out waiting for services");
    }

    // Synchronous loading (simple assets)
    public void LoadTexture()
    {
        Texture2D texture = _resourceService.Load<Texture2D>("Textures/MyTexture");
        if (texture != null)
        {
            // Use the texture
        }
    }

    // Asynchronous loading (recommended)
    public async void LoadModelAsync()
    {
        GameObject model = await _resourceService.LoadAsync<GameObject>("Models/MyModel");
        if (model != null)
        {
            // Use the model
        }
    }

    // With options
    public async void LoadWithOptions()
    {
        var options = new ResourceLoadOptions
        {
            Priority = ResourceLoadPriority.High,
            KeepInMemory = true,
            FallbackIds = new[] { "Fallback1", "Fallback2" }
        };

        AudioClip sound = await _resourceService.LoadAsync<AudioClip>("Sounds/MySound", options);
    }
}
using LoLEngine.Core.ResourceManagement.Interfaces;
using LoLEngine.Core.ServiceManagement.Service;
using LoLEngine.Runtime;
using UnityEngine;
#if UNITY_ADDRESSABLES
using UnityEngine.AddressableAssets;
#endif

public class AssetReferenceGameController : MonoBehaviour
{
    [Header("Asset References - Drag assets here in Inspector")]
#if UNITY_ADDRESSABLES
    [SerializeField] private AssetReference playerPrefabRef;
    [SerializeField] private AssetReference backgroundMusicRef;
    [SerializeField] private AssetReference uiTextureRef;
#endif

    private IResourceService _resourceService;
    private bool _isInitialized = false;

    private void Start()
    {
        if (ServiceAwaiter.AreServicesReady())
            OnServicesReady();
        else
            ServiceAwaiter.WaitForServices(this, OnServicesReady, OnServicesTimeout);
    }

    private void OnServicesReady()
    {
        _resourceService = ServiceLocator.Instance.Get<IResourceService>();
        _isInitialized = true;
    }

    private void OnServicesTimeout()
    {
        Debug.LogError("Timed out waiting for services");
    }

#if UNITY_ADDRESSABLES
    // Type-safe AssetReference loading
    public async void LoadPlayerPrefab()
    {
        if (playerPrefabRef != null && playerPrefabRef.RuntimeKeyIsValid())
        {
            GameObject playerPrefab = await _resourceService.LoadAsync<GameObject>(playerPrefabRef);
            if (playerPrefab != null)
            {
                // Instantiate player at spawn point
                Instantiate(playerPrefab, Vector3.zero, Quaternion.identity);
            }
        }
    }

    // AssetReference with options
    public async void LoadBackgroundMusic()
    {
        if (backgroundMusicRef != null && backgroundMusicRef.RuntimeKeyIsValid())
        {
            var options = new ResourceLoadOptions
            {
                KeepInMemory = true,  // Keep music in memory
                Priority = ResourceLoadPriority.High
            };

            AudioClip music = await _resourceService.LoadAsync<AudioClip>(backgroundMusicRef, options);
            if (music != null)
            {
                // Play background music
                var audioSource = GetComponent<AudioSource>();
                audioSource.clip = music;
                audioSource.Play();
            }
        }
    }

    // Proper cleanup with AssetReference
    private void OnDestroy()
    {
        // Release AssetReference resources
        if (_resourceService != null)
        {
            if (playerPrefabRef != null) _resourceService.Release(playerPrefabRef);
            if (backgroundMusicRef != null) _resourceService.Release(backgroundMusicRef);
            if (uiTextureRef != null) _resourceService.Release(uiTextureRef);
        }
    }
#endif
}

Object Pooling

using LoLEngine.Core.ResourceManagement.Interfaces;
using LoLEngine.Core.ServiceManagement.Service;
using LoLEngine.Runtime;
using UnityEngine;

public class EnemySpawner : MonoBehaviour
{
    [SerializeField] private string enemyPrefabId = "Prefabs/Enemy";

    private IResourceService _resourceService;
    private bool _isInitialized = false;

    private void Start()
    {
        if (ServiceAwaiter.AreServicesReady())
            OnServicesReady();
        else
            ServiceAwaiter.WaitForServices(this, OnServicesReady, OnServicesTimeout);
    }

    private void OnServicesReady()
    {
        _resourceService = ServiceLocator.Instance.Get<IResourceService>();
        _isInitialized = true;
    }

    private void OnServicesTimeout()
    {
        Debug.LogError("Timed out waiting for services");
    }

    // Spawn an enemy
    public void SpawnEnemy(Vector3 position)
    {
        GameObject enemy = _resourceService.Instantiate(enemyPrefabId, position, Quaternion.identity);

        // The enemy is either newly instantiated or recycled from the pool
    }

    // Return an enemy to the pool
    public void RecycleEnemy(GameObject enemy)
    {
        _resourceService.Recycle(enemy);

        // The enemy is now inactive and back in the pool
    }
}

Versioning and Updates

Note: Asset versioning and updates are optional advanced features. Most games won't need this.

using LoLEngine.Core.ResourceManagement.Interfaces;
using LoLEngine.Core.ServiceManagement.Service;
using LoLEngine.Runtime;
using UnityEngine;

public class GameUpdater : MonoBehaviour
{
    private IAssetUpdaterService _assetUpdater;
    private bool _isInitialized = false;

    private void Start()
    {
        if (ServiceAwaiter.AreServicesReady())
            OnServicesReady();
        else
            ServiceAwaiter.WaitForServices(this, OnServicesReady, OnServicesTimeout);
    }

    private void OnServicesReady()
    {
        // AssetUpdaterService is registered under its interface when enabled in ServiceConfiguration.
        _assetUpdater = ServiceLocator.Instance.Get<IAssetUpdaterService>();
        if (_assetUpdater != null)
        {
            _isInitialized = true;
        }
        else
        {
            Debug.LogWarning("IAssetUpdaterService not available — enable it in ServiceConfiguration.");
        }
    }

    private void OnServicesTimeout()
    {
        Debug.LogError("Timed out waiting for services");
    }

    // Check for updates
    public async void CheckForUpdates()
    {
        int updateCount = await _assetUpdater.CheckForUpdates();
        if (updateCount > 0)
        {
            Debug.Log($"Found {updateCount} updates available");
        }
    }

    // Download all updates
    public async void DownloadUpdates(System.IProgress<float> progress = null)
    {
        bool success = await _assetUpdater.DownloadAllUpdates(progress);
        if (success)
        {
            Debug.Log("Updates downloaded successfully");
        }
    }
}

Event Handling

using LoLEngine.Core.Events.Interfaces;
using LoLEngine.Core.Events.Providers;
using LoLEngine.Core.ResourceManagement.Events;
using LoLEngine.Core.ServiceManagement.Service;
using LoLEngine.Runtime;
using UnityEngine;

public class ResourceMonitor : MonoBehaviour,
    IEventListener<ResourceEvents.ResourceLoaded>,
    IEventListener<ResourceEvents.ResourceLoadFailed>
{
    private bool _isInitialized = false;

    private void Start()
    {
        if (ServiceAwaiter.AreServicesReady())
            OnServicesReady();
        else
            ServiceAwaiter.WaitForServices(this, OnServicesReady, OnServicesTimeout);
    }

    private void OnServicesReady()
    {
        this.EventStartListening<ResourceEvents.ResourceLoaded>();
        this.EventStartListening<ResourceEvents.ResourceLoadFailed>();
        _isInitialized = true;
    }

    private void OnServicesTimeout()
    {
        Debug.LogError("Timed out waiting for services");
    }

    private void OnDisable()
    {
        this.EventStopListening<ResourceEvents.ResourceLoaded>();
        this.EventStopListening<ResourceEvents.ResourceLoadFailed>();
    }

    public void OnGameEvent(ResourceEvents.ResourceLoaded evt)
    {
        Debug.Log($"Resource loaded: {evt.ResourceId} of type {evt.AssetType.Name}");
    }

    public void OnGameEvent(ResourceEvents.ResourceLoadFailed evt)
    {
        Debug.LogError($"Resource load failed: {evt.ResourceId}, Error: {evt.Error}");
    }
}

Integrating in Your Game

Automatic Integration (Current System)

The Resource Management System integrates automatically via ImprovedGameInitializer and ServiceConfiguration.

No manual service registration needed! Services auto-register when you:

  1. Enable "Enable Resource Service" in ServiceConfiguration asset
  2. Create ResourceManagementConfig at Assets/Resources/Configs/DefaultResourceManagementConfig.asset
  3. Add ImprovedGameInitializer to your scene

That's it! The engine handles all service initialization automatically.

Advanced: Custom Game Init Logic

If you need custom logic during initialization:

using LoLEngine.Core.GameInitializer;
using LoLEngine.Core.ResourceManagement.Interfaces;
using LoLEngine.Core.ServiceManagement.Service;
using LoLEngine.Runtime;
using UnityEngine;

public class MyGameController : MonoBehaviour
{
    private IResourceService _resourceService;
    private bool _isInitialized = false;

    private void Start()
    {
        // Wait for automatic service initialization
        if (ServiceAwaiter.AreServicesReady())
            OnServicesReady();
        else
            ServiceAwaiter.WaitForServices(this, OnServicesReady, OnServicesTimeout);
    }

    private void OnServicesReady()
    {
        // Access services after automatic initialization
        _resourceService = ServiceLocator.Instance.Get<IResourceService>();
        _isInitialized = true;

        // Your custom initialization logic here
        Debug.Log("ResourceService is ready!");
    }

    private void OnServicesTimeout()
    {
        Debug.LogError("Services failed to initialize");
    }
}

Step 1: Configure Memory Budgets

Adjust memory budgets based on your target platform:

Desktop/Console: 512MB-1GB (default 512MB) Mobile: 256MB-384MB VR (Quest): 128MB-256MB CRITICAL for VR VR (PC): 256MB-384MB

Edit in ResourceManagementConfig asset or via code:

private void OnServicesReady()
{
    _resourceService = ServiceLocator.Instance.Get<IResourceService>();

    // Platform-specific memory budgets
    #if UNITY_STANDALONE || UNITY_EDITOR
        _resourceService.SetMemoryBudget(512 * 1024 * 1024); // 512MB desktop
    #elif UNITY_ANDROID || UNITY_IOS
        _resourceService.SetMemoryBudget(256 * 1024 * 1024); // 256MB mobile
    #endif
}

Step 2: Organize Your Resources

  1. Put assets in the appropriate folders:
  2. Resources folder for direct loading
  3. AssetBundles for grouped loading
  4. Addressables for modern async loading with remote capabilities

  5. Set up Addressables (recommended):

  6. Window > Asset Management > Addressables > Groups
  7. Mark assets as Addressable
  8. Set meaningful Address names (e.g., "Audio/BackgroundMusic01")

  9. Use consistent naming conventions for resource IDs

Step 4: Take Advantage of Object Pooling

For frequently instantiated and destroyed objects (bullets, enemies, particles, etc.), use the pooling system to improve performance.

Step 5: Set Up Asset Versioning (Optional)

If you need asset updates:

  1. Set up a server to host your version catalog and assets
  2. Use the Asset Versioning Tool to generate version information
  3. Implement update checking and downloading in your game

Thread Safety & Handle Management

Enhanced Thread Safety

The ResourceService and AddressableResourceLoader now provide production-ready thread safety:

// All operations are thread-safe and can be called from any thread
private async Task LoadAssetsFromMultipleThreads()
{
    var tasks = new List<Task>();

    // Safe to call from multiple threads simultaneously
    tasks.Add(Task.Run(async () => await _resourceService.LoadAsync<Texture2D>("UI/Icon1")));
    tasks.Add(Task.Run(async () => await _resourceService.LoadAsync<Texture2D>("UI/Icon2")));
    tasks.Add(Task.Run(async () => await _resourceService.LoadAsync<AudioClip>("Audio/SFX1")));

    await Task.WhenAll(tasks);
}

Handle Leak Prevention

All Addressables handles are now automatically tracked and cleaned up:

// Check handle count for debugging
public void MonitorHandles()
{
    var stats = _resourceService.GetResourceStats();

    // Handle counts are automatically logged in ResourceStats
    // Active AssetReference handles: 5
    // Active AddressableResourceLoader handles: 12

    // Manual cleanup if needed
    _resourceService.Shutdown(); // Releases all tracked handles
}

Debugging Handle Leaks

Monitor handle usage during development:

public class ResourceMonitor : MonoBehaviour
{
    private IResourceService _resourceService;

    private void Start()
    {
        _resourceService = ServiceLocator.Instance.Get<IResourceService>();

        // Check handles periodically
        InvokeRepeating(nameof(LogHandleStats), 10f, 10f);
    }

    private void LogHandleStats()
    {
        // This will log active handle counts to help detect leaks
        _resourceService.GetResourceStats();
    }
}

Best Practices

  1. Use AssetReferences When Possible - Prefer AssetReference over string-based loading for:
  2. Type Safety: Compile-time validation of asset types
  3. Inspector Integration: Drag-and-drop asset assignment
  4. Automatic Dependency Tracking: Unity handles asset dependencies
  5. Refactoring Safety: Asset moves don't break references

  6. Asynchronous Loading - Prefer async methods for larger assets to avoid frame rate spikes

  7. Resource IDs - For string-based loading, use consistent, hierarchical naming (e.g., "Characters/Player", "UI/MainMenu")

  8. Memory Management - Release assets when no longer needed:

    // For AssetReference
    _resourceService.Release(myAssetReference);
    
    // For string-based loading
    _resourceService.Release("path/to/asset");
    

  9. Preloading - Preload assets during loading screens to avoid hitches during gameplay

  10. Pooling - Use object pooling for frequently instantiated objects

  11. Cleanup - Make sure to release resources in OnDestroy or when switching scenes

  12. AssetReference Validation - Always check RuntimeKeyIsValid() before loading:

    if (assetRef != null && assetRef.RuntimeKeyIsValid())
    {
        var asset = await _resourceService.LoadAsync<T>(assetRef);
    }
    

  13. Handle Monitoring - In development builds, monitor handle counts to detect leaks early:

    #if DEVELOPMENT_BUILD || UNITY_EDITOR
    _resourceService.GetResourceStats(); // Logs handle counts
    #endif
    

Common Pitfalls

  1. Memory Leaks - Not releasing assets when they're no longer needed
  2. Main Thread Blocking - Using synchronous loading for large assets during gameplay
  3. Path Inconsistencies - Inconsistent casing or slashes in resource paths
  4. Missing References - Not checking for null when loading assets
  5. Pool Bloat - Not setting maximum pool sizes for object pools

Addressables Integration

Setting Up Addressables

  1. Initialize Addressables in Unity:

    Window > Asset Management > Addressables > Groups
    → Click "Create Addressables Settings" if not already done
    

  2. Mark Assets as Addressable:

  3. Select assets in Project window
  4. Check "Addressable" checkbox
  5. Set Address to meaningful names (e.g., "Audio/BackgroundMusic01")

  6. Configure ResourceManagementConfig:

    // Set these values in your ResourceManagementConfig asset
    enableAddressables = true
    preferredSource = ResourceSource.Addressables
    fallbackToResources = true
    

Loading priority

When Addressables is enabled in ResourceManagementConfig, ResourceService tries loaders in this order:

  1. Addressables (AddressableResourceLoader) — if enabled and initialized
  2. Resources (ResourcesLoader) — if fallbackToResources is true
  3. AssetBundles (AssetBundleLoader)

Your game code stays the same — LoadAsync<T>("Audio/MyClip") uses the configured priority automatically. AudioService and other consumers load through IResourceService; no per-service Addressables setup is required.

Editor setup checklist:

  1. Window > Asset Management > Addressables > Groups → Create Addressables Settings
  2. Mark assets Addressable with meaningful addresses (e.g. Audio/BackgroundMusic01)
  3. Create Resource Management Config (Create > LoLEngine > Config > Resource Management Config)
  4. Set enableAddressables = true, preferredSource = Addressables
  5. Assign config to ServiceConfiguration and test:
var clip = await ServiceLocator.Instance.Get<IResourceService>()
    .LoadAsync<AudioClip>("Audio/YourAddressableAudio");

Addressables Benefits

  • Async-First: Built for non-blocking asset loading
  • Remote Content: Download assets from CDN/servers
  • Memory Management: Automatic reference counting and cleanup
  • Build Optimization: Assets can be built separately from main app
  • Progress Tracking: Built-in loading progress reports

Migration from Resources

Replace Resources folder assets with Addressable assets:

// OLD (Resources folder):
// "Resources/Audio/Music.wav" → Resources.Load<AudioClip>("Audio/Music")

// NEW (Addressables):
// Mark "Audio/Music.wav" as Addressable with Address "Audio/Music"
// Same ResourceService code works automatically!
var music = await _resourceService.LoadAsync<AudioClip>("Audio/Music");

Advanced Features

Custom asset loaders

ResourceService registers built-in loaders during Initialize():

  • AddressableResourceLoader (when Addressables enabled)
  • ResourcesLoader
  • AssetBundleLoader

There is no public RegisterLoader() API — custom loading strategies should go through IResourceService configuration or game-layer wrappers. Forking the engine to add IResourceLoader implementations is possible but not a supported extension point for Asset Store projects today.

If you only need a different source priority, use ResourceManagementConfig.preferredSource and fallbackToResources.

Memory Budget Tuning

See "Step 1: Configure Memory Budgets" section above for platform-specific recommendations.

Adjust memory budgets dynamically at runtime:

private void OnServicesReady()
{
    var resourceService = ServiceLocator.Instance.Get<IResourceService>();

    // Set different budgets based on platform
    #if UNITY_ANDROID || UNITY_IOS
        resourceService.SetMemoryBudget(256 * 1024 * 1024); // 256MB for mobile
    #elif UNITY_STANDALONE || UNITY_EDITOR
        resourceService.SetMemoryBudget(512 * 1024 * 1024); // 512MB for desktop
    #endif
}

VR-SPECIFIC RESOURCE MANAGEMENT RECOMMENDATIONS

Critical VR Considerations

Memory Constraints - VR requires double rendering (one per eye), leaving significantly less memory for assets: - Quest 2/3: Set maxMemoryBudgetMB = 128-256 (NOT 512!) - PC VR: Set maxMemoryBudgetMB = 256-384 depending on GPU VRAM

Asynchronous Loading is MANDATORY - VR cannot afford frame drops:

// NEVER in VR - causes stuttering and nausea
var model = _resourceService.Load<GameObject>("Models/Enemy");

// ALWAYS use async in VR
var model = await _resourceService.LoadAsync<GameObject>("Models/Enemy");

Aggressive Object Pooling - Essential for 90fps minimum:

// Pool EVERYTHING that spawns frequently
// Even small objects add up with VR's strict frame budget
_resourceService.Instantiate(bulletPrefab, pos, rot);  // Uses pool automatically
_resourceService.Recycle(bullet);  // Return to pool - CRITICAL!

Preloading During Load Screens - Never load assets mid-gameplay in VR:

async void PreloadVRAssets()
{
    await _resourceService.LoadAsync<GameObject>("Player/HandModels");
    await _resourceService.LoadAsync<GameObject>("UI/VRMenuPrefab");
    await _resourceService.LoadAsync<AudioClip>("Audio/UIClick");
    // Preload ALL gameplay assets before entering VR scene
}

Texture Compression - Mandatory for VR: - Use ASTC compression for Quest - Use BC7 for PC VR - Enable Addressables texture streaming - Set preferredSource = ResourceSource.Addressables

Handle Leak Prevention - CRITICAL for long VR sessions:

// Monitor handles in development builds
#if DEVELOPMENT_BUILD || UNITY_EDITOR
void Update()
{
    if (Time.frameCount % 300 == 0)  // Every 5 seconds at 60fps
    {
        _resourceService.GetResourceStats();  // Logs handle counts
    }
}
#endif

Release Assets Aggressively:

// VR has tight memory limits - clean up immediately
void OnSceneUnload()
{
    _resourceService.Release("LevelSpecificAsset");
    _resourceService.ReleaseAll();  // When changing levels
}

Troubleshooting

Asset Not Found

  • Check that the resource ID (path) is correct, including case
  • Verify the asset is in the correct location
  • Check for compilation errors in asset scripts

Memory Issues

  • Monitor memory usage with GetResourceStats()
  • Release unused assets
  • Set appropriate memory budgets
  • Use object pooling for frequent instantiation

Slow Loading

  • Use asynchronous loading for large assets
  • Preload assets during loading screens
  • Consider using asset bundles or addressables for better loading
  • Compress textures and audio appropriately

Architecture Diagram

+-------------------+       +-------------------+
|                   |       |                   |
| Game Code         |------>| IResourceService  |
|                   |       |                   |
+-------------------+       +--------+----------+
                                     |
                                     | implements
                                     v
+-----------------+        +-------------------+
|                 |        |                   |
| ResourceEvents  |<------>| ResourceService   |
|                 |        |                   |
+-----------------+        +--------+----------+
                                    |
                                    | uses
                                    v
+-------------------+     +-------------------+     +------------------------+
|                   |     |                   |     |                        |
| ResourcesLoader   |     | AssetBundleLoader |     | AddressableResourceLdr |
|                   |     |                   |     |  (NEW - Priority #1)   |
+-------------------+     +-------------------+     +------------------------+
                                    |
                                    | manages
                                    v
+-------------------+     +-------------------+     +-------------------+
|                   |     |                   |     |                   |
| ResourcePool      |     | AssetUpdaterService     | VersionCatalog    |
|                   |     |                   |     |                   |
+-------------------+     +-------------------+     +-------------------+