Как сохранить ссылку на экземпляр объекта во время выполнения в Unity - PullRequest
1 голос
/ 17 апреля 2019

Мне нужно создать экземпляр объекта во время выполнения, где количество объектов основано на текстовом файле (количество строк). Все объекты должны быть активными (событие onClick ()), и при их нажатии должна появиться всплывающая подсказка. Всплывающие подсказки являются конкретными (Object1 -> Tooltip1, Object2 -> Tooltip2). Всплывающие подсказки - это просто панель, состоящая из нескольких других панелей и кнопок. Один из них создает новую панель. Также эти Panel специфичны (Tooltip1 -> Panel1 и т. Д.). Я создаю префаб для каждого из трех объектов.

Итак, Object1 - onClick () -> Tooltip1 - onClick () -> Panel1. Во время выполнения.

Как сохранить ссылку на объект, созданный во время выполнения?

Мое первое решение :

  1. Создайте пустой объект и назначьте ему скрипт с общедоступной переменной (Object prefab).
  2. Создание экземпляра объекта:

    for (int i = 0; i < numberOfObject; i++)
    {
       var instance = Instantiate(m_ObjectPrefab);
       instance.name = "Object_" + m_nameObject[i];
    
  3. Создание одной подсказки для каждого объекта:

        var instancePanelTooltip = Instantiate(m_panelTooltipPrefab, m_canvas.transform);
        instancePanelTooltip.name = "Panel_Tooltip_" + m_nameObject[i];
    
  4. Создание одной панели для каждой подсказки:

       var instancePanel = Instantiate(m_panelPrefab, m_canvas.transform);
       instancePanel.name = "Panel_" + m_nameObject[i];
       instancePanel.SetActive(false);
       instancePanelTooltip.SetActive(false);
    
  5. Добавление обработчика событий для объекта

        DetectClickOnObject scriptDetectClickPanelTooltip = instance.AddComponent<DetectClickOnObject>();
        scriptDetectClickPanelTooltip.SetTooltip(instancePanelTooltip);
    
  6. Добавить обработчик события для кнопки на всплывающей подсказке

        DetectClickOnObject scriptDetectClickPanel = buttonOpenPanel.AddComponent<DetectClickOnObject>();
        scriptDetectClickPanel.SetPanel(instancePanel);
    }
    

Проблема с этим решением:

  1. Я создам 3 экземпляра объекта (Object, Tooltip, Panel) для каждой строки файла. Для объекта это нормально, но это не относится к подсказке и панели, потому что активна только одна подсказка среди всех (то же самое для панели).

  2. Я просто избегаю проблемы ссылок, потому что я создаю в одном месте все объекты (по одному для каждого элемента), но что я могу сделать, если мне нужно получить доступ к Tooltip2 или Panel3 без ссылки (я пытаясь избежать Find и подобных).

Вывод первого решения: Решение работает, но я думаю, что есть лучший способ сделать это (избегайте создания такого большого количества объектов и сохраняйте правильные ссылки).

Мое второе решение (рекомендации):

  1. Я пытаюсь создать класс, чтобы сохранить ссылку на все объекты, создаваемые во время выполнения.
  2. Я хочу создать экземпляр объекта для каждой строки, но мне нужна только одна всплывающая подсказка и панель для всех объектов и изменение свойств в соответствии с щелчком объекта. Таким образом, Object создается во время выполнения, но Tooltip и Panel уже находятся на сцене, но не активны.
  3. Мне нужен менеджер событий Register, чтобы добавить событие onClick () во время выполнения для объектов, и он должен обрабатывать свойства, которые должны быть установлены для всплывающей подсказки и панели, на основе объекта, по которому щелкнули.

Проблемы со вторым решением: Ссылаясь на 1) Я пытался следовать этому , но ничего не получилось. Я потерян между синглтоном, статикой и чем-то еще. Ссылаясь на 2) Я думаю, что это может быть легко, мне просто нужно вырезать некоторые из первого решения. Ссылаясь на 3) Я не могу сделать больше, если у меня нет менеджера ссылок класса.

Что я ищу:

  1. Первое решение так плохо? Если я смотрю на код, то мне это противно, он далек от элегантности (или чего-то подобного).
  2. Можете ли вы предложить мне, как я могу отслеживать создание ссылок во время выполнения с помощью диспетчера ссылок? И как это использовать?

@ Бхенам Саттар: Как класс DataModell,

public class DataModelPOI
{
    public string m_namePOI { get; private set; }
    public string m_locationPOI { get; private set; }
    public Vector2d m_positionPOI { get; private set; }

    public GameObject m_gameObject_POI;
    public GameObject m_gameObjectTooltip;
    public GameObject m_gameObjectPanel;

    public DataModelPOI(string namePOI, string locationPOI, Vector2d positionPOI)
    {
        this.m_namePOI = namePOI;
        this.m_locationPOI = locationPOI;
        this.m_positionPOI = positionPOI;
    }
}

как DataManager,

public class POIManager : MonoBehaviour
{
    List<DataModelPOI> dataCollectionPOI = new List<DataModelPOI>();

    void Start()
    {
        ReadFile();

        SpawnPOI();
    }

    void Update()
    {
        int count = dataCollectionPOI.Count;
        for (int i = 0; i < count; i++)
        {
            UpdatePOIPosition();
        }
    }

    void ReadFile()
    {
        TakeDataFromFile();
        for (int i = 0; i < ength; i++)
        {
            DataModelPOI dataPOI = new DataModelPOI(m_namePoi[i], m_namePoi[i], _locations[i]);
            dataCollectionPOI.Add(dataPOI);
        }
    }

    private void SpawnPOI()
    {
        for (int i = 0; i < dataCollectionPOI.Count; i++)
        {
            DataModelPOI dataPOI = dataCollectionPOI[i];
            var instance = Instantiate(m_POIPrefab);
            instance.name = "POI_" + m_namePoi[i];
            dataPOI.m_gameObject_POI = instance;
            dataPOI.m_gameObjectTooltip = m_panelTooltipPOI;
            dataPOI.m_gameObjectPanel = m_panelPOI;
        }
    }

Теперь мне нужно зарегистрировать событие, связанное с экземпляром GameObject. Я хочу сделать это в моем EventManager. Как я могу указать на dataCollectionPOI в классе EventManager, созданном и переданном в DataManager? Спасибо за ваше время.

Ответы [ 2 ]

1 голос
/ 17 апреля 2019

Исходя из моего понимания, ваш вопрос в основном вопрос дизайна.В первой части этого ответа я предлагаю вам сделать дизайн и сохранить ссылку на ваши объекты.Во второй части я дам вам несколько советов относительно производительности.

[Я использую RootObject вместо Object, чтобы отсылать вас к основному GameObject, который вы создаете.]

Дизайн

Давайте разберем наши потребности, а затем предложим решение для каждого.

Сначала мы хотим прочитать какой-нибудь текстовый файл, а затем получить из него некоторые данные.Эти данные будут использованы позже для создания GameObjects.А сейчас давайте сосредоточимся на самих данных.

Здесь нам нужен класс менеджера, который читает для нас файл и сохраняет данные в некоторой форме.Мы получаем доступ к этому менеджеру позже и запрашиваем наши данные для создания объектов GameObject.

Этот класс менеджера хранит наши данные в коллекции объектов данных [обратите внимание, что здесь речь идет о простых объектах, а не об объектах Unity GameObjects],Вам нужно спроектировать этот класс данных на основе каждой строки текста, которую вы имеетеПри желании вы также можете сохранить здесь ссылки на GameObjects.

Предположим, вы читаете три строковых значения из каждой строки с именами ValueOne, ValueTwo и ValueThree и хотите сохранитьссылка на три объекта GameObject, называемых RootObject, ToolTip и Panel.Для этой цели вы можете определить следующий класс:

public class DataModel {

  // Values read from text file.
  public string valueOne { get; private set; }
  public string valueTwo { get; private set; }
  public string valueThree { get; private set; }

  // Placeholders for GameObjecs created at runtime.
  public GameObject rootObject;
  public GameObject tooltipObject;
  public GameObject panelObject;

  public DataModel(string valueOne, string valueTwo, string valueThree){
    this.valueOne = valueOne;
    this.valueTwo = valueTwo;
    this.valueThree = valueThree;
  }
}

Затем в вашем классе менеджера вы можете создать коллекцию (например, список) для хранения ваших данных.В какой-то момент ваш класс менеджера должен прочитать текстовый файл и заполнить этот список экземплярами DataModel.Это будет что-то вроде этого:

public class DataManager {

  List<DataModel> dataCollection = new List<DataModel>();

  public void ReadFile() {

    // Here you need to read the file and get the values you need.
    // The actual code should be different from what I'm putting here.

    foreach(string line in lines) {
      // You get valueOne, valueTwo and valueThree
      // from each line and maybe prepare them 
      // (maybe you need conversion from string to int)

      DataModel data = new DataModel(valueOne, valueTwo, valueThree);
      dataCollection.Add(data);
    }
  }
}

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

Пришло время создавать объекты на основе данных и сохранять ссылку.

for (int i = 0; i++; i <= manager.dataCollection.Count) {
  DataModel data = manager.dataCollection[i];
  data.rootObject = instantiate() // You instantiate the root GameObject here.
  data.tooltip = instantiate() // You instantiate the tooltop GameObject here.
  data.panel = instantiate() // You instantiate the panel GameObject here.
}

Готово.Теперь у вас есть класс менеджера, который имеет ссылку на все данные, а также объекты GameObject, которые создаются на основе этих данных.

Производительность

Это может быть не очень хорошая идеяделать все это во время выполнения, так как это может привести к падению кадров в вашей игре.Если это проблема, вы можете попробовать пул объектов.Если вы ищете это, вы сможете найти отличные учебные пособия о том, как выполнять пул объектов.

Если вы в конечном итоге не использовали пул объектов, вы все равно можете исправить любые падения производительности, создавая только один GameObject на кадр.Это можно сделать с помощью сопрограмм.Вам просто нужно сделать yield return new WaitForEndOfFrame() в вашем цикле создания экземпляров.

Заключительное примечание

Имейте в виду, что это все предположение, и я не думаю, что тамэто один лучший ответ на ваш вопрос.Убедитесь, что вы пытаетесь понять, какие инструменты находятся в вашем распоряжении, и попробуйте их все, прежде чем выбрать одно решение.:)

0 голосов
/ 17 апреля 2019

Вы в основном на правильном пути, но вы можете отделить (или «объективировать»;), какие сценарии хранят, какие ссылки.Сохраняйте сценарии, которые управляют внутренней работой ваших префабов, в этих префабах.

Одноэлементный шаблон для всплывающих подсказок, безусловно, является подходящим вариантом.Там, где вы не можете использовать одноэлементный объект, например, в интерактивных объектах, используйте что-то более похожее на внедрение зависимостей (для сохранения на Finds) и предварительно сохраненную ссылку:

var obj = Instantiate(...);
obj.GetComponent<SomeManagerScript>.SomeDependency = SingletonInstance;
obj.GetComponent<StoredReferenceScript>.TextPanel.Text = "Some Text";

Что касается событийВы можете справиться с ними несколькими способами.Во-первых, каждый кликабельный управляет своими собственными привязками / функциональностью;или два, каждый кликабельный уведомляет глобального менеджера о том, что на него щелкнули, и менеджер определяет действия, которые нужно предпринять.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...