Unity 使用 IENumerator 设置另一个对象将使用的 class
Unity Using IENumerator to set a class that another object will use
我正在尝试从我的数据库中创建另一个 class 使用的对象。
但是由于 IEnumerator 的延迟响应(也许?),似乎存在问题。
我们有一个非常简单的敌人 class 将 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 return 是一个默认的 Enemy 对象,而不是从 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);
}
}
我在 Whosebug 上找到了这个助手 class。我忘了在哪里,否则我会给予适当的信任,但效果很好!
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 方法 return 我的 Enemy 对象,而不是将私人敌人作为受 IEnumerator 影响和 return 由 getter 方法编辑的变量。
非常感谢您的帮助!
非协程函数不能等待协程函数。如果你试图强制这样做,那么你需要在每一帧的 Update
函数中使用一个布尔变量来做到这一点。我不建议那样做。使用 WWW
API 发出网络请求需要几帧左右。这意味着在您尝试访问该值之前 GetEnemyFromDB
函数调用尚未完成或返回。
对于您的情况,您必须进行以下更改:
1。您必须使 GetEnemy
函数成为协程函数,以便您可以等待 GetEnemyFromDB
函数完成。这是通过 yield return
语句完成的。
2。要在协程函数的参数中设置对象,请使用 Action
。在这种情况下,Action<Enemy> enemyResult
是合适的。
3。将 Start
函数更改为协程函数。是的,你可以这样做。它是 Unity 为数不多的可以做成协程函数的回调函数之一。请注意,它是 Start
而不是 start
,因为您在问题中输入了代码。
你的新 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
}
}
你的新 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);
}
}
你需要明白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
}
});
}
}
我正在尝试从我的数据库中创建另一个 class 使用的对象。 但是由于 IEnumerator 的延迟响应(也许?),似乎存在问题。
我们有一个非常简单的敌人 class 将 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 return 是一个默认的 Enemy 对象,而不是从 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);
}
}
我在 Whosebug 上找到了这个助手 class。我忘了在哪里,否则我会给予适当的信任,但效果很好!
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 方法 return 我的 Enemy 对象,而不是将私人敌人作为受 IEnumerator 影响和 return 由 getter 方法编辑的变量。
非常感谢您的帮助!
非协程函数不能等待协程函数。如果你试图强制这样做,那么你需要在每一帧的 Update
函数中使用一个布尔变量来做到这一点。我不建议那样做。使用 WWW
API 发出网络请求需要几帧左右。这意味着在您尝试访问该值之前 GetEnemyFromDB
函数调用尚未完成或返回。
对于您的情况,您必须进行以下更改:
1。您必须使 GetEnemy
函数成为协程函数,以便您可以等待 GetEnemyFromDB
函数完成。这是通过 yield return
语句完成的。
2。要在协程函数的参数中设置对象,请使用 Action
。在这种情况下,Action<Enemy> enemyResult
是合适的。
3。将 Start
函数更改为协程函数。是的,你可以这样做。它是 Unity 为数不多的可以做成协程函数的回调函数之一。请注意,它是 Start
而不是 start
,因为您在问题中输入了代码。
你的新 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
}
}
你的新 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);
}
}
你需要明白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
}
});
}
}