简体   繁体   中英

How do I get a value from another script in Unity (with an IEnumerator)?

I want to get a value in a script from a different script. I have two scripts:

public class CargarDatos : MonoBehaviour
{
    public string[] fechas;
    public string dia;

    IEnumerator Start()
    {
        UnityWebRequest www = UnityWebRequest.Get("http://localhost/Linx/Fechas.php");
        yield return www.SendWebRequest();
        string fechasDatos = www.downloadHandler.text;
        print(fechasDatos);
        fechas = fechasDatos.Split(';');
        dia = GetValorDatos(fechas[0], "Dia:");
        //print(dia);
    }

    string GetValorDatos(string datos, string index)
    {
        string valor = datos.Substring(datos.IndexOf(index)+index.Length);
        if(valor.Contains("|"))
        {
           valor = valor.Remove(valor.IndexOf("|")); 
        }
        return valor;
    }
}

Here, as it is shown, a connection is established to a database and get a value (dia). This script is attached to the Main Camera, as I only want it to connect at the start of the game. Here, the function print(dia) returns the correct value, so this script works.

The problem is when I try to get the value in this script:

public class BarraExperiencia : MonoBehaviour
{
    void Start()
    {
        print(GameObject.Find("Main Camera").GetComponent<CargarDatos>().dia);
    }
}

This script is attached to a slider, and I have tried to print that to see if it works, but it doesn't print anything. What have I done wrong? Thanks.

The problem is most likely that the Start method on BarraExperiencia is called too early.

In this case, you should use a callback process or event. There are numerous ways to do it, here is one without changing too much of your existing code.

We first need to modify CargarDatos, I only keep the relevant parts so you need to keep all other members and methods.

public class CargarDatos : MonoBehaviour
{
    public event Action<string> RaiseDataComplete;

    IEnumerator Start()
    {
        UnityWebRequest www = UnityWebRequest.Get("http://localhost/Linx/Fechas.php");
        yield return www.SendWebRequest();
        string fechasDatos = www.downloadHandler.text;
        fechas = fechasDatos.Split(';');
        dia = GetValorDatos(fechas[0], "Dia:");
        RaiseDataComplete?.Invoke(dia);
    }
}

And here goes the other script:

public class BarraExperiencia : MonoBehaviour
{
    void Start()
    {
        GameObject.Find("Main Camera").GetComponent<CargarDatos>().RaiseDataComplete += OnComplete;
    }

    void OnComplete(string data){ print(data); }
}

The second script finds CargarDatos and assign a listener to the event. When the data is loaded on CargarDatos, it will notify any listener passing the data as parameter.

EDIT: if more data are needed,

  1. Add more event so each listener can register to that specific event and get only that data
  2. Pass the whole class to the event (not recommended but possible)
  3. Create a container type to pass all the needed data

    public class Storage { public string Dia {get; private set; } public string Other{ get; private set; } public int SomeValue{get; private set; } public Storage(string dia, string other, int value) { Dia = dia; Other = other; SomeValue = value; } }

then modify the event:

public event Action<Storage>RaiseDataComplete;

and pass the new object:

RaiseDataComplete?.Invoke(new Storage(dia, "other string", 123));

Then listener gets new method signature

void OnComplete(Storage data)
{ 
     print($"{data.Dia} {data.Other} {data.SomeValue}"); 
}

Finally, you can turn all your members in CargarDatos as private and anything that is needed outside is passed via the event.

I think you are trying to get the "dia" value before the coroutine has finished. Try to print the value only after the coroutine is finished.

Also, it would be faster to use FindObjectOfType to get the component.

The value is not available yet. I highly recommend you not use public variables and rather encapsulate access to the value in a property like:

        private string dia = null;
        public string Dia {
            get {
                if(dia != null) {
                    return dia;
                }

                UnityWebRequest www = UnityWebRequest.Get ("http://localhost/Linx/Fechas.php");
                yield return www.SendWebRequest ();
                string fechasDatos = www.downloadHandler.text;
                print (fechasDatos);
                fechas = fechasDatos.Split (';');
                dia = GetValorDatos (fechas[0], "Dia:");
            }
        }

UPDATED Please take a look at the following example architecture for your scenario. We cannot encapsulate yield within the property as I originally suggested due to the async nature of Unity UnityWebRequest .

In the below, we call our service and pass in a callback of type Action<User> to run when the service completes. This callback can now do whatever is needed from your consuming component - in the example, we update a Text component.

You can copy and paste these into your project and they will return sample data from Github.

MyService.cs

using System;
using System.Collections;
using UnityEngine;
using UnityEngine.Networking;

namespace MyCompany.MyGame {
    public class User {
        public string login;
    }

    public class MyService {
        public IEnumerator GetUser (Action<User> callback) {
            using (UnityWebRequest webRequest = UnityWebRequest.Get ("https://api.github.com/users/octocat")) {
                yield return webRequest.SendWebRequest ();
                callback (JsonUtility.FromJson<User> (webRequest.downloadHandler.text));
            }
        }
    }
}

Entry.cs

using UnityEngine;
using UnityEngine.UI;

namespace MyCompany.MyGame {
    public class Entry : MonoBehaviour {
        MyService myService;

        [SerializeField]
        Text playerName;

        void Awake () {
            myService = new MyService ();
        }

        void Start () {
            StartCoroutine (myService.GetUser ((User result) => {
                Debug.Log (result);
                playerName.text = result.login;
            }));
        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM