云同步游戏流程详解

收藏
我的收藏
云同步游戏流程,从开始单播状态、到进入同玩游戏、到结束一局游戏,主要需要接入”初始化“、”请求匹配”、与“结束同玩“相关接口、相关事件。
本文对如何接入和使用这些接口和事件进行说明。

流程总览

总流程图:

    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 :
请求匹配,使用系统自带提供的匹配池。 成功后进入多人同玩状态。
【示例代码】:1. 请求匹配: (可对照参考 Demo 工程代码的 Scripts/PK/Main.cs ):
/// 开始匹配 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但已经在房主状态,或已切流加入到了他人房间)
【示例代码】:处理匹配结果: (可对照参考 Demo 工程代码的 Scripts/PK/Main.cs ):
/// 处理匹配结果 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.[可选]:若玩家一直不加入、或加入后又退出,可以做自定义的处理,例如:可进行弹框提示,可提供给用户界面按钮,也可以提前结束本局同玩。
【示例代码】:房主加载游戏,并等待玩家加入:(可对照参考 Demo 工程代码的 Scripts/PK/Game.cs ):
// 监听事件:玩家加入/离开 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() (不指定座位号),返回单播。
【示例代码】:结束同玩: (可对照参考 Demo 工程代码的 Scripts/PK/UIGameResult.cs ):
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,表示结束房主状态了。
【使用举例】:
详见上一小节”#结束同玩 EndMatchGame“ 中的使用举例。已详细说明。
注:结束同玩成功后,在房主侧、回流的玩家侧,分别要做什么不同表现,这部分开发者可以自定义实现。
【示例代码】:收到结束同玩了事件时,检查事件类型、做相应展示:(可对照参考 Demo 工程代码的 Scripts/PK/Main.cs ):
// 监听事件 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 的事件类型、结束信息。