我正在开发团队比赛游戏。每支队伍都有自己的重生点,目标是摧毁另一支队伍的重生点。spawn 以 3 个对象的形式在网络中设置 - 一个实际 spawn(稍后称为物理 spawn),仅对服务器可见,一个名为MapManager的对象存在于客户端和服务器中,它充当通过Rpc 调用和一个简单的 spawn 图形(稍后称为视觉 spawn),呈现在客户端和服务器的本地播放器上。
每个对象的 NetworkIdentity 设置分别为:
- 仅服务器,
- 本地玩家权威,
- 无 - 是一种 MonoBehaviour。
物理 spawn判断是否对 spawn 造成了任何伤害,如果是,则将标志isHealthChanged设置为true。
public class SpawnerServer : NetworkBehaviour {
//used to determine whether has the health changed
bool isHealthChanged = false;
//indicates the team of this spawner via enum state
public Data.teams myTeamEnum;
...
public RectTransform healthBar;
private void OnTriggerEnter2D(Collider2D collision)
{
//Checking whether the bullet wll damage the spawn
OnChangeHealth(healthPoints);
}
//if the health changed, change the healthbar as well
public void OnChangeHealth(float healthPoints)
{
if (isServer)
{
...
isHealthChanged = true;
}
}
//returns the deltaSize of the healthbar
public Vector2 GetHealthbarSizeDelta()
{
return healthBar.sizeDelta;
}
//switches the healthChanged bool back to false. Called by the MapManager
public void UncheckHealthChanged()
{
isHealthChanged = false;
}
//returns the state of the healthChanged bool
public bool IsHealthChanged()
{
return isHealthChanged;
}
}
MapManager在物理生成(仅在服务器上检查)和检测到真值时检查标志的状态:
- 将标志重置为false,
- 获取健康栏的宽度,
将有关健康栏的新宽度和生成数量的信息传递给 RPC 调用RpcChangeSpawnersHealthClients(),该调用调用负责更新视觉生成的健康栏转换数据的函数。
public class ClientSpawnControl : NetworkBehaviour { //the references to scripts of the server spawns, required for checking whether has //the state of any of the spawns changed public SpawnerServer serverSpawn1; public SpawnerServer serverSpawn2; //the references to spawns that belong to the clients public SpawnerClient clientSpawn1; public SpawnerClient clientSpawn2; //acts as a messenger that forces change of spawn healthbars in clients [ClientRpc] private void RpcChangeSpawnersHealthClients(int whatTeam, float healthChangeDelta) { if(clientSpawn1 != null) { if (whatTeam == (int)clientSpawn1.myTeam) { clientSpawn1.OnChangeHealth(healthChangeDelta); return; } } if(clientSpawn2 != null) { if (whatTeam == (int)clientSpawn2.myTeam) { clientSpawn2.OnChangeHealth(healthChangeDelta); return; } } } // Update is called once per frame void Update () { if (!isServer) return; if(serverSpawn1.IsHealthChanged()) { serverSpawn1.UncheckHealthChanged(); RpcChangeSpawnersHealthClients((int)serverSpawn1.myTeamEnum, serverSpawn1.GetHealthbarSizeDelta().x); } if (serverSpawn2.IsHealthChanged()) { serverSpawn2.UncheckHealthChanged(); RpcChangeSpawnersHealthClients((int)serverSpawn2.myTeamEnum, serverSpawn2.GetHealthbarSizeDelta().x); } }
这是引发异常的地方:“在 TeamSelector (UnityEngine.GameObject) 上未发现传入 [ClientRpc:InvokeRpcRpcChangeSpawnersHealthClients] 的行为,服务器和客户端应具有相同的 NetworkBehaviour 实例......”
该错误非常有趣,因为提到的TeamSelector实际上与生成器更新无关。一开始就是用来初始化球员的球队数据的,仅此而已。甚至更多 - 一个物理生成物也以某种方式在客户端上生成,但如果我没记错的话,仅服务器选项应该可以防止这种行为,而且我不会尝试通过网络创建对象。
早些时候,我尝试生成带有NetworkBehaviour和NetworkIdentity的视觉生成物——即使我在网络管理器中注册了这些对象,甚至使用了 ClientScene.RegisterPrefab()方法,也无法在客户端生成这些对象。使这两者的每一种可能的组合 - 情况没有改变。后来我将视觉生成重新排列为它们当前的形式,并尝试在它们检测到健康变化时立即从物理生成中调用MapManager的 RPC 调用。在客户端引发了与上面引用的类似的异常,但名称为物理生成在TeamSelector对象的位置。搜索解决方案没有找到任何可行的方法,例如,我没有任何具有重复名称但属于不同名称空间的方法。
我什至不知道在哪里寻找问题的根源。在与 Unity 争论超过 3 天后,我觉得它只是不想让我创建那个游戏。有谁知道什么会导致问题?
对于这篇文章的长度,我感到非常抱歉,但我不知道如何用更短的方式描述这个问题。