Unidad usando iEnumerator para establecer una clase que usará otro objeto -- # campo con json campo con unity3d campo con ienumerator camp Relacionados El problema

Unity Using IENumerator to set a class that another object will use


3
vote

problema

Español

Estoy tratando de crear un objeto de mi DB que usa otra clase. Pero parece que hay un problema debido a la respuesta tardía del iEnumerador (tal vez?).

Tenemos una clase enemiga muy simple para analizar a JSON a:

  private static class ViewHolder{     RadioButton m_radioButton;     TextView m_profileName;     TextView m_profileDetails;     LinearLayout m_profileLyt; } 0  

El Battlemanager está unido a un objeto en la escena

  private static class ViewHolder{     RadioButton m_radioButton;     TextView m_profileName;     TextView m_profileDetails;     LinearLayout m_profileLyt; } 111  

Aquí está aquí donde obtengo información de la base de datos. Todo parece ir correctamente, excepto Getenemy, devuelve un objeto enemigo predeterminado en lugar de uno con sus variables cargadas del JSON en el iEnumerator getenemyfromdb

  private static class ViewHolder{     RadioButton m_radioButton;     TextView m_profileName;     TextView m_profileDetails;     LinearLayout m_profileLyt; } 2  

Encontré esta clase de ayuda aquí en StackOverflow. Olvido a donde o daría un crédito adecuado, ¡pero funciona bien!

  private static class ViewHolder{     RadioButton m_radioButton;     TextView m_profileName;     TextView m_profileDetails;     LinearLayout m_profileLyt; } 3  

súper frustrante, creo que he leído un millón de puestos en el iEnumerator. Ojalá pudiera hacer que el método de iEnumerator devuelva mi objeto enemigo en lugar de tener un enemigo privado como variable que se efectúa por el iEnumerador y devuelto por el método de Getter.

¡Muchas gracias por cualquier ayuda!

Original en ingles

I am trying to create an object from my DB that another class uses. But there seems to be a problem due to the delayed response from the IEnumerator (maybe?).

We have a very simple enemy class to parse a json to:

[System.Serializable] public class Enemy{  public string EnemyID;  public string Name; } 

the BattleManager is attached to an object in the scene

public class BattleManager : Monobehaviour{   public Enemy debugEnemy;   void start()  {    //get a reference to the DBAccess    DBA = GameObject.Find("ManagerDB").GetComponent<DBAccess>();     debugEnemy = DBA.GetEnemy(1);    //debugEnemy EnemyID=0 and Name=""    //This is where the Problem is! Why is this not set from my DB?  } } 

Now here is where I get info from the database. Everything seems to go properly except GetEnemy returns a default Enemy Object instead of the one with its variables loaded from the json in the IEnumerator GetEnemyFromDB

public class DBAccess: Monobehaviour{   private Enemy enemy;   public Enemy GetEnemy(int EnemyID)  {    enemy = new Enemy();    StartCoroutine(GetEnemyFromDB(EnemyID));     //HERE enemy.EnemyID is 0 and enemy.Name is ""    return enemy;  }  private IEnumerator GetEnemyFromDB(int EnemyID)  {      WWWForm postData = new WWWForm();      postData.AddField("EnemyID", EnemyID);       WWW dbProc = new WWW(GetEnemyURL, postData);      yield return dbProc;       if (string.IsNullOrEmpty(dbProc.error)) //error is null or empty so: SUCCESS!      {          string jsonstring = "{\"Items\":" + dbProc.text + "}";           Enemy[] EnemiesFromDB;          EnemiesFromDB = JsonHelper.FromJson<Enemy>(jsonstring);           if (EnemiesFromDB.Length > 0)          {              enemy = EnemiesFromDB[0];              //HERE enemy.EnemyID is 1 and enemy.Name is "Evil Enemy Monster Man!"              //So it is working here!          }          else throw new System.Exception("No Enemy Found When Reading Json: " + JsonUtility.FromJson<Enemy>(jsonstring));       }      else throw new System.Exception("DB ERROR: " + dbProc.error);  }  } 

I found this helper class here on stackoverflow. I forget where or I would give proper credit, but it works great!

public static class JsonHelper {  public static string RemoveBrackets(string s)  {     s = s.Replace("[", string.Empty);     s = s.Replace("]", string.Empty);     return s;  }   public static T[] FromJson<T>(string json)  {      Wrapper<T> wrapper = JsonUtility.FromJson<Wrapper<T>>(json);     return wrapper.Items;  }   public static string ToJson<T>(T[] array)  {     Wrapper<T> wrapper = new Wrapper<T>();     wrapper.Items = array;     return JsonUtility.ToJson(wrapper);  }   public static string ToJson<T>(T[] array, bool prettyPrint)  {     Wrapper<T> wrapper = new Wrapper<T>();     wrapper.Items = array;     return JsonUtility.ToJson(wrapper, prettyPrint);  }   [System.Serializable]  private class Wrapper<T>  {     public T[] Items;  } } 

Super frustrating, I think I have read a million posts on IEnumerator. I wish I could just have the IEnumerator method return my Enemy object instead of having private enemy as variable that is effected by the IEnumerator and returned by the getter method.

Thanks so much for any help!

           

Lista de respuestas

3
 
vote
vote
La mejor respuesta
 

Una función no corutínea no puede esperar una función de coroutine. Si intenta forzar esto, entonces tendrá que hacerlo con una variable booleana en la función Update cada cuadro. No sugeriría eso. Hacer una solicitud web con el WWW API toma varios cuadros o menos. Esto significa que la llamada mom0 aún no ha terminado o devuelto antes de intentar acceder al valor.

En su caso, debe realizar los siguientes cambios:

1 . Tienes que hacer la función mom11111> mom1111. para que pueda esperar la función buildscript { dependencies { classpath ... } }12 hasta que haya terminado. Esto se hace con la Declaración buildscript { dependencies { classpath ... } }13 .

2 . Para configurar el objeto en un parámetro en una función de coroutine Use mom4 . En este caso, mom5 es apropiado.

3 . Cambie la función buildscript { dependencies { classpath ... } }16 a una función de coroutine. Si tu puedes hacerlo. Es una de las pocas funciones de devolución de llamada de Unity que se pueden hacer en una función de coroutine. Tenga en cuenta que es mom7 NO mom8 Como pone en el código de su pregunta.

su nuevo mom9 clase:

  momd0  

su nuevo momd1 clase:

  momd2  
 

A non coroutine function cannot wait for a coroutine function. If you try to force this then you would need to do that with a boolean variable in the Update function every frame. I wouldn't suggest that. Making a web request with the WWW API takes several frames or so. This means that the GetEnemyFromDB function call has not yet finished or returned before you tried to access the value.

In your case, you have to make the following changes:

1. You have to make the GetEnemy function a coroutine function so that you can wait for the GetEnemyFromDB function until it's done. This is done with the yield return statement.

2. To set Object in a parameter in a coroutine function use Action. In this case, Action<Enemy> enemyResult is appropriate.

3. Change the Start function to a coroutine function. Yes, you can do that. It's one of the few Unity's callback functions that can be made into a coroutine function. Note that it's Start not start as you put in the code from your question.

Your new BattleManager class:

public class BattleManager : MonoBehaviour {      public Enemy debugEnemy;      IEnumerator Start()     {         //get a reference to the DBAccess         DBAccess DBA = GameObject.Find("ManagerDB").GetComponent<DBAccess>();         yield return StartCoroutine(DBA.GetEnemy(1, (result) => { debugEnemy = result; }));          //YOU CAN NOW USE debugEnemy below      } } 

Your new DBAccess class:

public class DBAccess : MonoBehaviour {     public IEnumerator GetEnemy(int EnemyID, Action<Enemy> enemyResult)     {         yield return StartCoroutine(GetEnemyFromDB(EnemyID, enemyResult));     }      private IEnumerator GetEnemyFromDB(int EnemyID, Action<Enemy> enemyResult)     {         WWWForm postData = new WWWForm();         postData.AddField("EnemyID", EnemyID);          WWW dbProc = new WWW(GetEnemyURL, postData);         yield return dbProc;          if (string.IsNullOrEmpty(dbProc.error)) //error is null or empty so: SUCCESS!         {             string jsonstring = "{\"Items\":" + dbProc.text + "}";              Enemy[] EnemiesFromDB;             EnemiesFromDB = JsonHelper.FromJson<Enemy>(jsonstring);              if (EnemiesFromDB.Length > 0)             {                 //Pass result back to param                 if (enemyResult != null)                     enemyResult(EnemiesFromDB[0]);                  //HERE enemy.EnemyID is 1 and enemy.Name is "Evil Enemy Monster Man!"                 //So it is working here!             }             else throw new System.Exception("No Enemy Found When Reading Json: " + JsonUtility.FromJson<Enemy>(jsonstring));          }         else throw new System.Exception("DB ERROR: " + dbProc.error);     } } 
 
 
         
         
0
 
vote

Necesita entender que laCorutina no bloquea la ejecución, el problema está aquí:

  momd3  

Necesitas cambiarlo a:

  momd4  

y use de esta manera:

  momd5  
 

You need to understand that StartCorutine does not block execution, problem is here:

 public Enemy GetEnemy(int EnemyID)  {    enemy = new Enemy();    StartCoroutine(GetEnemyFromDB(EnemyID)); //this is executed asynchronously    return enemy;  } 

you need to change it to:

public void GetEnemy(int EnemyID, Action<Enemy> callback) {     StartCoroutine(GetEnemyFromDB(EnemyID,callback)); }  private IEnumerator GetEnemyFromDB(int EnemyID, Action<Enemy> callback) {     WWWForm postData = new WWWForm();     postData.AddField("EnemyID", EnemyID);      WWW dbProc = new WWW(GetEnemyURL, postData);     yield return dbProc; //code below is executed later, after after receiving the response from the server      if (string.IsNullOrEmpty(dbProc.error)) //error is null or empty so: SUCCESS!     {         string jsonstring = "{\"Items\":" + dbProc.text + "}";         Enemy[] EnemiesFromDB;         EnemiesFromDB = JsonHelper.FromJson<Enemy>(jsonstring);         if (EnemiesFromDB.Length > 0)         {             var enemy = EnemiesFromDB[0];             callback(enemy); //return enemy             yield break;         }         else             Debug.LogError("Enemy does not exist");     }     else         Debug.LogError("WWW request failed: " + dbProc.error);     callback(null); //call empty calbback to inform that something has failed  }  

and use that this way:

public class BattleManager : Monobehaviour{   public Enemy debugEnemy;   void Start()  {    //get a reference to the DBAccess    DBA = GameObject.Find("ManagerDB").GetComponent<DBAccess>();     debugEnemy = DBA.GetEnemy(1,(e)=>{       if(e!=null)       {       //Do something with enemy here, it will be couple frames later       }    });  } } 
 
 

Relacionados problema

6  Implementando un enumerador bidireccional en C #  ( Implementing a bidirectional enumerator in c sharp ) 
¿Hay una manera de usar los bloques de rendimiento para implementar un IEnumerator<T> que puede ir hacia atrás ( 9988777663 ), así como hacia adelante? ...

0  ¿El iEnumerator de .NET ILIST proporciona el orden de la lista?  ( Does the ienumerator of net ilist provide the order of the list ) 
¿Puedo asumir que el IEnumerator obtengo de un IList (llamando al método lodash2 del IEnumerable Interfaz) me dará los artículos en el orden de la lis...

0  VB.NET IEnumerator (de entero, MyClass) ¿No es posible?  ( Vb net ienumeratorof integer myclass not possible ) 
¿Cómo devuelvo un IEnumerator de un SortedDictionary(Of Integer, MyClass) tengo esto. Dim dictionaryTest As New SortedDictionary(Of Integer, MyClass)...

22  ¿Puedo tener un método que devuelve el iEnumerator <T> y utilícelo en un bucle forach?  ( Can i have a method returning ienumeratort and use it in a foreach loop ) 
Necesito establecer la altura de cada cuadro de texto en mi forma, algunos de los cuales están anidados en otros controles. Pensé que podría hacer algo así: ...

4  Enumerador como argumento  ( Enumerator as an argument ) 
De acuerdo, vamos a empezar con este botón muy simple, haga clic en Método private void button1_Click(object sender, EventArgs e) { int cou...

0  Recuperando el tipo original de un iEnumerator  ( Retrieving the original type from an ienumerator ) 
Ejemplo: puedo escribir un método que realice un rendimiento de rendimiento en cada elemento en una lista, como este: IEnumerator test1() { List<int> l...

0  Error al implementar IEnumerator  ( Error while implementing ienumerator ) 
He seguido el artículo aquí y El código de muestra dado en él. Lo que estoy tratando de implementar es bastante simple. Tengo un entendimiento justo sobre...

5  Tener problemas para refactorar un método de iEnumerator con múltiples rendimientos  ( Having trouble refactoring an ienumerator method with multiple yields ) 
La esencia de mi código es la siguiente: // Play the first beat audio.PlayOneShot(beat); // Show 1st heartbeat border flash TweenAlpha.Begin(heartbeatPane...

1  ¿Cómo escoge la carpeta creada más recientemente utilizando el contenedor de bucle forach en el paquete SSIS?  ( How do i pick the most recently created folder using foreach loop container in s ) 
Tengo un desafío interesante con SSIS. Usando un enumerador de archivos para cada archivo, necesito elegir la subcarpeta que ha sido creada más recientemente,...

0  ¿Cómo puedo extender iEnumerable <T> para cargar mi colección de un servicio web?  ( How can i extend ienumerablet to load my collection from a web service ) 
He estado tratando de rompecificar cómo podría implementar Enumerable<T> / Auth::user()->? 0 Para satisfacer lo siguiente: Llame un servicio web para c...




© 2022 respuesta.top Reservados todos los derechos. Centro de preguntas y respuestas reservados todos los derechos