所以我想创建一个包含一堆静态方法的对象。这些方法是远程服务器的API。我正在阅读并认为我可以使用统一StartCoroutine
方法,但在这种情况下不可用,所以现在我不知道该去哪里。
一般的想法是我希望能够调用我的一个对象的方法,传递一个委托并让id去做它的工作。完成后,使用结果调用委托。我不能使用线程,因为Unity3D不是线程安全的。
我知道c#有这个yield
的东西,我已经读了好几个地方,但它仍然让我感到困惑。我如何重构下面的代码,以便完成我正在尝试做的事情?
public class Server
{
private static string baseURL = "http://localhost/game.php";
private static Hashtable session_ident = new Hashtable();
//--- Public API
public delegate void DeviceSeenCallback(bool seen);
public static void DeviceSeen(DeviceSeenCallback callBack) {
StartCoroutine(DoDeviceSeen(callBack));
}
public delegate void AuthenticateCallback(bool authenticated, string errorMessage);
public static void Authenticate(string username, string passwordHash, AuthenticateCallback callBack) {
StartCoroutine(DoAuthenticate(username, passwordHash, callBack));
}
//--- Private API
private static IEnumerator DoDeviceSeen(DeviceSeenCallback callBack)
{
WWWForm form = new WWWForm();
form.AddField("deviceID", SystemInfo.deviceUniqueIdentifier);
WWW www = new WWW(baseURL + "?cms=seen", form.data, session_ident);
yield return www;
// Check for errors
callBack(ResultIsOk(www.text));
}
private static IEnumerator DoAuthenticate(string username, string passwordHash, AuthenticateCallback callBack)
{
WWWForm form = new WWWForm();
form.AddField("deviceID", SystemInfo.deviceUniqueIdentifier);
form.AddField("deviceType", SystemInfo.deviceType.ToString() + "||" + SystemInfo.deviceModel);
form.AddField("user", username);
form.AddField("pass", passwordHash);
WWW www = new WWW(baseURL + "?cms=auth", form.data, session_ident);
yield return www;
if (ResultIsOk(www.text)) {
callBack(true, "");
} else {
int code;
string message;
ResultGetError(www.text, code, message);
callBack(false, message);
}
}
private static bool ResultIsOk(string resultText) {
return false;
}
private static void ResultGetError(string resultText, out int code, out string message) {
code = -1;
message = "Some Error Message";
}
}
答案 0 :(得分:3)
你总是遇到Unity的诀窍,就是你的代码运行的唯一上下文是在脚本回调期间。
这基本上意味着最终在某个地方,你必须在场景中有一个GameObject,其中MonoBehaviour附加到所谓的对象上,该对象调用你想要重复运行的代码。
除了从MonoBehaviour继承的类的实例之外没有可用的StartCoroutine这样的事实,你有一个问题,你需要让代码执行每一帧左右来检查一些东西。除了制作MonoBehaviour之外,没有其他方法可以解决这个问题。只需拥抱它即可。
一种常见的模式是创建一个单独的GameObject,它可以在其上调用DontDestroyOnLoad,也可以根据需要为每个场景重新创建。您甚至可以将一个对象指定为“coroutine bucket”,它不依赖于特定的类或任何东西。字面上是一个空洞的MonoBehaviour,其唯一目的就是开始协同程序。
您可以通过将Server类设置为MonoBehaviour来快速解决此问题:
然后添加一个单例属性:
private static Server ServerObject {
get {
if (_serverObject == null) {
var gameObj = new GameObject("Server Object");
_serverObject = gameObj.AddComponent<Server>();
GameObject.DontDestroyOnLoad(_serverObject); // Optional
}
return _serverObject;
}
}
private static Server _serverObject;
(单身人士很粗暴,但在这种情况下这是必要的邪恶)
然后更改要在单身MonoBehaviour实例上调用的所有StartCoroutine
次调用:
public static void DeviceSeen(DeviceSeenCallback callBack) {
this.ServerObject.StartCoroutine(DoDeviceSeen(callBack));
}
GoKit使用此模式并生成一个名为“Go”的GameObject,它处理补间的所有逻辑。由于Unity没有提供一种方法来指定经常运行而不附加到游戏对象的脚本,因此这是我们能做的最好的。
如果您的服务器类会保持很多状态,或者它不是必不可少的,那么建议您不要在其上调用DontDestroyOnLoad
。如果场景发生更改并删除了ServerObject游戏对象,则对_serverObject的后续检查将显示为null,从而进行重新实例化(因为Unity会重置空转换运算符)。
答案 1 :(得分:0)
对于统一,您通常必须进行手动线程处理(除非您的线程不对Unity API进行任何调用)。也就是说,例如, Authenticate
推送一个函数,该函数轮询其结果(无阻塞调用!)到私有静态列表上。然后提供一个静态Update
方法,该方法遍历列表并调用每个轮询方法,如果它指示它已完成则删除它。当每个轮询方法完成时,它应该调用相关的回调。安排让您的应用程序定期调用此静态Update
方法(例如,在通常的统一更新传递中)。