Skip to content

Commit

Permalink
Improving asset bundle simulation mode, added new method to load mult…
Browse files Browse the repository at this point in the history
…iple bundles and this method reports the progress of loaded bundles. Various minor fixes.
  • Loading branch information
nievesj committed Jul 7, 2018
1 parent 22cfb72 commit a625f69
Show file tree
Hide file tree
Showing 8 changed files with 442 additions and 374 deletions.
44 changes: 31 additions & 13 deletions Services/AssetService/AssetBundleLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,6 @@ internal AssetBundleLoader()
/// <returns> Observable </returns>
internal async Task<T> LoadAsset<T>(BundleRequest bundleRequest, bool forceLoadFromStreamingAssets) where T : Object
{
#if UNITY_EDITOR
if (EditorPreferences.EDITORPREF_SIMULATE_ASSET_BUNDLES)
return await SimulateAssetBundle<T>(bundleRequest);
#endif
var bundle = await LoadBundle(bundleRequest, forceLoadFromStreamingAssets);
return await bundle.LoadAssetAsync<T>(bundleRequest.AssetName);
}
Expand All @@ -54,6 +50,30 @@ internal async Task<LoadedBundle> LoadBundle(BundleRequest bundleRequest, bool f
{
if (!_loadedBundles.ContainsKey(bundleRequest.BundleName))
{

#if UNITY_EDITOR
if (EditorPreferences.EDITORPREF_SIMULATE_ASSET_BUNDLES)
{
var assPath = bundleRequest.AssetCategory.ToString().ToLower() + "/" + bundleRequest.BundleName;
var paths = AssetDatabase.GetAssetPathsFromAssetBundle(assPath);

var theone = string.Empty;
foreach (var path in paths)
{
if (path.ToLower().Contains(bundleRequest.AssetName))
{
theone = path;
break;
}
}

var asset = AssetDatabase.LoadAssetAtPath<GameObject>(theone);
_loadedBundles.Add(bundleRequest.BundleName, new LoadedBundle(asset));

return _loadedBundles[bundleRequest.BundleName];
}
#endif

AssetBundle bundle;
if (_assetService.UseStreamingAssets || forceLoadFromStreamingAssets)
bundle = await GetBundleFromStreamingAssetsAsync(bundleRequest);
Expand All @@ -80,7 +100,7 @@ internal async Task<Object> LoadScene(BundleRequest bundleRequest, bool forceLoa
internal async Task UnloadAssetBundle(string name, bool unloadAllDependencies)
{
name = name.ToLower();

if (_loadedBundles.ContainsKey(name))
{
_loadedBundles[name].Unload(unloadAllDependencies);
Expand All @@ -90,10 +110,10 @@ internal async Task UnloadAssetBundle(string name, bool unloadAllDependencies)
}
}

internal T GetLoadedBundle<T>(string name) where T : Object
internal AssetBundle GetLoadedBundle(string name)
{
if (_loadedBundles.ContainsKey(name.ToLower()))
return _loadedBundles[name.ToLower()].Bundle as T;
return _loadedBundles[name.ToLower()].Bundle;

return null;
}
Expand All @@ -103,7 +123,7 @@ internal async Task UnloadAssetBundle(string name, bool unloadAllDependencies)
/// <summary>
/// Method attemps to get an asset from the asset database.
/// </summary>
/// <param name="bundleRequest"> Bundle to request </param>
/// <param name="bundleRequest"> Bundle to request </param>
private async Task<T> SimulateAssetBundle<T>(BundleRequest bundleRequest) where T : Object
{
Debug.Log($"AssetBundleLoader: Simulated | Requesting: {bundleRequest.AssetName} {bundleRequest.BundleName}".Colored(Colors.Aqua));
Expand All @@ -127,7 +147,6 @@ internal async Task UnloadAssetBundle(string name, bool unloadAllDependencies)
break;
}

//Not really needed but I want to keep the async pattern
await new WaitForEndOfFrame();
}

Expand All @@ -137,7 +156,6 @@ internal async Task UnloadAssetBundle(string name, bool unloadAllDependencies)

return assets.First();
}

#endif

/// <summary>
Expand All @@ -154,15 +172,15 @@ private async Task<AssetBundle> GetBundleFromWebOrCacheAsync(BundleRequest bundl
//cache bundles by using Unity Cloud Build manifest
uint buildNumber = 0;
buildNumber = System.Convert.ToUInt32(_assetService.CloudBuildManifest.buildNumber);
uwr = UnityWebRequestAssetBundle.GetAssetBundle(bundleRequest.AssetPath, buildNumber, 0);
uwr = UnityWebRequestAssetBundle.GetAssetBundle(bundleRequest.GetAssetPath(_assetService.Configuration), buildNumber, 0);
}
else if (_assetService.CloudBuildManifest == null || _assetService.AssetCacheState == AssetCacheState.NoCache)
{
if (_assetService.AssetCacheState == AssetCacheState.Cache)
Debug.Log("AssetBundleLoader: Caching is enabled, but Unity Cloud Build Manifest was missing, bundle was not cached.".Colored(Colors.Aqua));

//No caching, just get the bundle
uwr = UnityWebRequestAssetBundle.GetAssetBundle(bundleRequest.AssetPath);
uwr = UnityWebRequestAssetBundle.GetAssetBundle(bundleRequest.GetAssetPath(_assetService.Configuration));
}

//Wait until www is done.
Expand All @@ -187,7 +205,7 @@ private async Task<AssetBundle> GetBundleFromStreamingAssetsAsync(BundleRequest
{
Debug.Log($"AssetBundleLoader: Using StreamingAssets - Requesting: {bundleRequest.AssetCategory} {bundleRequest.BundleName}".Colored(Colors.Aqua));
var path = Path.Combine(Application.streamingAssetsPath, bundleRequest.AssetPathFromLocalStreamingAssets);

return await AssetBundle.LoadFromFileAsync(path);
}
}
Expand Down
55 changes: 46 additions & 9 deletions Services/AssetService/AssetService.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using UnityEngine;
using Zenject;
Expand Down Expand Up @@ -45,7 +46,7 @@ private async void LoadBuildManifestAsync()
}
}

private async Task<T> GetAndLoadAsset<T>(BundleRequest bundleRequest, bool forceLoadFromStreamingAssets = false) where T : UnityEngine.Object
private async Task<T> LoadAsset<T>(BundleRequest bundleRequest, bool forceLoadFromStreamingAssets = false) where T : UnityEngine.Object
{
return await _assetBundlebundleLoader.LoadAsset<T>(bundleRequest, forceLoadFromStreamingAssets);
}
Expand All @@ -57,12 +58,12 @@ private async void LoadBuildManifestAsync()
/// <param name="assetName">Bundle name and asset name are the same</param>
/// <param name="forceLoadFromStreamingAssets">Forces loading from StreamingAssets folder. Useful for when including assets with the build</param>
/// <returns> Observable </returns>
public async Task<T> GetAndLoadAsset<T>(AssetCategoryRoot assetCatRoot, string assetName, bool forceLoadFromStreamingAssets = false) where T : UnityEngine.Object
public async Task<T> LoadAsset<T>(AssetCategoryRoot assetCatRoot, string assetName, bool forceLoadFromStreamingAssets = false) where T : UnityEngine.Object
{
var bundleNeeded = new BundleRequest(assetCatRoot,
assetName, assetName, Configuration);
assetName, assetName);

return await GetAndLoadAsset<T>(bundleNeeded, forceLoadFromStreamingAssets);
return await LoadAsset<T>(bundleNeeded, forceLoadFromStreamingAssets);
}

/// <summary>
Expand All @@ -73,19 +74,55 @@ private async void LoadBuildManifestAsync()
/// <param name="assetName">Bundle name and asset name are the same</param>
/// <param name="forceLoadFromStreamingAssets">Forces loading from StreamingAssets folder. Useful for when including assets with the build</param>
/// <returns> Observable </returns>
public async Task<T> GetAndLoadAsset<T>(AssetCategoryRoot assetCatRoot, string bundleName, string assetName, bool forceLoadFromStreamingAssets = false) where T : UnityEngine.Object
public async Task<T> LoadAsset<T>(AssetCategoryRoot assetCatRoot, string bundleName, string assetName, bool forceLoadFromStreamingAssets = false) where T : UnityEngine.Object
{
var bundleNeeded = new BundleRequest(assetCatRoot,
bundleName, assetName, Configuration);
bundleName, assetName);

return await GetAndLoadAsset<T>(bundleNeeded);
return await LoadAsset<T>(bundleNeeded);
}

public async Task<UnityEngine.Object> GetScene(BundleRequest bundleRequest)
{
return await _assetBundlebundleLoader.LoadScene(bundleRequest);
}

/// <summary>
/// Utility method to request multiple assets.
/// </summary>
/// <param name="requests">Bundle requests</param>
/// <param name="progress">Reports loading progress percentage</param>
/// <param name="forceLoadFromStreamingAssets"></param>
/// <returns></returns>
public async Task<Dictionary<string, LoadedBundle>> LoadMultipleBundles(List<BundleRequest> requests, IProgress<int> progress, bool forceLoadFromStreamingAssets = false)
{
var total = requests.Count;
var bundles = new Dictionary<string, LoadedBundle>();
var percent = 0;

var process = await Task.Run(async () =>
{
var tempCount = 0;
foreach (var request in requests)
{
await Awaiters.WaitForUpdate; //Wait for main thread
var bundle = await _assetBundlebundleLoader.LoadBundle(request, forceLoadFromStreamingAssets);
bundles.Add(request.BundleName, bundle);
tempCount++;
percent = tempCount * 100 / total;
progress?.Report(percent);
Debug.Log($"LoadMultipleAssets: {tempCount} of {total} | {percent}%".Colored(Colors.Aquamarine));
}
return tempCount;
});

return bundles;
}

/// <summary>
/// Unloads asset and removes it from memory
/// </summary>
Expand All @@ -101,9 +138,9 @@ public async Task UnloadAsset(string name, bool unloadAllDependencies)
/// </summary>
/// <param name="name"> Asset name </param>
/// <returns> T </returns>
public T GetLoadedBundle<T>(string name) where T : UnityEngine.Object
public AssetBundle GetLoadedBundle(string name)
{
return _assetBundlebundleLoader.GetLoadedBundle<T>(name);
return _assetBundlebundleLoader.GetLoadedBundle(name);
}
}
}
73 changes: 34 additions & 39 deletions Services/AssetService/BundleRequest.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System;
using UnityEngine;

namespace Core.Services.Assets
Expand All @@ -20,45 +21,22 @@ public AssetOptions(AssetCacheState sta)
public class BundleRequest
{
//Directory where the bundle is located.
private AssetCategoryRoot _assetCategory;
private AssetServiceConfiguration _assetServiceConfiguration;
public AssetCategoryRoot AssetCategory => _assetCategory;
private string _bundleName;
public string BundleName => _bundleName;
public AssetCategoryRoot AssetCategory { get; }

//Manifest file associated to the bundle. This is needed in case the HASH number is requiered for caching the bundle
public string ManifestName => _bundleName + ".manifest";
public string BundleName { get; }

public string AssetName { get; }

private string assetName;
public string AssetName => assetName;
//Manifest file associated to the bundle. This is needed in case the HASH number is requiered for caching the bundle
[Obsolete("Deprecated. No longer needed.")]
public string ManifestName => BundleName + ".manifest";

[Obsolete("Deprecated. No longer needed.")]
public string ManifestAgeFile => Application.persistentDataPath + "/" + BundleName + "age.json";

[Obsolete("Deprecated. No longer needed.")]
public string CachedManifestFile => Application.persistentDataPath + "/" + ManifestName;

public string AssetPath
{
get
{
if (AssetCategory.Equals(AssetCategoryRoot.None))
return _assetServiceConfiguration.PlatformAssetBundleURL + BundleName + "?r=" + UnityEngine.Random.value * 9999999; //this random value prevents caching on the web server
else
return _assetServiceConfiguration.PlatformAssetBundleURL + AssetCategory.ToString().ToLower() + "/" + BundleName;
}
}

public string ManifestPath
{
get
{
Debug.Log(("AssetBundleLoader: Loading Manifest " + ManifestName).Colored(Colors.Aqua));

if (AssetCategory.Equals(AssetCategoryRoot.None))
return _assetServiceConfiguration.PlatformAssetBundleURL + ManifestName + "?r=" + UnityEngine.Random.value * 9999999; //this random value prevents caching on the web server;
else
return _assetServiceConfiguration.PlatformAssetBundleURL + AssetCategory.ToString().ToLower() + "/" + ManifestName;
}
}

public string AssetPathFromLocalStreamingAssets
{
get
Expand All @@ -81,12 +59,29 @@ public string AssetPathFromLocalStreamingAssetsManifest
}
}

public BundleRequest(AssetCategoryRoot cat, string bundle, string asset, AssetServiceConfiguration config)
public BundleRequest(AssetCategoryRoot cat, string bundle, string asset)
{
_assetCategory = cat;
_bundleName = bundle.ToLower();
assetName = asset.ToLower();
_assetServiceConfiguration = config;
AssetCategory = cat;
BundleName = bundle.ToLower();
AssetName = asset.ToLower();
}

public string GetAssetPath(AssetServiceConfiguration config)
{
if (AssetCategory.Equals(AssetCategoryRoot.None))
return config.PlatformAssetBundleURL + BundleName + "?r=" + UnityEngine.Random.value * 9999999; //this random value prevents caching on the web server
else
return config.PlatformAssetBundleURL + AssetCategory.ToString().ToLower() + "/" + BundleName;
}

public string GetManifestPath(AssetServiceConfiguration config)
{
Debug.Log(("AssetBundleLoader: Loading Manifest " + ManifestName).Colored(Colors.Aqua));

if (AssetCategory.Equals(AssetCategoryRoot.None))
return config.PlatformAssetBundleURL + ManifestName + "?r=" + UnityEngine.Random.value * 9999999; //this random value prevents caching on the web server;
else
return config.PlatformAssetBundleURL + AssetCategory.ToString().ToLower() + "/" + ManifestName;
}
}

Expand All @@ -100,7 +95,7 @@ public enum AssetCategoryRoot
Services,
Levels,
Scenes,
Screens,
UI,
Audio,
Prefabs,
Particles
Expand Down
36 changes: 24 additions & 12 deletions Services/AssetService/LoadedBundle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,17 @@ namespace Core.Services.Assets
/// </summary>
public class LoadedBundle
{
private AssetBundle _assetBundle;
private ManifestInfo _manifestInfo;
internal AssetBundle Bundle => _assetBundle;
private readonly GameObject _simulatedAsset;
internal AssetBundle Bundle { get; }

public LoadedBundle(AssetBundle asset)
{
_assetBundle = asset;
Bundle = asset;
}

public LoadedBundle(GameObject asset)
{
_simulatedAsset = asset;
}

/// <summary>
Expand All @@ -25,8 +29,8 @@ public LoadedBundle(AssetBundle asset)
/// <param name="unloadAll"></param>
public void Unload(bool unloadAll = false)
{
if (_assetBundle)
_assetBundle.Unload(unloadAll);
if (Bundle)
Bundle.Unload(unloadAll);
}

/// <summary>
Expand All @@ -36,17 +40,27 @@ public void Unload(bool unloadAll = false)
/// <returns></returns>
public async Task<T> LoadAssetAsync<T>(string name) where T : UnityEngine.Object
{

#if UNITY_EDITOR
if (EditorPreferences.EDITORPREF_SIMULATE_ASSET_BUNDLES)
{
Debug.Log(("LoadedBundle Simulated: Async loading asset: " + name).Colored(Colors.Yellow));
var comp = _simulatedAsset.GetComponent<T>();
await Task.Yield();
return comp;
}
#endif

Debug.Log(("LoadedBundle: Async loading asset: " + name).Colored(Colors.Yellow));

return await GetAssetCompomnentAsync<T>(_assetBundle.LoadAssetAsync(name));
return await GetAssetComponentAsync<T>(Bundle.LoadAssetAsync(name));
}

/// <summary>
/// Operation extracts an asset from the loaded bundle
/// </summary>
/// <param name="asyncOperation"> </param>
/// <returns></returns>
private async Task<T> GetAssetCompomnentAsync<T>(AssetBundleRequest asyncOperation) where T : UnityEngine.Object
private async Task<T> GetAssetComponentAsync<T>(AssetBundleRequest asyncOperation) where T : UnityEngine.Object
{
await asyncOperation;

Expand All @@ -55,9 +69,7 @@ public void Unload(bool unloadAll = false)

//Current use case returns the component, if this changes then deal with it downstream but for now this should be ok
var go = asyncOperation.asset as GameObject;
var comp = go.GetComponent<T>();

return comp;
return go.GetComponent<T>();
}
}
}

0 comments on commit a625f69

Please sign in to comment.