Лучший способ найти игровые объекты без GameObject.Find

avatar
Rupture
8 апреля 2018 в 08:08
2137
2
4

Используя Unity, я работаю над игрой, в которой все Gameobjects с определенным тегом пропадают/появляются довольно регулярно (в среднем каждые 10 секунд). Я использую GameObject.FindGameObjectsWithTag() для создания Gameobject[], через который я перечисляю каждый раз, когда объекты нужно сделать видимыми/невидимыми. Я не могу вызвать его один раз, при запуске, так как во время игры создаются новые Gameobjects. Я думал, что будет хуже получать доступ и изменять Gameobject[] каждый раз, когда что-то создается/уничтожается. Есть ли лучший способ справиться с этим. Я знаю, как плохо влияют на производительность методы GameObject.Find...

Источник

Ответы (2)

avatar
Programmer
8 апреля 2018 в 08:51
8

Да, есть лучший способ сделать это. Имейте сценарий с переменной 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.

Rupture
8 апреля 2018 в 09:01
0

Я не могу тебя отблагодарить. Это решает все мои проблемы! Вы не могли бы объяснить это лучше. Примите мою глубочайшую благодарность.

Programmer
8 апреля 2018 в 09:32
0

Пожалуйста. Кстати, в моем ответе была опечатка в функции OnDestroy, где я использовал функцию List Add вместо функции Remove. Функция Add должна быть только в функции Awake. Я только что исправил это. Обязательно обновите код.

Galandil
8 апреля 2018 в 13:46
0

Обычно я предпочитаю выполнять операции с внешними ссылками в Start вместо Awake, если заранее не установлен очень специфический порядок выполнения скрипта.

Programmer
8 апреля 2018 в 14:05
0

@Galandil, я понимаю, что ты имеешь в виду. Незнание того, какую функцию поместить в код, может вызвать такие проблемы, как переменная, которая не инициализируется перед использованием. Хотя я потратил несколько минут, решая, какой из них использовать между Awake, Start и OnEnable, и решил использовать Awake. Это решение обусловлено двумя причинами: 1. Функция Awake будет вызываться, даже если этот сценарий отключен. Без этого скрипт не зарегистрируется, если он отключен. 2. Функция Awake будет вызываться в первом кадре, регистрируя ее, чтобы ее можно было использовать в обновлении Start других скриптов.

Galandil
8 апреля 2018 в 14:22
0

@Programmer Относительно 1: это неправда, хотя в документации говорится об обратном. Попробуйте Debug.Log("Awake"); в Awake() на отключенном объекте в иерархии перед входом в режим воспроизведения: он ничего не зарегистрирует, то же самое и с чем-либо, введенным в Start(). Вот почему сейчас я разделяю ссылки на себя/других между Awake и Start, и если мне нужно отключить объект при запуске, я делаю это с помощью кода в конце Start.

Programmer
8 апреля 2018 в 14:37
0

@Galandil Вы путаете отключение с деактивацией. Вы включаете/отключаете компоненты/скрипты, но активируете/деактивируете GameObjects. Я говорю о включении/отключении компонентов/скриптов из редактора или через скрипт: script.enabled = false;

Programmer
8 апреля 2018 в 15:18
0

@Rupture, лол, почему ты не принял ответ? Разве это не решило вашу проблему?

Rupture
8 апреля 2018 в 15:40
1

Виноват! Должно быть, это был промах

Galandil
8 апреля 2018 в 15:51
0

@Programmer Я неправильно прочитал сценарий для игрового объекта, мой плохой. Во всяком случае, я следую той же процедуре даже в отношении отключения компонентов, мне кажется гораздо более безопасной практикой знать, что любой объект или компонент, который нужно отключить при запуске, всегда выполняется внутри Start скрипта с большим количеством объектов в иерархию, как только я сошел с ума, чтобы найти, какой скрипт был отключен в Инспекторе (если нет способа, который я не знаю, для более глубокого поиска с помощью t:script и какой-то дополнительной команды, чтобы показать только отключенные, но я не знаю не думаю, что он существует).

avatar
Chopi
8 апреля 2018 в 08:22
2

Это правда, что вызов GameObject.Find потребляет много ресурсов.

Смысл должен заключаться в том, чтобы сохранить результат и использовать его всегда, но я понимаю, что вы не можете этого сделать.

Другой вариант — сделать все эти игровые объекты дочерними для одного игрового объекта, назовем его handlerGameobject. У него будет сценарий, который не использует GameObeject. Найдите, что он может получить дочерний элемент или преобразовать. Найдите, который использует меньше ресурсов.

Надеюсь, это поможет!

Rupture
8 апреля 2018 в 08:47
0

Большое спасибо! Это помогает.

PassetCronUs
8 апреля 2018 в 09:47
1

@Chopi Вам нужно 15 представителей, чтобы проголосовать. OP новый и еще не имеет этого представителя, поэтому не может голосовать. Хотя, он/она может принять ответ, который он/она уже сделал.