#c# #unity3d
Вопрос:
Прежде всего , я знаю, что у Unity есть функция FindObjectOfType (), но я не хочу ее использовать.
Я сделал своего рода «Менеджер ссылок» для всех ссылок на мои сценарии в unity. CameraManager,EventManager,UIManager и т.д..
Эти «Менеджеры» никогда не общаются друг с другом, они всегда общаются вот так. например, менеджер камеры —> Менеджер ссылок —>> Менеджер событий
В настоящее время в моем справочном менеджере есть ссылки на публичную структуру, в которых я храню все ссылки на менеджеров.
Мой вопрос : Есть ли какой-нибудь хороший способ, как у меня может быть 1 метод, который вернет менеджер нужного мне типа.?
Мой текущий код :
public class ReferenceManager : MonoBehaviour
{
[SerializeField]
public References references = new References(); //all references go throught this struct
public struct References
{
[field: SerializeField]
public CameraManager cameraManager { get; set; }
[field: SerializeField]
public UIManager uiManager { get; set; }
}
private void Awake()
{
AssignReferences();
}
void AssignReferences()
{
List<ISystemComponent> systemComponentsList = Reference.GetSystemComponents();
foreach(ISystemComponent sC in systemComponentsList)
{
if(sC.GetType() == typeof(CameraManager))
{
references.cameraManager = sC as CameraManager;
}
if(sC.GetType() == typeof(UIManager))
{
references.uiManager = sC as UIManager;
}
}
}
}
public static class Reference
{
static List<ISystemComponent> systemComponentList = new List<ISystemComponent>(); //i hold all Managers in this list
public static void SubscribeSystemComponent(ISystemComponent sC)
{
systemComponentList.Add(sC);
}//any manager can be added to list by this method
public static List<ISystemComponent> GetSystemComponents()
{
return systemComponentList;
}
}
public interface ISystemComponent
{
}//this connects all managers with interface
public class CameraManager : MonoBehaviour, ISystemComponent //first manager
{
private void Awake()
{
Reference.SubscribeSystemComponent(this);
}
}
public class UIManager : MonoBehaviour, ISystemComponent //second manager
{
ReferenceManager rM;
CameraManager cM;
private void Awake()
{
Reference.SubscribeSystemComponent(this);
}
private void Start()
{
GetReferences();
}
void GetReferences()
{
ReferenceManager rM = FindObjectOfType<ReferenceManager>();
cM = rM.references.cameraManager; ///This way i currectly connect 2 managers 1 manager ---> ReferenceManager ---> 2 manager
}
}
Я хочу создать 1 универсальный метод, который всегда будет возвращать менеджер типа того, что я буду вызывать, что-то вроде этого :
//Desired example
ISystemComponent GetReference(/*Ispecify here somehow what type of manager i want*/)
{
return /*based of my specification that type will be returned by this method*/
}
Я буду рад любым идеям, спасибо.
Комментарии:
1. Имейте список всех ваших менеджеров в своем справочном менеджере. Затащи их всех туда. В начале повторите список, чтобы заполнить словарь <Type, ISystemComponent>. Затем вы можете передать typeof() в качестве параметра и попробовать значение в своем словаре.
2. У меня уже есть список этих менеджеров и я делаю что-то подобное в справочном менеджере, методе AssignReferences() , не могли бы вы более подробно указать это «заполнить словарь <Type, ISystemComponent>» или не могли бы вы указать мне какой-нибудь пример? Что мне нужно искать?
Ответ №1:
Я считаю, что предложение Эвертса состоит в том, чтобы использовать Dictionary<Type, ISystemComponent>
:
public class ReferenceManager : MonoBehaviour
{
[SerializeField]
public References references = new References(); //all references go throught this struct
public struct References
{
[field: SerializeField]
public CameraManager cameraManager { get; set; }
[field: SerializeField]
public UIManager uiManager { get; set; }
}
private void Awake()
{
AssignReferences();
}
void AssignReferences()
{
var systemComponentsList = Reference.GetSystemComponents().Values;
foreach(ISystemComponent sC in systemComponentsList)
{
if(sC.GetType() == typeof(CameraManager))
{
references.cameraManager = sC as CameraManager;
}
if(sC.GetType() == typeof(UIManager))
{
references.uiManager = sC as UIManager;
}
}
}
}
public static class Reference
{
private static Dictionary<Type, ISystemComponent> systemComponentDict = new(); //i hold all Managers in this list
public static void SubscribeSystemComponent(ISystemComponent sC)
{
systemComponentDict.Add(sC.GetType(), sC);
}//any manager can be added to list by this method
public static Dictionary<Type, ISystemComponent> GetSystemComponents()
{
return systemComponentDict;
}
public static T GetISystemComponent<T>() where T : ISystemComponent
{
if(!systemComponentsDict.TryGetValue(typeof(T), out var component))
{
Debug.LogError(typeof(T) " - this component type not exist in List of ISystemComponent" );
component = default(T);
}
return component;
}
}
Комментарии:
1. Спасибо за ваш код, почему это должно быть лучше? Для меня это выглядит почти так же.
2. Это был бы тот же результат. Единственным преимуществом является производительность, поиск по списку равен O(n), в то время как поиск по словарю равен O(1), что означает, что для списка в худшем случае вам нужно запустить весь список, в словаре есть хэш-таблица, которая работает быстрее.
Ответ №2:
Вызов:
GetISystemComponent<MazeManager>();
Метод:
public static T GetISystemComponent<T>() where T : ISystemComponent
{
foreach(ISystemComponent sC in systemComponentList)
{
if(sC.GetType().Equals(typeof(T)))
{
return (T)sC;
}
}
Debug.LogError(typeof(T) " - this component type not exist in List of ISystemComponent" );
return default(T);
}
Комментарии:
1. Я думаю, что есть лучший синтаксис этого : public T GetISystemComponent<ISystemComponent>, где T: ISystemComponent
2. Спасибо за помощь, мне больше нравится по-твоему. Это работает так же.