我正在尝试从数据库中创建另一个类使用的对象。 但是由于IEnumerator的响应延迟(可能是?),似乎存在问题。
我们有一个非常简单的敌人类,可以将json解析为:
[System.Serializable]
public class Enemy{
public string EnemyID;
public string Name;
}
BattleManager附加到场景中的对象
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?
}
}
现在这是我从数据库中获取信息的地方。除了GetEnemy返回默认的敌人对象,而不是从IEnumerator GetEnemyFromDB中的json加载变量的对象,该方法似乎一切正常。
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);
}
}
我在stackoverflow上找到了这个帮助器类。我忘记了在哪里或应该给予应有的荣誉,但是效果很好!
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;
}
}
超级沮丧,我想我已经阅读了IEnumerator上的一百万篇文章。 我希望我可以让IEnumerator方法返回我的Enemy对象,而不是让私有敌人作为受IEnumerator影响并由getter方法返回的变量。
非常感谢您的帮助!
答案 0 :(得分:3)
非协程函数不能等待协程函数。如果尝试强制执行此操作,则需要在每一帧的Update
函数中使用布尔变量来执行此操作。我不建议那样。使用WWW
API发出Web请求大约需要几帧。这意味着GetEnemyFromDB
函数调用在您尝试访问该值之前尚未完成或返回。
对于您而言,您必须进行以下更改:
1 。您必须使GetEnemy
函数成为协程函数,以便您可以等待GetEnemyFromDB
函数直到完成。这是通过yield return
语句完成的。
2 。要在协程函数的参数中设置对象,请使用Action
。在这种情况下,Action<Enemy> enemyResult
是合适的。
3 。将Start
函数更改为协程函数。是的,你可以这么做。它是少数几个可以变成协程函数的回调函数之一。请注意,在您输入问题代码时,它是Start
而不是start
。
您的新BattleManager
类:
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
}
}
您的新DBAccess
类:
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);
}
}
答案 1 :(得分:0)
您需要了解StartCorutine不会阻止执行,问题出在这里:
public Enemy GetEnemy(int EnemyID)
{
enemy = new Enemy();
StartCoroutine(GetEnemyFromDB(EnemyID)); //this is executed asynchronously
return enemy;
}
您需要将其更改为:
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
}
并以这种方式使用它:
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
}
});
}
}