Skip to content

Commit

Permalink
Merge pull request #116 from hadashiA/ku/scoped-hierarchy
Browse files Browse the repository at this point in the history
Control the find scope of RegisterComponentInHierarchy.
  • Loading branch information
hadashiA authored Feb 10, 2021
2 parents 47c5946 + 4d583d3 commit f2cb6a3
Show file tree
Hide file tree
Showing 9 changed files with 309 additions and 125 deletions.
9 changes: 7 additions & 2 deletions VContainer/Assets/VContainer/Runtime/Interfaces.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

98 changes: 81 additions & 17 deletions VContainer/Assets/VContainer/Runtime/Unity/ComponentRegistration.cs
Original file line number Diff line number Diff line change
@@ -1,33 +1,40 @@
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;

namespace VContainer.Unity
{
public sealed class ComponentDestination
sealed class ComponentDestination
{
public readonly bool Find;
public readonly Component Prefab;
public readonly string NewGameObjectName;
public Scene Scene;

readonly Transform parent;
readonly Func<Transform> parentFinder;

public ComponentDestination(
Component prefab,
bool find,
Scene scene,
Transform parent,
Func<Transform> parentFinder,
Component prefab,
string newGameObjectName = null)
{
Find = find;
Prefab = prefab;
NewGameObjectName = newGameObjectName;
Scene = scene;
this.parent = parent;
this.parentFinder = parentFinder;
}

public Transform GetParent() => parent != null ? parent : parentFinder?.Invoke();
}

public sealed class ComponentRegistration : IRegistration
sealed class ComponentRegistration : IRegistration
{
public Type ImplementationType { get; }
public Lifetime Lifetime { get; }
Expand Down Expand Up @@ -55,29 +62,86 @@ internal ComponentRegistration(

public object SpawnInstance(IObjectResolver resolver)
{
Component component;
var parent = destination.GetParent();
if (destination.Find)
{
return FindComponent(resolver);
}
if (destination.Prefab != null)
{
if (destination.Prefab.gameObject.activeSelf)
return InstantiatePrefab(resolver);
}
return InstantiateNewGameObject(resolver);
}

Component FindComponent(IObjectResolver resolver)
{
Component component = null;
var parent = destination.GetParent();
if (parent != null)
{
component = parent.GetComponentInChildren(ImplementationType);
if (component == null)
{
destination.Prefab.gameObject.SetActive(false);
throw new VContainerException(ImplementationType, $"Component {ImplementationType} is not in the parent {parent.name}");
}
component = UnityEngine.Object.Instantiate(destination.Prefab, parent);
}
else
else if (destination.Scene.IsValid())
{
var name = string.IsNullOrEmpty(destination.NewGameObjectName)
? ImplementationType.Name
: destination.NewGameObjectName;
var gameObject = new GameObject(name);
gameObject.SetActive(false);
if (parent != null)
var gameObjectBuffer = UnityEngineObjectListBuffer<GameObject>.Get();
destination.Scene.GetRootGameObjects(gameObjectBuffer);
foreach (var gameObject in gameObjectBuffer)
{
gameObject.transform.SetParent(parent);
component = gameObject.GetComponentInChildren(ImplementationType, true);
if (component != null) break;
}
component = gameObject.AddComponent(ImplementationType);
if (component == null)
{
throw new VContainerException(ImplementationType, $"Component {ImplementationType} is not in this scene {destination.Scene.path}");
}
}
else
{
throw new VContainerException(ImplementationType, "Invalid Component find target");
}

if (component is MonoBehaviour monoBehaviour)
{
injector.Inject(monoBehaviour, resolver, Parameters);
}
return component;
}

Component InstantiatePrefab(IObjectResolver resolver)
{
var parent = destination.GetParent();
if (destination.Prefab.gameObject.activeSelf)
{
destination.Prefab.gameObject.SetActive(false);
}

var component = parent != null
? UnityEngine.Object.Instantiate(destination.Prefab, parent)
: UnityEngine.Object.Instantiate(destination.Prefab);

injector.Inject(component, resolver, Parameters);
component.gameObject.SetActive(true);
return component;
}

Component InstantiateNewGameObject(IObjectResolver resolver)
{
var parent = destination.GetParent();
var name = string.IsNullOrEmpty(destination.NewGameObjectName)
? ImplementationType.Name
: destination.NewGameObjectName;
var gameObject = new GameObject(name);
gameObject.SetActive(false);
if (parent != null)
{
gameObject.transform.SetParent(parent);
}
var component = gameObject.AddComponent(ImplementationType);

injector.Inject(component, resolver, Parameters);
component.gameObject.SetActive(true);
return component;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,52 +1,65 @@
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using VContainer.Internal;

namespace VContainer.Unity
{
public sealed class ComponentRegistrationBuilder : RegistrationBuilder
{
readonly bool find;
readonly Scene scene;
readonly Component prefab;

Transform parent;
Func<Transform> parentFinder;
string gameObjectName;
Component prefab;

ComponentRegistrationBuilder(
internal ComponentRegistrationBuilder(
Component prefab,
Type implementationType,
Lifetime lifetime,
List<Type> interfaceTypes = null)
: base(implementationType, lifetime, interfaceTypes)
: this(false, default, implementationType, lifetime, interfaceTypes)
{
InterfaceTypes = InterfaceTypes ?? new List<Type>();
InterfaceTypes.Add(typeof(MonoBehaviour));
InterfaceTypes.Add(ImplementationType);
this.prefab = prefab;
}

internal ComponentRegistrationBuilder(
Component prefab,
string gameObjectName,
Type implementationType,
Lifetime lifetime,
List<Type> interfaceTypes = null)
: this(implementationType, lifetime, interfaceTypes)
: this(false, default, implementationType, lifetime, interfaceTypes)
{
this.prefab = prefab;
this.gameObjectName = gameObjectName;
}

internal ComponentRegistrationBuilder(
string gameObjectName,
Scene scene,
Type implementationType,
List<Type> interfaceTypes = null)
: this(true, scene, implementationType, Lifetime.Scoped, interfaceTypes)
{
}

ComponentRegistrationBuilder(
bool find,
Scene scene,
Type implementationType,
Lifetime lifetime,
List<Type> interfaceTypes = null)
: this(implementationType, lifetime, interfaceTypes)
: base(implementationType, lifetime, interfaceTypes)
{
this.gameObjectName = gameObjectName;
this.find = find;
this.scene = scene;
}

public override IRegistration Build()
{
var injector = InjectorCache.GetOrBuild(ImplementationType);
var destination = new ComponentDestination(prefab, parent, parentFinder, gameObjectName);
var destination = new ComponentDestination(find, scene, parent, parentFinder, prefab, gameObjectName);

return new ComponentRegistration(
ImplementationType,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,25 +29,32 @@ public void OnException(Action<Exception> exceptionHandler)
public readonly struct ComponentsBuilder
{
readonly IContainerBuilder containerBuilder;
readonly Transform parentTransform;

public ComponentsBuilder(IContainerBuilder containerBuilder)
public ComponentsBuilder(IContainerBuilder containerBuilder, Transform parentTransform = null)
{
this.containerBuilder = containerBuilder;
this.parentTransform = parentTransform;
}

public RegistrationBuilder AddInstance(MonoBehaviour component)
=> containerBuilder.RegisterComponent(component);
public RegistrationBuilder AddInstance(Component component)
{
return containerBuilder.RegisterComponent(component);
}

public RegistrationBuilder AddInHierarchy<T>() where T : Component
=> containerBuilder.RegisterComponentInHierarchy<T>();
public ComponentRegistrationBuilder AddInHierarchy<T>()
=> containerBuilder.RegisterComponentInHierarchy<T>()
.UnderTransform(parentTransform);

public ComponentRegistrationBuilder AddOnNewGameObject<T>(Lifetime lifetime, string newGameObjectName = null)
where T : Component
=> containerBuilder.RegisterComponentOnNewGameObject<T>(lifetime, newGameObjectName);
=> containerBuilder.RegisterComponentOnNewGameObject<T>(lifetime, newGameObjectName)
.UnderTransform(parentTransform);

public ComponentRegistrationBuilder AddInNewPrefab<T>(T prefab, Lifetime lifetime)
where T : Component
=> containerBuilder.RegisterComponentInNewPrefab(prefab, lifetime);
=> containerBuilder.RegisterComponentInNewPrefab(prefab, lifetime)
.UnderTransform(parentTransform);
}

public static class ContainerBuilderUnityExtensions
Expand All @@ -61,16 +68,18 @@ public static void UseEntryPoints(
configuration(entryPoints);
}

public static void RegisterEntryPointExceptionHandler(
this IContainerBuilder builder,
Action<Exception> exceptionHandler)
public static void UseComponents(this IContainerBuilder builder, Action<ComponentsBuilder> configuration)
{
builder.RegisterInstance(new EntryPointExceptionHandler(exceptionHandler));
var components = new ComponentsBuilder(builder);
configuration(components);
}

public static void UseComponents(this IContainerBuilder builder, Action<ComponentsBuilder> configuration)
public static void UseComponents(
this IContainerBuilder builder,
Transform root,
Action<ComponentsBuilder> configuration)
{
var components = new ComponentsBuilder(builder);
var components = new ComponentsBuilder(builder, root);
configuration(components);
}

Expand All @@ -84,6 +93,13 @@ public static RegistrationBuilder RegisterEntryPoint<T>(this IContainerBuilder b
return registrationBuilder.AsImplementedInterfaces();
}

public static void RegisterEntryPointExceptionHandler(
this IContainerBuilder builder,
Action<Exception> exceptionHandler)
{
builder.RegisterInstance(new EntryPointExceptionHandler(exceptionHandler));
}

public static RegistrationBuilder RegisterComponent(this IContainerBuilder builder, MonoBehaviour component)
{
var registrationBuilder = builder.RegisterInstance(component);
Expand All @@ -106,31 +122,13 @@ public static RegistrationBuilder RegisterComponent<TInterface>(this IContainerB
return registrationBuilder;
}

public static RegistrationBuilder RegisterComponentInHierarchy<T>(this IContainerBuilder builder)
public static ComponentRegistrationBuilder RegisterComponentInHierarchy<T>(this IContainerBuilder builder)
{
var lifetimeScope = (LifetimeScope)builder.ApplicationOrigin;
var scene = lifetimeScope.gameObject.scene;
var component = default(T);

var gameObjectBuffer = UnityEngineObjectListBuffer<GameObject>.Get();
scene.GetRootGameObjects(gameObjectBuffer);
foreach (var gameObject in gameObjectBuffer)
{
component = gameObject.GetComponentInChildren<T>(true);
if (component != null) break;
}

if (component == null)
{
throw new VContainerException(typeof(T), $"Component {typeof(T)} is not in this scene {scene.path}");
}

var registrationBuilder = builder.RegisterInstance(component).As(typeof(T));
if (component is MonoBehaviour monoBehaviour)
{
registrationBuilder.As<MonoBehaviour>();
builder.RegisterBuildCallback(container => container.Inject(monoBehaviour));
}
var registrationBuilder = new ComponentRegistrationBuilder(scene, typeof(T));
builder.Register(registrationBuilder);
return registrationBuilder;
}

Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions VContainer/Assets/VContainer/Runtime/Unity/LifetimeScope.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ static LifetimeScope Create(IInstaller installer = null)
return newScope;
}

public static LifetimeScope Create(Action<IContainerBuilder> installation)
=> Create(new ActionInstaller(installation));
public static LifetimeScope Create(Action<IContainerBuilder> configuration)
=> Create(new ActionInstaller(configuration));

public static ParentOverrideScope EnqueueParent(LifetimeScope parent)
=> new ParentOverrideScope(parent);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using System;
using System.Collections.Generic;

namespace VContainer.Internal
namespace VContainer.Unity
{
static class UnityEngineObjectListBuffer<T> where T : UnityEngine.Object
{
Expand Down
Loading

1 comment on commit f2cb6a3

@vercel
Copy link

@vercel vercel bot commented on f2cb6a3 Feb 10, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.