适配
- 项目引用了navMesh库,2022版本已经内置到引擎,因此升级后要删掉库引用
- auth库的2.4.0有bug,编辑器环境下profile检查会抛异常,导致编辑器环境不能使用lobby,仿照官方,降到2.3.1版本
- 不清楚为什么打出来的包不能跟编辑器联机,貌似编出来的是release版,没找到改参数的地方,多人调试只能用parrelSync
参考文档
知乎其他人经验 这个人的代码老旧,部分已经对不上,如角色网络对象和显示分离做法已经变了
初始化
在StartUp做游戏初始化,然后再到开始场景
切换场景不销毁的对象,都用了DontDestroyOnLoad
NetworkManager是NetCode的控制器,挂在全局对象上
ConnectionManager是BossRoom自己写的组件,声明并且注入了游戏状态机,游戏状态改变时调ChangeState
ClientConnectingState 进入这个状态会起一个异步任务,连接服务器ConnectClientAsync
玩家连接,断开服务器,都通过这个管理
GameDataSource 里存行为列表
using Action = Unity.BossRoom.Gameplay.Actions.Action;
[Tooltip("All Action prototype scriptable objects should be slotted in here")]
[SerializeField]
// 这就是BossRoom的所有技能,都做成asset配置,存到这里了
private Action[] m_ActionPrototypes;
SceneLoader里SceneLoaderWrapper是BossRoom实现的类
NetCode自带了一个场景管理NetworkSceneManager,并且官方希望刚开始用NetCode的人都要用这个场景管理
OnSceneEvent附带挺多事件的,加载过程根据这几个状态做进度可视化,直到SynchronizeComplete才做逻辑初始化,可避免掉虚空
玩家
对服务器而言玩家只是个网络连接,对客户端而言玩游戏玩的是输入控制
玩家不等于游戏角色,只不过玩家的输入控制,影响了游戏角色
每个网络连接会绑定一个永久的玩家Prefab,BossRoom里叫PersistentPlayer,它拥有NetworkObject组件,定义在NetworkManager里面,这个东西不销毁
Startup场景初始化游戏,没有角色也不需要输入控制
MainMenu场景已经初始化了游戏,需要指定服务器,输入控制实际上控制的是配置和网络连接,没角色
CharSelect场景连上了服务器,但是也不需要角色,输入控制就是组队的UI交互
BossRoom场景才真正有了游戏角色,根据网络连接创建角色通知客户端,客户端再判断是不是玩家,是不是自己,如果是自己,就绑定输入控制
void OnLoadEventCompleted(string sceneName, LoadSceneMode loadSceneMode, List<ulong> clientsCompleted, List<ulong> clientsTimedOut)
{
if (!InitialSpawnDone && loadSceneMode == LoadSceneMode.Single)
{
InitialSpawnDone = true;
// 为每个网络连接创建游戏角色并初始化位置
foreach (var kvp in NetworkManager.Singleton.ConnectedClients)
{
//ServerBossRoomState定义了PlayerPrefab,创建玩家角色的时候就创建这个prefab
SpawnPlayer(kvp.Key, false);
}
}
}
// 上面的SpawnPlayer常规执行创建,关键是最后
// NetworkObject创建好,要调用SpawnWithOwnership,把自己注册进NetworkSpawnManager,注册完成会回调各个组件的OnNetworkSpawn做初始化,游戏就可以开始了
// spawn players characters with destroyWithScene = true
newPlayer.SpawnWithOwnership(clientId, true);
角色信息的传递
guid不是guide,不是引导!!!!是职业ID
NetworkAvatarGuidState的作用根据guid初始化角色信息,因此它专门建了个asset配置AvatarRegistry,管理所有的角色配置
/// <summary>
/// NetworkBehaviour component to send/receive GUIDs from server to clients.
/// </summary>
public class NetworkAvatarGuidState : NetworkBehaviour
{
//这个GUID会随机初始化,然后选角色的后更新
[FormerlySerializedAs("AvatarGuidArray")]
[HideInInspector]
public NetworkVariable<NetworkGuid> AvatarGuid = new NetworkVariable<NetworkGuid>();
[SerializeField]
AvatarRegistry m_AvatarRegistry;
}
//它被声明在PersistentPlayer里面,先是初始化随机一个值
[RequireComponent(typeof(NetworkObject))]
public class PersistentPlayer : NetworkBehaviour
{
[SerializeField]
PersistentPlayerRuntimeCollection m_PersistentPlayerRuntimeCollection;
[SerializeField]
NetworkNameState m_NetworkNameState;
[SerializeField]
NetworkAvatarGuidState m_NetworkAvatarGuidState;
public NetworkNameState NetworkNameState => m_NetworkNameState;
public NetworkAvatarGuidState NetworkAvatarGuidState => m_NetworkAvatarGuidState;
}
// 选角色完成的时候会把角色信息存进去,由此可见,这个persistentPlayer,就是用来玩家数据并传递的
void SaveLobbyResults()
{
foreach (NetworkCharSelection.LobbyPlayerState playerInfo in networkCharSelectioLobbyPlayers)
{
var playerNetworkObject = NetworkManager.Singleton.SpawnManageGetPlayerNetworkObject(playerInfo.ClientId)
if (playerNetworkObject && playerNetworkObject.TryGetComponent(ouPersistentPlayer<