I am using addressables to instantiate prefabs. When a requester call my static class and method SpawnAddressable.Spawn(AssetReference), It checks to see if that asset has been loaded. If not, it will begin loading the asset, and queue the instantiate request for when it is done loading.
If the asset has been loaded, Spawn() calls AssetReference.InstanciateAsync() and returns the AsyncOperationHandle to the caller, who can manipulate the object using AsyncOperationHandle.Complete+=(operation) => { };
The issue with this is that if a requester calls, they need back the handle to the AssetReference.InstanciateAsync() task to instantiate the object, even if the reference is not yet loaded. Ideally, there would be a way to pause the instantiate task once it is created, and resume it once the asset fully loads.
This is the method I am calling when I need to queue an instantiate request
private static AsyncOperationHandle EnqueueSpawnForAfterInitialization(AssetReference assetReference)
{
if (QueuedSpawnRequests.ContainsKey(assetReference) == false)
QueuedSpawnRequests[assetReference] = new Queue<AsyncOperationHandle>();
AsyncOperationHandle handle = assetReference.InstantiateAsync();
QueuedSpawnRequests[assetReference].Enqueue(handle);
//handle.Task.Wait(); //freezes main thread, need something that just pauses...
//handle.Task.Delay(1000); //Delay is a static method, cannot call from instance
//Task.Delay(1000,handle.Task); //Handle.Task is not a cancellation token.
//how do prevent task from completing until later?
return handle;
}
Do I have to refactor how I pass back and manipulate the AsyncOperationHandle, or is there some way to do this?
I don’t know much about addressables but, your post is pretty confusing having looked a few things up
but InstantiateAsync loads and instantiates the asset. If you want to load and instantiate separately, then you would loadassetasync and then just instantiate it. There is no asynchronous instantiate. InstanciateAsync asynchronously loads and then synchronously instantiates. It even appears like InstanciateAsync might reload the asset every time. Not sure though.
You don’t need to return the async operation handle to attach a callback to the handle. You can just pass the callback into your function that sets up the async operation handle, and you attach it in there.
But why is the requester trying to instantiate it, when InstantiateAsync already instantiates it?
public class InstantiateAddressable : MonoBehaviour
{
public AssetReference GOAssetRef;
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
InstantiateGOAssetRef(OnInstantiated);
}
}
public void InstantiateGOAssetRef(Action<AsyncOperationHandle<GameObject>> instantiationCallback)
{
Debug.Log("Async Loading and Instantiating...");
AsyncOperationHandle<GameObject> handle;
handle = GOAssetRef.InstantiateAsync();
handle.Completed += instantiationCallback;
Debug.Log("Setup Callback");
}
private void OnInstantiated(AsyncOperationHandle<GameObject> instantiationHandle)
{
Debug.Log($"instantiation callback for GameObject {instantiationHandle.Result.name}");
}
}
I think Hikiko has answered with the completed callback code. That seems the best way to modify the instance being created.
The only change I would make it to check if it is already completed:
handle = GOAssetRef.InstantiateAsync();
if (handle.isDone)
InstantiationCallback(handle);
else
handle.Completed += instantiationCallback;
The reason for this, is that if the operation is already complete, then the callback will happen next frame.
When instantiating a gameObject, you can modify it right after the instantiate and it will take effect well before it is rendered or used by other systems (as Hikiko said the instantiate itself (copy from the shared Prefab memory into the Scene) is synchronous.
You also cannot pause any loads with Unity. Particularly with Tasks (they likely do very little of what you expect). Tasks in Unity run on the main thread, also Asset loading happens in the engine C++ on a different thread. Freezing the main thread through tasks will lock up your game. The Addressables task is not a task that is running the loading, this is simply a wait that gets flagged as done on loading complete. You can use it for waiting on using the C# tasks awaits (again runs on main thread only, by makes coding easier at the expense of GC)