云同步游戏流程详解
云同步游戏流程,从开始单播状态、到进入同玩游戏、到结束一局游戏,主要需要接入”初始化“、”请求匹配”、与“结束同玩“相关接口、相关事件。
本文对如何接入和使用这些接口和事件进行说明。
流程总览:
总流程图:
- 1.用户 A 和 B 分别”云同步初始化“。
- 2.都进入”单播状态“。
- 3.A 和 B 都调用 ”请求匹配”。
- a.匹配成功,系统会决定其中谁做房主(host 主机)、谁做加入的玩家(client 客机)。
- 4.A 和 B 都进入”同玩状态”。
- a.其中 A 做房主。
- b.其中 B 作为玩家,系统会将其做切流处理,加入到 A 的云游戏实例。
- 5.游戏对局结束后,调用 ”结束同玩”。
- ◦房主A,他是运行云游戏实例的主机,他负责调用 ”结束同玩”。
- ◦玩家B 结束同玩时,从A的实例断开退出。系统将其回流,返回到自己原来实例的单播状态。
- ◦房主A 结束同玩后,也回到单播状态。
- 6.A 和 B 都回到单播状态。
接口总览:
// 访问接口入口: ICloudSync.Instance; ICloudMatchManager MatchManager = ICloudSync.Instance.MatchManager; // 云同步初始化 ICloudSync.Instance.Init(...) // 请求匹配: MatchManager.RequestMatch(...) // 结束同玩: MatchManager.EndMatchGame(...)
事件总览:
// 监听事件:玩家加入/离开 ICloudSync.Instance.OnSeatPlayerJoined ICloudSync.Instance.OnSeatPlayerLeaving // 监听事件:匹配到了其他用户 MatchManager.OnMatchUsers // 监听事件:结束同玩了(返回单播) MatchManager.OnEndMatchEvent // 监听事件:云游戏即将销毁(Host主机关闭玩法、退出云游戏) ICloudSync.Instance.OnWillDestroy
前置:云同步初始化
前置需要完成云同步初始化。
初始化 Init
【示例代码】:云同步初始化:
private async Task<bool> InitCloudGame(string appId) { // 云同步初始化 Init // 传入实参:`new UDeviceFactory(viewProvider)` - 指定适配 UGUI 界面渲染。其中UDeviceFactory的构造函数传入实参:`viewProvider` - 引用当前场景中的`CloudViewProvider`。 var initResult = await ICloudSync.Instance.Init(appId, new UDeviceFactory(viewProvider)); if (!initResult.IsSuccess) { Debug.LogError($"Init 初始化失败,无法启动云游戏 {initResult.Message}"); return false; } return true; }
API参考文档:↗ICloudSync.Init
流程:请求匹配
流程图:
单播状态 → 匹配同玩 流程图:
- 1.前置:云同步初始化
- 2.添加事件监听
- 3.调用请求匹配:
RequestMatch
- a.[可选分支]:取消匹配
- 4.触发事件:
OnMatchUsers
匹配到了用户- a.用户A 收到匹配信息:自己作为房主,匹配到玩家B
- b.用户B 收到匹配信息:匹配到玩家A 作为房主,自己作为玩家。
- 5.系统自动完成:A做房主 & B切流,进入同玩
- a.其中,房主A
BeginHost
开始房主状态(系统自动完成)- b.其中,其他玩家B
SwitchTo
切流到房主A的实例(系统自动完成)- 6.
RequestMatch
返回匹配结果:IMatchResult
- a.成功
- b.已取消
- c.失败
- 7.触发事件:
OnSeatPlayerJoined
玩家加入了(玩家B)- 8.A与B进入同玩状态,进行联机游戏
1.初始化
上文章节 ”前置:云同步初始化“ 已说明。
2.添加事件监听
匹配同玩相关事件,有以下几个:
- 1.必要:玩家加入、离开
OnSeatPlayerJoined
, OnSeatPlayerLeaving
- 2.[可选]:匹配到了用户
OnMatchUsers
- 3.必要:结束同玩了
OnEndMatchEvent
【示例代码】:
// 添加事件监听 private void InitEvents() { // 监听事件:玩家加入/离开 ICloudSync.Instance.OnSeatPlayerJoined += OnPlayerJoined; ICloudSync.Instance.OnSeatPlayerLeaving += OnPlayerLeaving; MatchManager = ICloudSync.Instance.MatchManager; // 可选:监听事件:匹配到了其他用户 MatchManager.OnMatchUsers += OnMatchUsers; // 监听事件:结束同玩了(返回单播) MatchManager.OnEndMatchEvent += OnEndMatchEvent; }
3.请求匹配 RequestMatch
接口:
IMatchManager.RequestMatch
:请求匹配,使用系统自带提供的匹配池。 成功后进入多人同玩状态。
/// 开始匹配 private async void StartMatch() { MatchManager = ICloudSync.Instance.MatchManager; // 匹配配置,选择一个系统自带提供的匹配池 var matchConfig = new SimpleMatchConfig() { PoolType = SimpleMatchPoolType.P1v1, // 2人 1v1 MatchTag = "Demo-test-1.0" // 匹配标签,区分开用户 }; // 开始匹配,传入匹配配置。 成功后进入系统自动进入多人同玩状态。 var task = MatchManager.RequestMatch(matchConfig); var result = await task; // 等待结果 // todo: 处理结果:OnMatchResult(result); }
- •其中,匹配配置
SimpleMatchConfig
的字段含义说明:public class SimpleMatchConfig { /// 选择匹配池,对应一套匹配规则 public SimpleMatchPoolType PoolType; /// 匹配标签,区分开用户。 建议用不同值区分开测试环境"test"、线上环境"online",也可以用于分开不同版本"1.0","2.0"。 Example: 例如:"test-1.0", "online-2.0" public string MatchTag; }
- •其中,
PoolType
匹配池,提供以下几种:public enum SimpleMatchPoolType { /// 2人 1v1 P1v1, /// 4人 2v2 P2v2, /// 4人各自1队 P1x4, /// 3人各自1队 P1x3, }
- •其中,
MatchTag
匹配标签:- ◦开发者可自定义设置。只有相同标签的用户能匹配到。不同标签的用户会被区分开,不会匹配到一起。
- ◦建议:
- ▪用不同值区分开测试环境"test"、线上环境"online"
- ▪也可以用于分开不同版本"1.0","2.0"。
- ◦Example:
MatchTag
例如:"test-1.0"
, "online-2.0"
3.b 取消匹配
[可选]【示例代码】:2. 匹配、支持中途取消:
// 取消用的 CancellationTokenSource private CancellationTokenSource _matchCancel; /// 开始匹配 private async void StartMatch() { // 设置本地游戏状态,可自定义表现,例如:显示“匹配中”文字、Loading转圈、按钮置灰等等... SetState(State.Matching); // 创建取消用的 CancellationTokenSource,using使其自动Dispose using CancellationTokenSource cancel = _matchCancel = new CancellationTokenSource(); // 匹配配置,选择一个系统自带提供的匹配池 var matchConfig = new SimpleMatchConfig() { PoolType = SimpleMatchPoolType.P1v1, // 2人 1v1 MatchTag = "Demo-test-1.0" // 匹配标签,区分开用户 }; // 开始匹配,传入匹配配置,传入可取消用的cancel.Token。 成功后进入系统自动进入多人同玩状态。 var task = MatchManager.RequestMatch(matchConfig, cancel.Token); var result = await task; // 等待结果 _matchCancel = null; // 已出结果,不再需要取消,应把 TokenSource 引用置null // 处理匹配结果: OnMatchResult(result); } /// 主动取消匹配 private void CancelMatch() { if (_matchCancel != null) { // 主动取消。 若成功取消,`RequestMatch`返回结果的`IMatchResult.Code`会是`Cancelled` _matchCancel.Cancel(); // 取消后,应把 TokenSource 引用置null _matchCancel = null; } }
4.事件:匹配到了用户
收到了匹配到的用户是谁的信息。
可以做相关展示、信息提示。相应要做什么表现,这部分开发者可以自定义实现。
【示例代码】:
// 监听事件 MatchManager.OnMatchUsers += OnMatchUsers; private void OnMatchUsers(IMatchResult matchResult) { var isHost = matchResult.IsHost; var myIndex = matchResult.MyIndex; var users = matchResult.AllUsers; var opponents = users.Where(s => s.RoomIndex != myIndex).ToList(); var oppoNames = string.Join(", ", opponents.Select(s => s.Nickname).ToList()); ShowHostNote($"匹配到了用户: {oppoNames}"); }
5.系统自动:做房主 & 切流
如果上一步骤成功匹配到了用户,那么系统会自动完成:A做房主 & B切流,进入同玩。
其中,房主A
BeginHost
开始房主状态(系统自动完成):- ◦成功时,房主A 等待其他玩家连接加入。
- ◦如果发生开始房主失败,那么房主会收到匹配结果:
IMatchResult
的 Code 枚举为 Error: 错误。- ◦💡注:BeginHost 开始房主通常不会失败。只有例如当前状态无效、已经开始了房主状态,才可能错误。
其中,玩家B
SwitchTo
切流到房主A的实例(系统自动完成):- ◦成功时,玩家B 连接加入到房主A的实例去同玩。
- ◦如果发生切流失败,那么失败的那一方,会收到匹配结果:
IMatchResult
的 Code 枚举为 Error: 错误。- ◦💡注:SwitchTo 切流的操作,内部是由云游戏服务链路完成,如果偶现失败,常见的原因是网络波动、网络错误。
示意图:做房主 & 切流
6.匹配结果:IMatchResult
请求匹配的接口
MatchManager.RequestMatch
为异步方法,返回类型为 Task<IMatchResult>
。开发者应在
result = await task
等待到结果后,进行处 理。无论成功、失败、取消,都会返回结果。
- 1.若成功:
result.IsSuccess
true ,那么应当按result.IsHost
是否为true,区分我方是否房主:- a.若
IsHost
true 我方是房主,应负责加载启动本局游戏逻辑。 例如:应加载对局的游戏场景、界面UI等。
- b.若
IsHost
false 我方是要加入同玩的玩家,系统会切流加入到房主A的实例去同玩。 本地无需特殊处理。 [可选]:可以自定义展示内容,例如显示“与...同玩中”、显示“Loading”。
- c.成功时,从匹配结果
IMatchResult
中,还可以获取到本场匹配信息、对局的各个用户信息。 - ▪标识一场匹配的id
MatchId
;- ▪我的座位号
MyIndex
,房主用户HostUser
;- ▪各个玩家用户的列表
AllUsers
,队伍列表Teams
。
- 2.若失败:
!result.IsSuccess
,那么包含多个情况,应根据返回码:result.Code
枚举判断- ◦枚举
MatchResultCode
类型定义如下:- a.成功:Success
- b.已取消:Cancelled。(即:取消匹配成功)
- c.失败:
- i.Timeout: 超时。(即:超过了一定时间,没有匹配到)
- ii.Error: 错误。(例如发生网络错误,或切流失败等)
- iii.InvalidStateError: 当前状态无效。(例如:调用
RequestMatch
但已经在房主状态,或已切流加入到了他人房间)/// 处理匹配结果 private void OnMatchResult(IMatchResult result) { _matchResult = result; // 处理失败情况 if (!result.IsSuccess) { // 6.a 处理匹配失败 OnMatchFail(result); return; } var isHost = result.IsHost; Debug.Log($"StartMatch 匹配成功 isHost: {isHost}"); // 6.b 处理匹配成功 OnMatchSuccess(isHost); }
【示例代码】:处理匹配成功:
/// 6.a 处理匹配成功 private void OnMatchSuccess(bool isHost) { var result = _matchResult; if (isHost) { // 匹配成功后,若我方是房主,应负责加载启动本局游戏(包括例如加载游戏资源、切换场景、显示界面UI等)。 // 房主A SetState(State.InGameHost); // 从result中,可以获取到匹配结果信息内容,例如:标识一场匹配的id`MatchId`,我的座位号`MyIndex`,房主用户`HostUser`;各个玩家用户的列表`AllUsers`,Team列表`Teams` var playersCount = result.AllUsers.Count; game.LoadGame(new GameInfo { isPk = true, playersCount = playersCount, }); } else { // 若我方是要加入同玩的玩家,系统会切流加入到房主A的实例去同玩。 本地无需特殊处理,可以自定义显示,例如:显示“与...同玩中”、显示Loading。 // 玩家B SetState(State.InConnectOther); } }
【示例代码】:处理匹配取消或失败:
/// 6.b 处理匹配取消或失败 private void OnMatchFail(IMatchResult result) { var debugMsg = $"code: {result.Code} ({(int)result.Code}), msg: {result.Message}"; // 根据返回码:`result.Code`枚举判断 switch (result.Code) { case MatchResultCode.Success: break; case MatchResultCode.Cancelled: Debug.Log($"StartMatch 取消 {debugMsg}"); ShowHostNote("匹配已取消"); break; case MatchResultCode.Timeout: Debug.LogWarning($"StartMatch 匹配超时 {debugMsg}"); ShowHostNote("匹配超时,没有匹配到对手"); break; case MatchResultCode.Error: default: Debug.LogError($"StartMatch 匹配失败 {debugMsg}"); ShowHostNote($"匹配失败! {debugMsg}"); break; } // 重置本地游戏状态、界面,并返回,允许后续操作。 SetState(State.Ready); }
7.事件:玩家加入了
注意,房主需要特别关注其他玩家加入、离开的事件。
- •原因:在”房主A“这一侧,匹配成功后,而对方”玩家B“可能由于网络延迟、操作延迟等,时序上并不是完全相同时刻就加入进来,通常会稍晚一点。
- •⚠ 注意避免:房主自己收到匹配成功的结果后,还没有等待其他玩家的加入,自己就开始了游戏内容、走后续玩法逻辑了。
- •💡建议通常做法:
匹配成功后,若我方是房主:
- 1.先加载游戏资源、切换场景、显示界面UI等。
- 2.应当在等待和检查玩家加入后开局。 具体包括:
- a.等待和检查玩家B的加入事件
OnSeatPlayerJoined
- b.检查玩家加入到齐后,开始一局游戏玩法
- c.[可选]:若玩家B还未加入,可以显示例如”等待其他玩家...“
- d.[可选]:若玩家一直不加入、或加入后又退出,可以做自定义的处理,例如:可进行弹框提示,可提供给用户界面按钮,也可以提前结束本局同玩。
// 监听事件:玩家加入/离开 ICloudSync.Instance.OnSeatPlayerJoined += OnPlayerJoined; ICloudSync.Instance.OnSeatPlayerLeaving += OnPlayerLeaving; // 匹配成功 private void OnMatchSuccess(bool isHost) { if (isHost) { LoadGame(...); } } // 记录要参与本局的玩家座位列表 private List<ICloudSeat> _seats; // 2.加载游戏 public void LoadGame(...) { State = GameState.Loaded; // 记录要参与本局的玩家座位列表 _seats = GetSeatsForGame(_gameInfo); // todo: 自定义实现:加载游戏资源、切换场景、显示界面UI等... // 检查确认 CheckStartGame(); } // 示例:按来自gameInfo的玩家数量,记录对应玩家座位列表 private List<ICloudSeat> GetSeatsForGame(GameInfo gameInfo) { var userIndexes = new List<int> { 0 }; if (gameInfo.isPk) userIndexes = Enumerable.Range(0, gameInfo.playersCount).ToList(); var seatManager = ICloudSync.Instance.SeatManager; var seats = userIndexes.Select(u => seatManager.GetSeat((SeatIndex)u)).ToList(); return seats; } // 3.检查确认开始游戏 private void CheckStartGame() { if (State != GameState.Loaded) return; // 检查玩家加入到齐、确认OK后,开始正式一局游戏玩法。 if (ArePlayersJoined(seats)) { InitPlayerInfos(seats); // 开始正式一局游戏玩法 StartGame(); } } private bool ArePlayersJoined(List<ICloudSeat> seats) { var ret = seats.All(s => s is { State: SeatState.InUse }); Debug.Log($"Game ArePlayersJoined: {ret}"); return ret; } // 4.a 收到事件:玩家加入 private void OnPlayerJoined(ICloudSeat seat) { Debug.Log($"Game OnPlayerJoin seat index: {seat.Index}"); if (State == GameState.Loaded) CheckStartGame(); } /// 4.b 收到事件:玩家离开 private void OnPlayerLeaving(ICloudSeat seat) { if (State != GameState.Started) return; if (!_gameInfo.isPk) return; CheckGameEnd(); }
流程:结束同玩
流程图:
同玩中 → 结束同玩流程图:
- 1.调用结束同玩:
EndMatchGame
- ◦默认:所有人结束同玩
- ◦可选:
EndMatchGame(SeatIndex)
踢掉单个,指定让单个用户退出 - ◦返回:成功或失败
- 2.触发事件:
OnEndMatchEvent
结束同玩了(返回单播)- a.玩家B,回流到自己实例后,收到的事件参数中, EndType = EndMatchGame
- b.房主A,结束同玩相关操作结束后,收到的事件参数中, EndType = EndHostState
- 3.触发事件:
OnSeatPlayerLeaving
玩家 B 离开
1.结束同玩 EndMatchGame
接口:
IMatchManager.EndMatchGame
:结束同玩:让所有人、或让指定座位号的玩家,回到单播状态。
方法有多种重载:
Task<IEndResult> EndMatchGame(string endInfo = "") | 结束同玩:所有人回到单播状态。 |
Task<IEndResult> EndMatchGame(InfoMapping infoMapping) | 结束同玩:所有人回到单播状态。可以给每个用户分别发送不同的结束信息。 |
Task<IEndResult> EndMatchGame(SeatIndex seatIndex, string endInfo = "") | 指定座位号,单个用户结束同玩:使他退出、回到单播状态。 |
- •参数 endInfo :结束信息,用json透传,传递给同玩的用户
- •参数 infoMapping:结束信息映射,可以给每个用户分别发送不同的结束信息
- •参数 SeatIndex:可以指定座位号,单个用户结束同玩(使他退出、回到单播状态)
- •返回 IEndResult:结束同玩结果。包含:
- ◦bool IsSuccess 是否成功
- ◦EndResultCode Code 返回码
- ◦string Message 返回信息、错误信息
【使用说明】:
- •当 用户A 是房主,用户B 是加入的玩家:
- •同玩一局游戏结束,显示结算界面,本局分数情况。 界面中显示按钮【退出】
- •当点击【退出】时,应区分是否房主,选择如何结束同玩:
- •若是房主A 点击【退出】,建议:调用
EndMatchGame(string endInfo)
结束同玩:所有人(不指定座位号),endInfo
传入自定义结束信息- ◦玩家B 会退出同玩、回流,到自己的原实例,进入单播状态。
- ◦玩家B 会收到
OnEndMatchEvent
事件,事件参数中, EndType = EndMatchGame,表示结束同玩了,且事件会携带结束信息 endInfo 。- ◦房主A 在相关操作成功后,也会结束房主状态,进入单播状态。
- ◦房主A 也会收到
OnEndMatchEvent
事件,事件参数中, EndType = EndHostState,表示结束房主状态了。- ◦注:相关说明
- ▪调用
EndMatchGame
,是在A实例的玩法进程中调用- ▪玩家B 回流:玩家B会断开从A实例的拉流。此时单播状态表现为:单播自己的画面且不再从A拉流画面。
- ▪玩家B 收到的
OnEndMatchEvent
事件参数中携带结束信息endInfo
,该内容与调用结束同玩时传入的endInfo
(或infoMapping
参数)一致。- ▪注意:玩家B 收到
OnEndMatchEvent
事件,是在其原来实例(玩家B在匹配前单播时的云机)的玩法进程中触发,而不是在房主A实例的玩法进程中。- •若是玩家B 点击【退出】,建议:调用
EndMatchGame(SeatIndex seatIndex, string endInfo)
单个用户结束同玩,指定 seatIndex
座位号,endInfo
传入自定义结束信息- ◦所指定的玩家 B 会退出同玩、回流,回到自己的单播状态。
- ◦玩家 B 原实例中,会收到
OnEndMatchEvent
事件,事件参数中, EndType = EndMatchGame,表示结束同玩了。- ◦由于是对玩家B 单个用户结束同玩,所以 房主A 状态不变。
- ◦注:相关说明
- ▪调用单个用户结束同玩
EndMatchGame
的操作实际在房主A实例的玩法进程中执行。虽然一般是由玩家B 的 操作点击触发,但执行的代码还是在房主A的实例里。- ▪座位号
SeatIndex
序号从 0 开始,0 为房主自己,从 1 开始为其他同玩玩家。- ▪[可选]
endInfo
传入自定义结束信息。因为玩家B是自己主动退出同玩、且已经看到了结算信息,所以可按需要自定义透传什么结束信息。- ▪注意:玩家B 收到
OnEndMatchEvent
事件,是在其原来实例(玩家B在匹配前单播时的云机)的玩法进程中触发(同上结束所有人同玩的情况一样)。- •如果其他玩家都退出了,A还没有点击【退出】:
- ◦注:其他玩家退出同玩时,房主侧不强制退出同玩状态。可以A自己点击退出时,调用结束同玩接口
EndMatchGame()
(不指定座位号),返回单播。private ICloudSeat _seat; /// 显示结算 public void ShowGameResult(UserCloudView view, GameResult result) { // 记录当前视图对象 _view = view; // 记录是哪个用户座位 _seat = view.Seat; // 记录本局游戏结果数据 _result = result; } // 当点击【退出】 private async void OnClickExit() { // 要传递的结束信息 var resultJson = JsonUtility.ToJson(_result); // 结束同玩 var result = await EndMatchGame(_seat.IsHost(), _seat.Index, resultJson); if (!result.IsSuccess) { // 结束同玩 result 失败 OnEndMatchFail(result); return; } // 结束同玩 result 成功 OnConfirmEnd(); } // 区分是否房主,结束同玩 private async Task<IEndResult> EndMatchGame(bool isHost, SeatIndex seatIndex, string endInfo) { Debug.Log($"GameResult EndMatchGame isHost: {isHost}, seatIndex: {seatIndex}"); IEndResult result; if (isHost) { // 若是房主A点击【退出】,调用EndMatchGame 且不指定座位号,并传入结束信息 // 逻辑其实是踢所有人,这时我们需要把结算结果透传给另一个主播 // 让另一个主播在回到原实例后仍可继续看结算结果(收到OnEndMatchEvent事件) result = await ICloudSync.Instance.MatchManager.EndMatchGame(endInfo); } else { // 若是玩家B点击【退出】,调用EndMatchGame 且指定座位号 index1,并传入结束信息 // [可选] endInfo 传入自定义结束信息,因为他是自己主动退了, 他已经看到了结算信息,所以可按需要自定义透传什么结束信息。 result = await ICloudSync.Instance.MatchManager.EndMatchGame(seatIndex, endInfo); } return result; } // 示例:结束同玩失败时,做相关提示 private void OnEndMatchFail(IEndResult result) { var debugMsg = $"code: {result.Code} ({(int)result.Code}), msg: {result.Message}"; // 根据返回码:`result.Code`枚举判断 switch (result.Code) { case EndResultCode.Success: break; case EndResultCode.Timeout: Debug.LogWarning($"EndMatchGame 结束同玩超时 {debugMsg}"); ShowNote("结束同玩超时"); break; case EndResultCode.Error: default: Debug.LogError($"EndMatchGame 结束同玩失败 {debugMsg}"); ShowNote($"结束同玩失败! {debugMsg}"); break; } }
2.事件:结束同玩了
调用结束同玩
ICloudMatchManager.EndMatchGame
后:- ◦玩家B 会退出同玩、回流,到自己的原实例,进入单播状态。
- ◦玩家B 会收到
OnEndMatchEvent
事件,事件参数中, EndType = EndMatchGame,表示结束同玩了,且事件会携带结束信息 endInfo 。- ◦房主A 在相关操作成功后,也会结束房主状态,进入单播状态。
- ◦房主A 也会收到
OnEndMatchEvent
事件,事件参数中, EndType = EndHostState,表示结束房主状态了。【使用举例】:
注:结束同玩成功后,在房主侧、回流的玩家侧,分别要做什么不同表现,这部分开发者可以自定义实现。
// 监听事件 MatchManager.OnEndMatchEvent += OnEndMatchEvent; // 收到事件:结束同玩了(返回单播) private void OnEndMatchEvent(IEndEvent endEvent) { var endType = endEvent.EndType; var info = endEvent.EndInfo; Debug.Log($"OnEndMatchEvent, endType: {endType}, has info: {endEvent.HasEndInfo()}"); // 重置本地游戏状态、界面,允许后续操作。 ViewA.Clean(); SetState(State.Ready); switch (endType) { case EndEventType.EndMatchGame: // 玩家B回流到自己实例 ShowHostNote("回流了"); try { // 回流的玩家,收到结算信息 var gameResult = JsonUtility.FromJson<GameResult>(info); if (gameResult != null) ViewA.ShowGameResult(gameResult, false); } catch (Exception) { ShowHostAlert("endInfo", info); } break; case EndEventType.EndHostState: // 房主自己结束同玩了 ShowHostNote("结束同玩了"); break; case EndEventType.ExitForJoinError: case EndEventType.ExitForGameExpired: default: ShowHostNote($"退出同玩了 ({endType})"); break; } }
3.事件:玩家离开
如果同玩状态下,玩家离开,可以做相应展示。
如果离开后,没有其他玩家了,要做什么表现,这部分开发者可以自定义实现。
举例:如果同玩已正式开始一局,可以将本局提前结束。
【示例代码】:
private void OnPlayerLeaving(ICloudSeat seat) { var playerText = seat.IsHost() ? "主播" : $"玩家{seat.IntIndex + 1}"; ViewA.ShowNote($"{playerText}: {seat.PlayerInfo?.NickName} 离开了游戏"); if (State != GameState.Started) return; CheckGameEnd(); } private void CheckGameEnd() { if (State != GameState.Started) return; if (AreOtherSeatsDisconnected()) { // 其他玩家都已退出同玩。房主侧不强制退出,开发者可以在做好合适的展示后,自己调用`结束同玩`接口 Debug.Log("其他玩家都已退出同玩"); EndGame(); return; } if (_liveBlockCount == 0) { Debug.Log("游戏关卡完成"); EndGame(); } }
4.事件:云游戏即将销毁
如果游戏中,房主(Host主机)自己关闭直播玩法、或异常退出,那么直播平台的系统会关闭其云游戏,进而会触发事件:云游戏即将销毁
OnWillDestroy
。当Host主机关闭玩法、云游戏实例即将销毁,可以将非 Host 的玩家用户踢回去,并且`endInfo`可传递自定义需要的信息。
【示例代码】:
// 监听事件 ICloudSync.Instance.OnWillDestroy += OnWillDestroy; // 事件回调:当Host主机关闭玩法、云游戏实例即将销毁,可以将非 Host 的玩家用户踢回去 public void OnWillDestroy(DestroyInfo destroyInfo) { // 如果不在云同步游戏,表示主播自己在关闭玩法,无需处理 if (!InMatchGame) return; // 以下内容,开发者可以自定义实现,`endInfo`可替换为传递自定义需要的信息 var reason = destroyInfo.Reason; var gameInfo = Game.Instance.GameInfo; var result = new GameResult { gameInfo = gameInfo, endNotice = "房主已退出(或关闭了玩法)", players = gameInfo.players }; Debug.Log($"OnWillDestroy reason: {reason}, endNotice: {result.endNotice}"); var resultJson = JsonUtility.ToJson(result); // 单个用户结束同玩:使他退出、回到单播状态 EndMatchGame(resultJson); }
附:变更记录
版本 2.6.0 :
- •添加:事件:云游戏即将销毁
OnWillDestroy
相关接入说明- •优化:根据开发者反馈,完善多处流程说明。
版本 2.5.0 :
- •变更:结束同玩
EndMatchGame
接口- ◦现在支持异步等待,且会返回 result 。
- ◦相关示例代码,增加了处理结束同玩 result 成功失败。
- •变更事件:结束同玩了
- ◦新事件名:
OnEndMatchEvent
- ◦新事件的参数:
IEndEvent endEvent
结束同玩事件,包含多个成员属性,如:枚举 EndType
结束事件类型、string EndInfo
结束信息、等。- ◦废弃旧事件名:
OnEndMatchGame
- ◦相关示例代码,增加了处理 endEvent 的事件类型、结束信息。