Loading
One of the key features of AlchemyBow.Core is its ability to provide an easy, organized, and efficient way to handle loading processes, as well as designated places for initialization and deinitialization.
ICoreLoadable
ICoreLoadable is an interface for objects that require a time-consuming loading process. It defines a single method, Load(OperationHandle)
, which takes an OperationHandle as a parameter. The loading process is considered complete once you call OperationHandle.MarkDone()
.
using AlchemyBow.Core;
using System.Collections;
using UnityEngine;
public class SampleCoreLoadable : MonoBehaviour, ICoreLoadable
{
void ICoreLoadable.Load(OperationHandle handle)
{
StartCoroutine(CreateLoadCoroutine(handle));
}
private IEnumerator CreateLoadCoroutine(OperationHandle handle)
{
// Simulate a time-consuming operation (e.g., loading resources, initializing data)
yield return new WaitForSeconds(1);
handle.MarkDone();
}
}
Note
It often makes sense to use explicit interface implementation to hide the Load()
method from the public interface of your class as it is only used by the framework.
Getting Loadables
To specify what to load and in what order, override the GetLoadables()
method in either CoreController or CoreProjectContext, depending on the scope to which the loadable belongs.
Note
Loadables returned by the CoreController are loaded each time you change the scene, but loadables returned by the CoreProjectContext are only loaded once.
There are several ways to get your loadables. One efficient method is to use dependency injection directly, as shown in the example below:
using AlchemyBow.Core;
using AlchemyBow.Core.IoC;
using System.Collections.Generic;
[InjectionTarget]
public class MyCoreProjectContext : CoreProjectContext
{
[Inject]
private readonly SampleCoreLoadable sampleCoreLoadable;
protected override IEnumerable<ICoreLoadable> GetLoadables()
{
yield return sampleCoreLoadable;
yield break;
}
}
This solution is very convenient as it allows you explicitly define the loadables and their order.
Tip
As your project grows, you may want to consider delegating the task of managing loadables to other objects to prevent controllers from becoming overly large and tightly coupled with specific loadables, which can reduce maintainability.
Another interesting approach is to leverage the dynamic collection binding feature. You can add your loadables to a collection using installers and then use that collection in the GetLoadables()
method. This approach can be even more effective if combined with a sorted or prioritized collection, still allowing you to control the loading order.
Loading Progress
You can track the progress of the loading process and react to it (e.g., by updating your loading screen). To do this, override the CoreController.OnLoadablesProgressed(LoadablesProgress)
method:
using AlchemyBow.Core;
using AlchemyBow.Core.IoC;
using System.Collections.Generic;
[InjectionTarget]
public class MyCoreController : CoreController<CoreProjectContext>
{
protected override IEnumerable<ICoreLoadable> GetLoadables()
{
// Return your loadables here
}
protected override void OnLoadablesProgressed(LoadablesProgress progress)
{
UnityEngine.Debug.Log($"{progress.loadableIndex}/{progress.numberOfLoadables}---{progress.loadable}:{progress.loadableCompleted}");
}
}
Note
This method is called twice for each loadable — once when the loading starts, and again when it's completed.
Note
If project context loadables haven’t been loaded previously, they will also be included in the progress.
ICoreLoadingCallbacksHandler
The ICoreLoadingCallbacksHandler provides a mechanism for notifying objects when the CoreController has finished loading or started unloading.
using AlchemyBow.Core;
public class SampleCoreLoadingCallbacksHandler : ICoreLoadingCallbacksHandler
{
public void OnCoreLoadingFinished()
{
// For example:
// - Load some data
// - Subscribe to events
}
public void OnCoreSceneChangeStarted()
{
// For example:
// - Unsubscribe from events
}
}
To receive the callbacks, add subscribers to the dynamic List<ICoreLoadingCallbacksHandler>
binding:
using AlchemyBow.Core;
using AlchemyBow.Core.Extras.FluentBindings;
using AlchemyBow.Core.IoC;
using System.Collections.Generic;
public class SampleMonoInstaller : MonoInstaller
{
public override void InstallBindings(IBindOnlyContainer container)
{
var sample = new SampleCoreLoadingCallbacksHandler();
container.Bind(sample);
// Add to the dynamic collection binding
container.AddToDynamicCollectionBinding<List<ICoreLoadingCallbacksHandler>, ICoreLoadingCallbacksHandler>(sample);
// Or use the shortcut:
// container.AddToDynamicListBinding<ICoreLoadingCallbacksHandler>(sample);
// Alternatively, with fluent syntax:
// container.StartFluentBinding(new SampleCoreLoadingCallbacksHandler())
// .BindToSelf()
// .AddToCoreLoadingCallbacksHandlers();
}
}
Warning
ICoreLoadingCallbacksHandler cannot be used in the project context.
CoreBehaviour
CoreBehaviour serves as a base class for plain C# objects that can be enabled or disabled based on the CoreBehaviourEnablingStrategy you select. It's built on top of core loading callbacks allowing easy initialization or cleanup.
using AlchemyBow.Core.Extras.Behaviours;
using AlchemyBow.Core.IoC;
using UnityEngine;
public class MyCoreBehaviour : CoreBehaviour
{
// The strategy is set via the constructor
public MyCoreBehaviour()
: base(CoreBehaviourEnablingStrategy.SimpleAutoEnableDisable)
{
}
// You can safely override any of these methods:
protected override void OnCoreLoadingFinished()
{
// Initialization logic
}
protected override void OnCoreSceneChangeStarted()
{
// Cleanup logic
}
protected override void OnEnabled()
{
// Logic executed when enabled
}
protected override void OnDisabled()
{
// Logic executed when disabled
}
}
Since CoreBehaviour implements ICoreLoadingCallbacksHandler, it also must be added to the dynamic list binding of ICoreLoadingCallbacksHandler to receive callbacks.
using AlchemyBow.Core;
using AlchemyBow.Core.Extras.FluentBindings;
using AlchemyBow.Core.IoC;
using System.Collections.Generic;
public class MyCoreBehaviourMonoInstaller : MonoInstaller
{
public override void InstallBindings(IBindOnlyContainer container)
{
container.StartFluentBinding(new MyCoreBehaviour())
.BindToSelf()
.AddToCoreLoadingCallbacksHandlers();
}
}