Используя Unity, я работаю над игрой, в которой все Gameobjects
с определенным тегом пропадают/появляются довольно регулярно (в среднем каждые 10 секунд). Я использую GameObject.FindGameObjectsWithTag()
для создания Gameobject[]
, через который я перечисляю каждый раз, когда объекты нужно сделать видимыми/невидимыми. Я не могу вызвать его один раз, при запуске, так как во время игры создаются новые Gameobjects
. Я думал, что будет хуже получать доступ и изменять Gameobject[]
каждый раз, когда что-то создается/уничтожается. Есть ли лучший способ справиться с этим. Я знаю, как плохо влияют на производительность методы GameObject.Find
...
Лучший способ найти игровые объекты без GameObject.Find
Ответы (2)
Да, есть лучший способ сделать это. Имейте сценарий с переменной List
, которая может хранить игровые объекты. Сделать его синглтоном лучше, но не обязательно.
Этот одноэлементный скрипт должен быть прикреплен к пустому игровому объекту:
public class GameObjectManager : MonoBehaviour
{
//Holds instance of GameObjectManager
public static GameObjectManager instance = null;
public List<GameObject> allObjects = new List<GameObject>();
void Awake()
{
//If this script does not exit already, use this current instance
if (instance == null)
instance = this;
//If this script already exit, DESTROY this current instance
else if (instance != this)
Destroy(gameObject);
}
}
Теперь напишите сценарий, который регистрирует себя как List
в сценарии GameObjectManager
. Он должен зарегистрироваться в функции Awake
и отменить регистрацию в функции OnDestroy
.
Этот сценарий должен быть прикреплен к каждому префабу, который вы хотите добавить в список. Просто сделайте это в редакторе:
public class GameObjectAutoAdd : MonoBehaviour
{
void Awake()
{
//Add this GameObject to List when it is created
GameObjectManager.instance.allObjects.Add(gameObject);
}
void OnDestroy()
{
//Remove this GameObject from the List when it is about to be destroyed
GameObjectManager.instance.allObjects.Remove(gameObject);
}
}
Если GameObjects не сборные, а GameObjects, созданные с помощью кода, просто прикрепите к ним сценарий GameObjectAutoAdd
после их создания:
GameObject obj = new GameObject("Player");
obj.AddComponent<GameObjectAutoAdd>();
Теперь вы можете получить доступ к вашему GameObjects
в списке с помощью GameObjectManager.instance.allObjects[n];
, где n
— это порядковый номер, и вам больше не нужно использовать какие-либо функции Find
для поиска GameObject.
Это правда, что вызов GameObject.Find потребляет много ресурсов.
Смысл должен заключаться в том, чтобы сохранить результат и использовать его всегда, но я понимаю, что вы не можете этого сделать.
Другой вариант — сделать все эти игровые объекты дочерними для одного игрового объекта, назовем его handlerGameobject. У него будет сценарий, который не использует GameObeject. Найдите, что он может получить дочерний элемент или преобразовать. Найдите, который использует меньше ресурсов.
Надеюсь, это поможет!
Большое спасибо! Это помогает.
@Chopi Вам нужно 15 представителей, чтобы проголосовать. OP новый и еще не имеет этого представителя, поэтому не может голосовать. Хотя, он/она может принять ответ, который он/она уже сделал.
Я не могу тебя отблагодарить. Это решает все мои проблемы! Вы не могли бы объяснить это лучше. Примите мою глубочайшую благодарность.
Пожалуйста. Кстати, в моем ответе была опечатка в функции
OnDestroy
, где я использовал функцию ListAdd
вместо функцииRemove
. ФункцияAdd
должна быть только в функцииAwake
. Я только что исправил это. Обязательно обновите код.Обычно я предпочитаю выполнять операции с внешними ссылками в
Start
вместоAwake
, если заранее не установлен очень специфический порядок выполнения скрипта.@Galandil, я понимаю, что ты имеешь в виду. Незнание того, какую функцию поместить в код, может вызвать такие проблемы, как переменная, которая не инициализируется перед использованием. Хотя я потратил несколько минут, решая, какой из них использовать между
Awake
,Start
иOnEnable
, и решил использоватьAwake
. Это решение обусловлено двумя причинами: 1. ФункцияAwake
будет вызываться, даже если этот сценарий отключен. Без этого скрипт не зарегистрируется, если он отключен. 2. ФункцияAwake
будет вызываться в первом кадре, регистрируя ее, чтобы ее можно было использовать в обновленииStart
других скриптов.@Programmer Относительно 1: это неправда, хотя в документации говорится об обратном. Попробуйте
Debug.Log("Awake");
вAwake()
на отключенном объекте в иерархии перед входом в режим воспроизведения: он ничего не зарегистрирует, то же самое и с чем-либо, введенным вStart()
. Вот почему сейчас я разделяю ссылки на себя/других междуAwake
иStart
, и если мне нужно отключить объект при запуске, я делаю это с помощью кода в концеStart
.@Galandil Вы путаете отключение с деактивацией. Вы включаете/отключаете компоненты/скрипты, но активируете/деактивируете GameObjects. Я говорю о включении/отключении компонентов/скриптов из редактора или через скрипт:
script.enabled = false;
@Rupture, лол, почему ты не принял ответ? Разве это не решило вашу проблему?
Виноват! Должно быть, это был промах
@Programmer Я неправильно прочитал сценарий для игрового объекта, мой плохой. Во всяком случае, я следую той же процедуре даже в отношении отключения компонентов, мне кажется гораздо более безопасной практикой знать, что любой объект или компонент, который нужно отключить при запуске, всегда выполняется внутри
Start
скрипта с большим количеством объектов в иерархию, как только я сошел с ума, чтобы найти, какой скрипт был отключен в Инспекторе (если нет способа, который я не знаю, для более глубокого поиска с помощьюt:script
и какой-то дополнительной команды, чтобы показать только отключенные, но я не знаю не думаю, что он существует).