我正在努力让我的事件系统在统一中工作。我正在使用Nakama服务器制作在线回合制游戏。
Nakama 服务器使用事件来通知客户端。我有一个MatchClient.cs
脚本来处理连接到服务器和匹配。当匹配器匹配玩家并且游戏准备好开始时,此脚本将调用MatchReady
(UnityEvent)。
MatchEventBroadcastManager.cs
脚本处理服务器发送到客户端的匹配事件。游戏特定的事件在此脚本中声明(例如其他脚本订阅的OnRivalMove等)它有一个MatchReady()
函数,当调用MatchClient的MatchReady
事件时调用该函数。
到目前为止,一切正常。
这是MatchEventBroadcastManager.cs
脚本的一部分:
public delegate void MatchStartedEvent();
public static event MatchStartedEvent OnMatchStart;
public void MatchReady()
{
//Handle notifications from the server
_client.Socket.OnNotification += (_, notification) =>
{
switch (notification.Code)
{
case 1:
OnMatchStart?.Invoke();
break;
}
};
}
我尝试使用另一个 UnityEvent 进行OnMatchStart
,但它不会触发检查器中引用的任何函数(我相信这可能与事件的嵌套有关,但不确定)。
这需要在事件中,因为在玩家匹配之前我无法订阅Socket.OnNotification
事件(并且Socket
不为空)。
在这一点上,我们有:
- Nakama事件通知MatchClient玩家已匹配。
- MatchClient 通知 MatchEventBroadcastManager(以及其他脚本)匹配现已准备就绪。
- MatchEventBroadcastManager 订阅服务器的 OnNotification 事件。
- 当此事件触发时,将调用 OnMatchStart。
到目前为止,一切似乎都正常工作。
直到
我有一个处理等待屏幕的WaitingScreenMenu.cs
脚本(显然)。
它订阅OnMatchStart
事件,只想转换到下一个菜单。
private void Start()
{
MatchEventBroadcastManager.OnMatchStart += MatchStarted;
Debug.Log("Menu manager: ");
Debug.Log(_menuManager.name);
}
public void MatchStarted()
{
Debug.Log("Menu manager: ");
Debug.Log(_menuManager.name);
Debug.Log("Test after");
}
我删除了到下一个菜单的过渡,只是尝试访问外部对象进行测试,但这是执行挂起的点。
在游戏启动时的调试输出中_menuManager.name
正确打印到控制台。
当调用 MatchStarted 时,"Menu manager: "
会打印出来,但不打印其他内容。
如果我注释掉Debug.Log(_menuManager.name);
,那么它会打印:
"Menu manager: "
"Test after"
我尝试过其他脚本,结果相同。我从一个订阅到非嵌套事件且运行正常的函数中调用了该函数。
我试图将游戏逻辑分成可管理的部分,但似乎处理事件的唯一方法是让MatchClient.cs
处理所有事情,并处理巨大而笨拙的混乱。
在事件方面,Unity 真的以这种方式受到限制还是我错过了什么?
提前感谢您的帮助。
担
使用 Visual Studio 调试器发现了错误。在事件中中断并检查_menuManager
的值显示了 UnityException。UnityException: get_gameObject can only be called from the main thread.
正在从异步方法调用事件。查看它,我发现您无法从单独的线程与 UnityEngine 进行通信。
我没有直接在Socket.OnNotification
事件中调用我的事件,而是设置了一个在Update()
中签入的标志,该标志随后在主线程上调用我的事件。
MatchEventBroadcastManager.cs
脚本的替换部分:
private bool notifMatchStarted;
public UnityEvent MatchStarted;
void Update()
{
//Invoke events here to guarantee they are called from the main thread.
if(notifMatchStarted)
{
MatchStarted.Invoke();
notifMatchStarted = false;
}
}
public void MatchReady()
{
//Handle notifications from the server
_client.Socket.OnNotification += (_, notification) =>
{
switch (notification.Code)
{
case 1:
notifMatchStarted = true;
break;
}
};
}