Gameplay Ingredients是一组用于 Unity 游戏的运行时和编辑器工具:一组脚本的集合,可在制作游戏和原型时简化简单的任务。
您最多选择25个主题 主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

272 行
9.0 KiB

using System.Linq;
using System.Collections.Generic;
using UnityEngine;
using System;
using System.Reflection;
namespace GameplayIngredients
{
[HelpURL(Help.URL + "managers")]
public abstract class Manager : MonoBehaviour
{
private static Dictionary<Type, Manager> s_Managers = new Dictionary<Type, Manager>();
public static bool TryGet<T>(out T manager) where T: Manager
{
manager = null;
if(s_Managers.ContainsKey(typeof(T)))
{
manager = (T)s_Managers[typeof(T)];
return true;
}
else
return false;
}
public static T Get<T>() where T: Manager
{
if(s_Managers.ContainsKey(typeof(T)))
return (T)s_Managers[typeof(T)];
else
{
Debug.LogError($"Manager of type '{typeof(T)}' could not be accessed. Check the excludedManagers list in your GameplayIngredientsSettings configuration file.");
return null;
}
}
public static bool Has<T>() where T:Manager
{
return(s_Managers.ContainsKey(typeof(T)));
}
static readonly Type[] kAllManagerTypes = TypeUtility.GetConcreteTypes<Manager>();
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
static void AutoCreateAll()
{
s_Managers.Clear();
var exclusionList = GameplayIngredientsSettings.currentSettings.excludedeManagers;
if(GameplayIngredientsSettings.currentSettings.verboseCalls)
Debug.Log("Initializing all Managers...");
DependencyGraph dg = new DependencyGraph();
foreach (var type in kAllManagerTypes)
{
// Check for any Do Not Create Attribute
var doNotCreateAttr = type.GetCustomAttribute<DoNotCreateManagerAttribute>();
if (doNotCreateAttr != null)
continue;
dg.Add(type);
var dependencies = type.GetCustomAttribute<ManagerDependsOnAttribute>();
if(dependencies != null)
{
foreach(var depType in dependencies.dependantTypes)
{
dg.AddDependency(type, depType);
}
}
}
if (exclusionList != null)
{
foreach (var type in exclusionList)
{
if(dg.TryExclude(type, exclusionList))
{
if (GameplayIngredientsSettings.currentSettings.verboseCalls)
Debug.LogWarning($"Manager : Excluded {type} from manager creation");
}
else
{
if (GameplayIngredientsSettings.currentSettings.verboseCalls)
Debug.LogWarning($"Manager : Could not exclude {type} from manager creation because it has dependencies");
}
}
}
List<Type> toBeCreated = dg.GetOrderedList();
// Finally, create all managers
foreach (var type in toBeCreated)
{
var prefabAttr = type.GetCustomAttribute<ManagerDefaultPrefabAttribute>();
GameObject gameObject;
if (prefabAttr != null)
{
var prefab = Resources.Load<GameObject>(prefabAttr.prefab);
if (prefab == null) // Try loading the "Default_" prefixed version of the prefab
{
prefab = Resources.Load<GameObject>("Default_" + prefabAttr.prefab);
}
if (prefab != null)
{
gameObject = GameObject.Instantiate(prefab);
}
else
{
Debug.LogError($"Could not instantiate default prefab for {type.ToString()} : No prefab '{prefabAttr.prefab}' found in resources folders. Ignoring...");
continue;
}
}
else
{
gameObject = new GameObject();
gameObject.AddComponent(type);
}
gameObject.name = type.Name;
GameObject.DontDestroyOnLoad(gameObject);
var comp = (Manager)gameObject.GetComponent(type);
s_Managers.Add(type, comp);
if (GameplayIngredientsSettings.currentSettings.verboseCalls)
Debug.Log(string.Format(" -> <{0}> OK", type.Name));
}
}
class DependencyGraph
{
List<DependencyNode> nodes;
public DependencyGraph()
{
nodes = new List<DependencyNode>();
}
public void Add(Type o)
{
if (!nodes.Any(n => n.target == o))
{
var node = new DependencyNode(o);
nodes.Add(node);
}
}
DependencyNode Get(Type o)
{
return nodes.Where(n => n.target == o).FirstOrDefault();
}
public void AddDependency(Type o, Type dependency)
{
Add(o);
Add(dependency);
var node = Get(o);
if(!IsDependentOn(dependency, o)) // Prevent Circular Dependency
{
node.dependencies.Add(Get(dependency));
}
else
{
if (GameplayIngredientsSettings.currentSettings.verboseCalls)
Debug.LogWarning($"Managers : Found circular dependency {o} -> {dependency}, ignoring");
}
}
// Try exclude a type by its name, if it's not a dependency of another node
public bool TryExclude(string type, string[] exclusionList)
{
var n = nodes.Where(n => n.target.Name == type).FirstOrDefault();
foreach(var node in nodes)
{
if (node == n)
continue;
// If type our type is a dependency or is already excluded, then we don't remove the node
if (IsDependentOn(node.target, n.target) || exclusionList.Contains(node.target.Name))
return false;
}
n.excluded = true;
return true;
}
// Walk the tree to find dependencies
public bool IsDependentOn(Type type, Type dependency)
{
foreach(var dep in Get(type).dependencies)
{
if (dep.target == type)
return true;
else
return (IsDependentOn(dep.target, dependency));
}
return false;
}
// Return all dependencies of a given type
public List<Type> GetDependencies(Type type)
{
List<Type> t = new List<Type>();
t.Add(type);
foreach(var dep in Get(type).dependencies)
{
if (!t.Contains(dep.target))
t.Concat(GetDependencies(dep.target));
}
return t;
}
// Builds a list of loading order
public List<Type> GetOrderedList()
{
Dictionary<Type, int> d = new Dictionary<Type, int>();
foreach(var node in nodes)
{
if (node.excluded)
continue;
if (!d.ContainsKey(node.target))
d.Add(node.target, 0);
else
d[node.target] += 1;
foreach(var dep in node.dependencies)
{
if (dep.excluded)
continue;
if (!d.ContainsKey(dep.target))
d.Add(dep.target, 0);
else
d[dep.target] += 1;
}
}
return d.OrderBy(x => x.Value).Reverse().ToDictionary(x => x.Key, x => x.Value).Keys.ToList();
}
class DependencyNode
{
public List<DependencyNode> dependencies;
public readonly Type target;
public bool excluded;
public DependencyNode(Type target)
{
dependencies = new List<DependencyNode>();
this.target = target;
this.excluded = false;
}
public override string ToString()
{
return target.Name;
}
}
}
}
}