diff --git a/Runtime/Context/SceneContext.cs b/Runtime/Context/SceneContext.cs index 668bb1b..262a39f 100644 --- a/Runtime/Context/SceneContext.cs +++ b/Runtime/Context/SceneContext.cs @@ -20,7 +20,8 @@ public static bool TryGetSceneContext(Scene scene, out SceneContext sceneSceneCo } - public override Scene Scene => gameObject.scene; + private Scene scene; + public override Scene Scene => scene; private bool isReverseLoaded; public override bool IsReverseLoaded => isReverseLoaded; @@ -37,6 +38,7 @@ public static bool TryGetSceneContext(Scene scene, out SceneContext sceneSceneCo protected override async void Awake() { + scene = gameObject.scene; SceneContextMap[Scene] = this; base.Awake(); diff --git a/Runtime/Context/SceneContextLoader.cs b/Runtime/Context/SceneContextLoader.cs index e469c4c..c2e56d5 100644 --- a/Runtime/Context/SceneContextLoader.cs +++ b/Runtime/Context/SceneContextLoader.cs @@ -15,8 +15,8 @@ public class SceneContextLoader : MonoBehaviour, IAsyncDisposable { private SceneLoader SceneLoader { get; } = new(); private IContext Context { get; set; } - private List ChildSceneContexts { get; } = new(); - public IReadOnlyList ReadonlyChildSceneContexts => ChildSceneContexts; + private List ChildSceneContexts { get; } = new(); + public IReadOnlyList ReadonlyChildSceneContexts => ChildSceneContexts; public float Progression => SceneLoader.Progression; private TaskQueue TaskQueue { get; } = new(); private bool Disposed { get; set; } @@ -24,9 +24,9 @@ public class SceneContextLoader : MonoBehaviour, IAsyncDisposable private async void OnDestroy() { Disposed = true; - TaskQueue.Dispose(); - await UnloadAllScenesAsync(); + await UnloadAllScenesInternalAsync(); await SceneLoader.DisposeAsync(); + TaskQueue.Dispose(); } public void SetContext(IContext context) @@ -78,7 +78,7 @@ private async ValueTask LoadAsyncInternal(UnifiedScene unifiedScen return sceneContext; } - public async ValueTask UnloadAsync(IContext sceneContext) + public async ValueTask UnloadAsync(SceneContext sceneContext) { await TaskQueue.EnqueueAsync(async _ => { @@ -86,26 +86,37 @@ await TaskQueue.EnqueueAsync(async _ => }); } - private async ValueTask UnloadAsyncInternal(IContext sceneContext) + private async ValueTask UnloadAsyncInternal(SceneContext context) { - if (!ChildSceneContexts.Contains(sceneContext)) return; + if (!ChildSceneContexts.Contains(context)) return; foreach (var childSceneContext in ChildSceneContexts.ToArray()) await childSceneContext.SceneContextLoader.UnloadAllScenesAsync(); - ChildSceneContexts.Remove(sceneContext); + ChildSceneContexts.Remove(context); + + var scene = context.Scene; - sceneContext.Dispose(); + context.Dispose(); - await SceneLoader.UnloadAsync(sceneContext.Scene); + if (scene.IsValid()) + await SceneLoader.UnloadAsync(scene); } public async ValueTask UnloadAllScenesAsync() { if (Disposed) return; + await UnloadAllScenesInternalAsync(); + } + + private async ValueTask UnloadAllScenesInternalAsync() + { if (!ChildSceneContexts.Any()) return; - await Task.WhenAll(ChildSceneContexts.ToArray().Select(x => UnloadAsync(x).AsTask())); + await Task.WhenAll( + ChildSceneContexts + .ToArray() + .Select(x => UnloadAsync(x).AsTask())); } public void AddChild(SceneContext target) diff --git a/Writerside~/topics/context_space/close_context_space.md b/Writerside~/topics/context_space/close_context_space.md index 0a499d0..c40a6ff 100644 --- a/Writerside~/topics/context_space/close_context_space.md +++ b/Writerside~/topics/context_space/close_context_space.md @@ -33,10 +33,13 @@ var sceneContext = await sceneContextLoader.LoadAsync(firstScene, active: true); await sceneContextLoader.UnloadAsync(sceneContext); ``` -> これ以外の方法で直接シーンを閉じたときでも、シーンコンテクスト自体は自動的に閉じられますが、 -> Addressables でロードしたシーンをアンロードする場合は、そのハンドルが解放されなくなってしまうので、必ず、シーンローダーを使うか、コンテクストの破棄をするようにしてください。 -{style="warning"} +## SceneManager でアンロードする +通常の Unity が提供するシーンのアンロード機能を使って、シーンコンテクストを閉じても構いません。 + +```C# +SceneManager.UnloadSceneAsync(targetScene); +``` ## ゲームオブジェクトコンテクスト空間: Destroy() を呼び出す diff --git a/Writerside~/topics/context_space/close_context_space_en.md b/Writerside~/topics/context_space/close_context_space_en.md index 465c17b..54fbb92 100644 --- a/Writerside~/topics/context_space/close_context_space_en.md +++ b/Writerside~/topics/context_space/close_context_space_en.md @@ -1,9 +1,9 @@ # Closing the Context Space -There are several ways to close the context space. -Please choose the appropriate method according to your situation. +There are several ways to close a context space. +Choose the method that suits your situation. -## Calling Dispose +## Call Dispose By calling ```Dispose()``` on the context, you can close the context. Objects belonging to their own context space are registered in the DI container as ```IContext```. @@ -23,9 +23,9 @@ public async Task DisposeSceneContext() } ``` -## Scene Context Space: Calling SceneContextLoader.UnloadAsync() +## Scene Context Space: Call SceneContextLoader.UnloadAsync() -In the case of scene context, by keeping the SceneContext when loading the scene, you can close that context. +In the case of a scene context, you can close the context by holding the SceneContext when you load the scene. ```C# var sceneContext = await sceneContextLoader.LoadAsync(firstScene, active: true); @@ -33,14 +33,17 @@ var sceneContext = await sceneContextLoader.LoadAsync(firstScene, active: true); await sceneContextLoader.UnloadAsync(sceneContext); ``` -> Even when the scene is closed directly by methods other than this, the scene context itself is automatically closed, but -> when unloading a scene loaded with Addressables, the handle will not be released, so always use the scene loader or dispose of the context. -{style="warning"} +## Unload with SceneManager +You can also close the scene context using the scene unload function provided by Unity. -## GameObject Context Space: Calling Destroy() +```C# +SceneManager.UnloadSceneAsync(targetScene); +``` + +## GameObject Context Space: Call Destroy() -When an object with a GameObject context attached is destroyed, that context is closed. +When you destroy an object with a GameObjectContext attached, the context is closed. ```C# Destroy(gameObjectContext); diff --git a/Writerside~/topics/injection.md b/Writerside~/topics/injection.md index d5f5cb6..c169b22 100644 --- a/Writerside~/topics/injection.md +++ b/Writerside~/topics/injection.md @@ -59,29 +59,28 @@ public class SomeClass } ``` -## OnInjected() コールバック +## OnInjected コールバック - -```OnInjected()``` というメソッドは(引数なし・public) は、 +```OnInjected``` 属性を付けたメソッドは(引数なし・public) は、 インスタンスの注入が完了したタイミングで、自動的に呼び出されます。 -```Task```, ```ValueTask``` の戻り値を持つ非同期関数であっても問題ありません。 +```Task```, ```ValueTask```, ```UniTask``` の戻り値を持つ非同期関数であっても問題ありません。 特に、MonoBehaviour を継承したコンポーネントの場合、Awake や Start などのライフサイクルメソッドと、 インジェクションが行われるタイミングが前後する可能性があります。 -```OnInjected()``` は、インジェクションが完了した次のフレームで呼び出されるようになっているため、 -初期化順を安定化させる目的で使用できます。 +```OnInjected``` コールバックは、インジェクションが完了した次のフレームで呼び出されるようになっているため、 +コンポーネントの初期化を安定させる目的で使用します。 ```C# public class SomeClass { - // [Inject] 属性をつけたメソッドは、インスタンスの生成後、DIコンテナによって呼び出され SomeDependency を引数に渡します + // [Inject] 属性を付与したメソッドは、インスタンスの生成後、DIコンテナによって呼び出され SomeDependency を引数に渡します [Inject] public InjectMethod(SomeDependency dependency) { ... } - // インスタンスの注入が完了したタイミングで、自動的に呼び出されます + // [OnInjected] 属性を付与したメソッドはインスタンスの注入が完了したタイミングで、自動的に呼び出されます [OnInjected] public void OnInjected() { @@ -106,6 +105,11 @@ public class SomeClass } ``` +> 特定のコンテクストを要求するようなオブジェクトを別のコンテクストでも動作できるようにします。 +> インジェクションがスキップされた場合に既定値を設定し、オブジェクトの動作を保証し、デバッグ効率を上げられるでしょう。 +> また、コンテクストによって、動作が切り替わるようなオブジェクトの実現にも役立ちます。 +{style="note"} + ## コンポーネントへの動的インジェクション (DynamicInjectable) コンテクスト空間の生成後、通常の Instantiate を使うなど、Factory などを通さずにコンポーネントを生成したときは、 @@ -118,3 +122,4 @@ public class SomeClass ```Object.Instantiate``` で、インスタンスが生成されたタイミングでも、 同オブジェクトにアタッチされている IInjectableComponent を継承したコンポーネントへのインジェクションを行うことができます。 +DI コンテナを使いながらも、Unity の通常のコーディングフローに従った開発をサポートします。 diff --git a/Writerside~/topics/injection_en.md b/Writerside~/topics/injection_en.md index 0428d56..a05d41d 100644 --- a/Writerside~/topics/injection_en.md +++ b/Writerside~/topics/injection_en.md @@ -1,6 +1,6 @@ # Injection -This section explains where dependencies are injected. +This section explains where to inject dependencies. ## Constructor Injection @@ -9,7 +9,7 @@ Constructor injection is a method of injecting dependencies into the arguments o ```C# public class SomeClass { - // When SomeClass is instantiated, SomeDependency is automatically injected + // When SomeClass is created, SomeDependency is automatically injected public SomeClass(SomeDependency dependency) { ... @@ -24,7 +24,7 @@ Method injection is a method of injecting dependencies by calling a method with ```C# public class SomeClass { - // Methods with the [Inject] attribute are called by the DI container after the instance is created, passing SomeDependency as an argument + // A method with the [Inject] attribute is called by the DI container after the instance is created, passing SomeDependency as an argument [Inject] public InjectMethod(SomeDependency dependency) { @@ -35,12 +35,12 @@ public class SomeClass ## Field Injection -Field injection is a method of injecting dependencies into ```public``` fields with the ```[Inject]``` attribute. +Field injection is a method of injecting dependencies into a ```public``` field with the ```[Inject]``` attribute. ```C# public class SomeClass { - // Inject SomeDependency into a public field with the [Inject] attribute + // Injects SomeDependency into a public field with the [Inject] attribute [Inject] public SomeDependency dependency; ... } @@ -48,35 +48,34 @@ public class SomeClass ## Property Injection -Property injection is a method of injecting dependencies into properties with a ```public``` setter and the ```[Inject]``` attribute. +Property injection is a method of injecting dependencies into a property with a ```public``` setter and the ```[Inject]``` attribute. ```C# public class SomeClass { - // Inject SomeDependency into a property with a public setter and the [Inject] attribute + // Injects SomeDependency into a property with a public setter and the [Inject] attribute [Inject] public SomeDependency Dependency {get; set;} ... } ``` -## OnInjected() Callback +## OnInjected Callback +A method with the ```OnInjected``` attribute (no arguments, public) is automatically called when the instance's injection is complete. It's no problem even if it's an asynchronous function with return values of ```Task```, ```ValueTask```, or ```UniTask```. -The method ```OnInjected()``` (no arguments, public) is automatically called when the injection of an instance is completed. It can be an asynchronous function with a return value of ```Task``` or ```ValueTask```. - -In particular, for components that inherit from MonoBehaviour, the timing of lifecycle methods such as Awake or Start and the timing of injections may change. ```OnInjected()``` is designed to be called on the frame after injection is complete, so it can be used to stabilize the initialization order. +Especially for components inheriting MonoBehaviour, the timing of lifecycle methods like Awake or Start and the injection may overlap. The ```OnInjected``` callback is designed to be called in the frame after the injection is complete, used to stabilize the initialization of the component. ```C# public class SomeClass { - // Methods with the [Inject] attribute are called by the DI container after the instance is created, passing SomeDependency as an argument + // A method with the [Inject] attribute is called by the DI container after the instance is created, passing SomeDependency as an argument [Inject] public InjectMethod(SomeDependency dependency) { ... } - // This is automatically called when the injection of an instance is completed + // A method with the [OnInjected] attribute is automatically called when the instance's injection is complete [OnInjected] public void OnInjected() { @@ -87,12 +86,12 @@ public class SomeClass ## Optional Injection -By specifying the ```[Optional]``` attribute as an argument, if the dependency cannot be resolved, the injection can be skipped. The ```default``` value of the type is passed to skipped arguments. +By specifying the ```[Optional]``` attribute for an argument, you can skip the injection if the dependency cannot be resolved. The ```default``` value of the type is passed to skipped arguments. ```C# public class SomeClass { - // If the dependency on SomeDependency cannot be resolved, null is passed + // If SomeDependency cannot be resolved, null is passed public SomeClass([Optional] SomeDependency dependency) { ... @@ -100,10 +99,15 @@ public class SomeClass } ``` +> This allows an object that requires a specific context to function in another context. By setting default values when the injection is skipped, you can ensure the object's operation and improve debugging efficiency. It's also useful for realizing objects whose operation switches depending on the context. +{style="note"} + ## Dynamic Injection to Components (DynamicInjectable) -Normally, injections are not performed on components created without going through a Factory, such as using the regular Instantiate, after the context space is created. +After the creation of the context space, when a component is created without going through a Factory, such as using a regular Instantiate, the component is not normally injected. + +In such cases, you can perform the injection by creating instances using a Factory, but it's also a hassle to prepare a Factory every time. -In such cases, you can perform injections by using a Factory to create instances, but it can be a hassle to prepare a Factory every time. +In such cases, if you attach the ```DynamicInjectable``` component to the GameObject in advance, you can inject into the components that inherit IInjectableComponent attached to the same object, even when instances are created with ```Object.Instantiate```. -In such cases, you can attach the ```DynamicInjectable``` component to the GameObject in advance, and perform injections to components that inherit IInjectableComponent attached to the same object, even at the timing of instance creation with ```Object.Instantiate```. \ No newline at end of file +This supports development following the regular coding flow of Unity while using the DI container. \ No newline at end of file