I’ve read the document many times here: Assets, Resources and AssetBundles - Unity Learn
but I still have some questions:
For most projects, this behavior is undesirable. Most projects should use AssetBundle.Unload(true) and adopt a method to ensure that Objects are not duplicated. Two common methods are:
1.Having well-defined points during the application’s lifetime at which transient AssetBundles are unloaded, such as between levels or during a loading screen. This is the simpler and most common option.
(so I should have a Dictionary that maps the resouce names to the loaded AssetBundles, because when I want to load a new AssetBundle, I must know if the new AssetBundle’s dependencies are already loaded for avoiding loading dependencies duplicately, is this correct? Does addressable solve this automatically?)
2.Maintaining reference-counts for individual Objects and unload AssetBundles only when all of their constituent Objects are unused. This permits an application to unload and reload individual Objects without duplicating memory.
(This really confuse me…
1.How can I “Maintaining reference-counts for individual Objects”? For example, I want to load a UI prefab that have many textures, should I create a new MonoBehaviour that tell my ResourceManager “i am referecing this texture” when Start called and tell the ResourceManager “i am not referecing this texture” when OnDestroy called for every RawImage? what about another types of asset? What if other programmer forgot to add this MonoBehaviour to the RawImages? that’s wired and I think it’s not acceptable and easy to make mistake.)
If an application must use AssetBundle.Unload(false), then individual Objects can only be unloaded in two ways:
1.Eliminate all references to an unwanted Object, both in the scene and in code. After this is done, call Resources.UnloadUnusedAssets.
(this is the pattern I use in my jam, the critical class is WeakReference, I’ve tested it simply, you can see the code below, does it have any problem?)
2.Load a scene non-additively. This will destroy all Objects in the current scene and invoke Resources.UnloadUnusedAssets automatically.
(Do these “Objects” only include the GameObjects in the scene view? if not, how shuold I do if I want to keep common assets I will use in any scene?)
//just a jam, didn't use it in my company's project
//ResourceManager.cs
public class ResourceManager : Singleton<ResourceManager>
{
Dictionary<string, WeakReference<Object>> loadedResources = new Dictionary<string, WeakReference<Object>>();
float tick = 0f;
public void Update() //called elsewhere
{
tick += Time.deltaTime;
if(tick >= 5f)
{
Resources.UnloadUnusedAssets();
tick = 0f;
}
}
public Task<T> LoadAsync<T>(string name) where T: Object
{
TaskCompletionSource<T> tcs = new TaskCompletionSource<T>();
Object existResource = null;
//check if the resouce is already loaded
if(loadedResources.ContainsKey(name) && loadedResources[name].TryGetTarget(out existResource))
{
tcs.SetResult(existResource as T);
}
else
{
var resourceRequest = Resources.LoadAsync<T>(name);//Can be replaced with AssetBundle
resourceRequest.completed += (ao) => {
if (loadedResources.ContainsKey(name))
{
var wr = loadedResources[name];
wr.SetTarget(resourceRequest.asset);
}
else
{
var wr = new WeakReference<Object>(resourceRequest.asset);
loadedResources.Add(name, wr);
}
tcs.SetResult(resourceRequest.asset as T);
};
}
return tcs.Task;
}
}
//MonsterManager.cs
public class MonsterManager : Singleton<ResourceManager>
{
//maintain the references of monster models that current level may use
//because I don't want the Resources.UnloadUnusedAssets to unload these monsters
Dictionary<string, GameObject> templates = new Dictionary<string, GameObject>();
public async void OnNewLevelLoaded()
{
//eliminate all reference, Resources.UnloadUnusedAssets can unload these monster templates
templates.Clear();
foreach(var monsterName in monsterNamesFromSomewhere)
{
GameObject monsterTemplate = await ResourceManager.Instance.LoadAsync<GameObject>("monster");
templates.Add(monsterName, monsterTemplate);
}
}
public GameObject CreateNew(string monsterName)
{
GameObject go;
if(templates.TryGetValue(monsterName, out go))
{
return GameObject.Instantiate(go);
}
return null;
}
}
again, does this have any problem with asset management(never mind async/await and the way(Resources.LoadAsync) I load assets here)?
finally, is this document already obsolete? should I use Addressables instead of AssetBundle currently?
thank for answering, and please use simpler words, my english sucks