292 lines
9.4 KiB
C#

using System;
using UnityEngine;
using VContainer.Internal;
namespace VContainer.Unity
{
public static class ObjectResolverUnityExtensions
{
public readonly struct PrefabDirtyScope : IDisposable
{
readonly GameObject _prefab;
readonly bool _madeDirty;
public PrefabDirtyScope(GameObject prefab)
{
_prefab = prefab;
#if UNITY_EDITOR && UNITY_2020_1_OR_NEWER
_madeDirty = prefab.activeSelf && !UnityEditor.EditorUtility.IsDirty(_prefab);
#else
_madeDirty = false;
#endif
}
public void Dispose()
{
#if UNITY_EDITOR && UNITY_2020_1_OR_NEWER
if (_madeDirty)
{
UnityEditor.EditorUtility.ClearDirty(_prefab);
}
#endif
}
}
public static void InjectGameObject(this IObjectResolver resolver, GameObject gameObject)
{
void InjectGameObjectRecursive(GameObject current)
{
if (current == null) return;
using (ListPool<MonoBehaviour>.Get(out var buffer))
{
buffer.Clear();
current.GetComponents(buffer);
foreach (var monoBehaviour in buffer)
{
if (monoBehaviour != null)
{ // Can be null if the MonoBehaviour's type wasn't found (e.g. if it was stripped)
resolver.Inject(monoBehaviour);
}
}
}
var transform = current.transform;
for (var i = 0; i < transform.childCount; i++)
{
var child = transform.GetChild(i);
InjectGameObjectRecursive(child.gameObject);
}
}
InjectGameObjectRecursive(gameObject);
}
public static T Instantiate<T>(this IObjectResolver resolver, T prefab)
where T : Component
{
return resolver.Instantiate(prefab, prefab.transform.position, prefab.transform.rotation);
}
public static T Instantiate<T>(this IObjectResolver resolver, T prefab, Transform parent, bool worldPositionStays = false)
where T : Component
{
var wasActive = prefab.gameObject.activeSelf;
using (new PrefabDirtyScope(prefab.gameObject))
{
prefab.gameObject.SetActive(false);
var instance = UnityEngine.Object.Instantiate(prefab, parent, worldPositionStays);
SetName(instance, prefab);
try
{
resolver.InjectGameObject(instance.gameObject);
}
finally
{
prefab.gameObject.SetActive(wasActive);
instance.gameObject.SetActive(wasActive);
}
return instance;
}
}
public static T Instantiate<T>(
this IObjectResolver resolver,
T prefab,
Vector3 position,
Quaternion rotation)
where T : Component
{
if (resolver.ApplicationOrigin is LifetimeScope scope)
{
return scope.Instantiate(prefab, position, rotation);
}
return resolver.Instantiate(prefab, position, rotation, null);
}
public static T Instantiate<T>(
this IObjectResolver resolver,
T prefab,
Vector3 position,
Quaternion rotation,
Transform parent)
where T : Component
{
var wasActive = prefab.gameObject.activeSelf;
using (new PrefabDirtyScope(prefab.gameObject))
{
prefab.gameObject.SetActive(false);
var instance = UnityEngine.Object.Instantiate(prefab, position, rotation, parent);
SetName(instance, prefab);
try
{
resolver.InjectGameObject(instance.gameObject);
}
finally
{
prefab.gameObject.SetActive(wasActive);
instance.gameObject.SetActive(wasActive);
}
return instance;
}
}
static T Instantiate<T>(this LifetimeScope scope, T prefab, Vector3 position, Quaternion rotation)
where T : Component
{
var wasActive = prefab.gameObject.activeSelf;
using (new PrefabDirtyScope(prefab.gameObject))
{
prefab.gameObject.SetActive(false);
T instance;
if (scope.IsRoot)
{
instance = UnityEngine.Object.Instantiate(prefab, position, rotation);
UnityEngine.Object.DontDestroyOnLoad(instance);
}
else
{
// Into the same scene as LifetimeScope
instance = UnityEngine.Object.Instantiate(prefab, position, rotation, scope.transform);
instance.transform.SetParent(null);
}
SetName(instance, prefab);
try
{
scope.Container.InjectGameObject(instance.gameObject);
}
finally
{
prefab.gameObject.SetActive(wasActive);
instance.gameObject.SetActive(wasActive);
}
return instance;
}
}
static GameObject Instantiate(this LifetimeScope scope, GameObject prefab, Vector3 position, Quaternion rotation)
{
var wasActive = prefab.activeSelf;
using (new PrefabDirtyScope(prefab))
{
prefab.SetActive(false);
GameObject instance;
if (scope.IsRoot)
{
instance = UnityEngine.Object.Instantiate(prefab, position, rotation);
UnityEngine.Object.DontDestroyOnLoad(instance);
}
else
{
// Into the same scene as LifetimeScope
instance = UnityEngine.Object.Instantiate(prefab, position, rotation, scope.transform);
instance.transform.SetParent(null);
}
SetName(instance, prefab);
try
{
scope.Container.InjectGameObject(instance);
}
finally
{
prefab.SetActive(wasActive);
instance.SetActive(wasActive);
}
return instance;
}
}
public static GameObject Instantiate(this IObjectResolver resolver, GameObject prefab)
{
return resolver.Instantiate(prefab, prefab.transform.position, prefab.transform.rotation);
}
public static GameObject Instantiate(this IObjectResolver resolver, GameObject prefab, Transform parent, bool worldPositionStays = false)
{
var wasActive = prefab.activeSelf;
using (new PrefabDirtyScope(prefab))
{
prefab.SetActive(false);
GameObject instance = null;
try
{
instance = UnityEngine.Object.Instantiate(prefab, parent, worldPositionStays);
SetName(instance, prefab);
resolver.InjectGameObject(instance);
}
finally
{
prefab.SetActive(wasActive);
instance?.SetActive(wasActive);
}
return instance;
}
}
public static GameObject Instantiate(
this IObjectResolver resolver,
GameObject prefab,
Vector3 position,
Quaternion rotation)
{
if (resolver.ApplicationOrigin is LifetimeScope scope)
{
return scope.Instantiate(prefab, position, rotation);
}
return resolver.Instantiate(prefab, position, rotation, null);
}
public static GameObject Instantiate(
this IObjectResolver resolver,
GameObject prefab,
Vector3 position,
Quaternion rotation,
Transform parent)
{
var wasActive = prefab.activeSelf;
using (new PrefabDirtyScope(prefab))
{
prefab.SetActive(false);
var instance = UnityEngine.Object.Instantiate(prefab, position, rotation, parent);
SetName(instance, prefab);
try
{
resolver.InjectGameObject(instance);
}
finally
{
prefab.SetActive(wasActive);
instance.SetActive(wasActive);
}
return instance;
}
}
static void SetName(UnityEngine.Object instance, UnityEngine.Object prefab)
{
if (VContainerSettings.Instance != null && VContainerSettings.Instance.RemoveClonePostfix)
instance.name = prefab.name;
}
}
}