Это эффективный способ сравнить имя игрового объекта с текстом InputField, чтобы включить / выключить игровой объект? - PullRequest
1 голос
/ 06 февраля 2020

Я пытаюсь сделать простой поиск и выявить инвентарь, задаваясь вопросом, есть ли лучший способ сделать это? Он прикреплен к родительскому Gameobject и работает с InputField для сравнения текста в поле и имени Gameobjects, чтобы показать и показать их.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System.Linq;

public class findFrame : MonoBehaviour
{

    public InputField searchText;

    void Find()
    {
        var found = new List<GameObject>(GameObject.FindGameObjectsWithTag("Button")).Where(g => g.transform.IsChildOf(this.transform));

        foreach (GameObject g in found)
        {
            Debug.Log("hello");

            if (g.transform.name != searchText.text)
            {
                gameObject.SetActive(false);
            }
            else
            {
                gameObject.SetActive(true);
            }
        }
    }


        void Update()
        { 
            Find();
        }
    }

Ответы [ 2 ]

1 голос
/ 06 февраля 2020

Краткий ответ: НЕТ!

  • В общем, как уже упоминалось: делать не делать это в Update => каждый кадр ! (Кстати, также нет в FixedUpdate, который выполняется реже, но все равно добавляет совершенно ненужные вызовы)

    Скорее сделайте ваш код управляемым событиями . В вашем случае вы могли бы скорее добавить слушателя к InputField.onValueChanged, который вызывается только тогда, когда значение действительно изменилось, что сокращает ваши вызовы метода до необходимого.

  • Тогда я бы предпочел вначале хранить все объекты один раз в HashSet (или, в зависимости от ваших потребностей, может быть даже Dictionary на случай, если вы захотите возможность доступа к кнопке по ее имени), что намного эффективнее, чем извлекать список снова и снова при каждом вызове метода.

  • Также использование FindObjectsWithTag стоит дорого а затем фильтрация с использованием isChildOf еще дороже и не нужна - на самом деле, она в основном рекурсивно всплывает transform.parent, пока не найдет заданное значение или null. Transform реализует IEnumerable, возвращающий все дочерние Transform с, так что вы можете просто перебирать все дочерние объекты, используя foreach. Так что скорее go наоборот и итерируйте только потомки и проверьте, есть ли у них тег, используя CompareTag.

    Это зависит от вашей настройки курса, поскольку isChildOf также возвращает Значение true, если данный объект является вложенным (глубоким) дочерним элементом. В случае, если к вашим кнопкам действительно прикреплен указанный c компонент - как я, например, предположил бы в этом случае Button - вы можете преодолеть это с помощью GetComponentsInChildren<Button>() и выполнить итерацию вместо этого.

  • Наконец, для сравнения строк есть более эффективные и, что более важно, более безопасные способы, чем просто использование == (см. C# разницу между == и Equals () )

    Вы можете либо найти точное совпадение, используя string.Eqials

    var isMatch = name.Equals(searchString, COMPARISONTYPE);
    

    Или - что я бы предпочел - вместо этого найти все частичные совпадения, просто используя string.Contains

    var isMatch = name.Contains(searchString);
    

    или, если вам нужно, более индивидуальное использование string.Compare

    var isMatch = string.Compare(name, searchString, COMPARISONTYPE)
    

    Где для Необязательный (но рекомендуемый) параметр COMPARISONTYPE. Вы можете выбрать одно из доступных значений StringComparison, например, для сопоставления без учета регистра et c.

Так что я бы использовал что-то вроде

public class findFrame : MonoBehaviour
{ 
    public InputField searchText;

    // A HashSet is basically a list but makes sure every element is unique
    // I would make this rather a Dictionary<string, GameObject> in case
    // you also plan to access a specific button by name
    // otherwise a HashSet does it
    private HashSet<GameObject> childButtons = new HashSet<GameObject>();

    public void Awake()
    {
        //Adds a listener to the input field and invokes a method when the value changes.
        searchText.onValueChanged.AddListener(OnValueChanged);

        // Run through all children GameObjects of this GameObject and check the tag
        foreach(Transform child in transform)
        // or as said if possible/necessary for nested children you could use
        //foreach(var child in GetComponentsInChildren<Button>())
        {
            // check if this child has the correct tag
            if(!child.CompareTag("Button")) continue;

            // If the tag matches add it to the HashSet
            childButtons.Add(child.gameObject);
        }
    }

    // Invoked when the value of the text field changes.
    public void OnValueChanged()
    {
        Debug.Log("Value Changed");

        // Get the search value
        var searchString = searchTexxt.text;

        // Simply run through the HashSet and set the matching object(s) active, the others inactive
        foreach(var button in childButtons)
        {
            // Skip null entries
            if(!button) continue;

            var name = button.name;

            // Here as said you can either check for an exact match
            //var isMatch = name.Equals(searchString, COMPARISONTYPE);
            // Or you could also check for partial matches - I would prefer this
            // (required if you want to see objects also while the user didn't type out the full name yet)
            // either by simply using Contains
            //var isMatch = name.Contains(searchString);
            // or if you need it more customized 
            var isMatch = string.Compare(name, searchString, COMPARISONTYPE) == 0;

            button.SetActive(isMatch);

            if(isMatch) Debug.Log($"Found match for {searchString} in button ${name} ");
        }
    }

    // When creating the buttons on runtime call this to also add them to the HashSet
    public void AddButton(GameObject button)
    {
        if(childButtons.Contains(button)) return;

        childButtons.Add(button);
    }
}
  • Если вы создадите кнопки на runt через скрипт, ну, тогда убедитесь, что вы добавляете их в HashSet каждый раз, когда создаете его экземпляр.

    // In another script you need the reference to the `findFrame` component
    // Drag it in here via the Inspector
    [SerializeField] findFrame findFrameReference;
    
    // As fallback find it on runtime (in case there is only one in the scene
    private void Awake ()
    {
        if(! findFrameReference) findFrameReference = FindObjectOfType<findFrame>();
    }
    

    Затем, где бы вы ни создавали новые кнопки

    var button = Instantiate(buttonPrefab, ...);
    findFrameReference.AddButton(button.gameObject);
    
0 голосов
/ 06 февраля 2020

В Unity функция обновления запускается один раз за каждый кадр!

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

Возможно, вы захотите получить пользовательский ввод в обновлении, а затем выполнить некоторые вычисления в FixedUpdate, который выполняется с фиксированной скоростью. частота кадров.

Это в стороне ... В Unity есть лучший способ сделать то, что вы делаете, используя метод CompareTag.

Этот метод компилируется "специальным" способом Компилятор Unity во избежание выделения строк и т. Д. c .. В основном оптимизирован для повышения производительности.

Вот несколько ссылок, которые могут помочь вам очистить воду:

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

В этом окне выполните поиск CompareTag в вашем браузере, чтобы точно определить вашу ситуацию: Сборка мусора и другие оптимизации

Из форума Unity

Из JetBrains

Наконец, если вам действительно нужно сравнить две строки ... Самый быстрый способ, скорее всего, будет выглядеть так:

string.CompareOrdinal(myString1, myString2) == 0 //this means equals


string.CompareOrdinal(myString1, myString2) != 0 //this means not equals
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...