零基础也能玩转!跟我学,用 Sync Relay 与 Netcode 轻松开发流畅的联机游戏!

在本篇文章中,我们将通过一个示例项目工程,展示在 Unity 编辑器中 如何使用Unity Online Services(UOS)提供的 Sync Relay 服务结合 Netcode for GameObjects 开发框架来实现高效、稳定的联机游戏所需的房间管理功能和数据同步机制

教程中涉及 UOS 服务包括:

  • 游戏数据同步服务 Sync Relay:用于实现游戏房间管理、实时数据同步等网络通信功能

游戏数据同步服务 Sync

UOS Sync 服务是由 Unity 资深团队打造的数据同步服务,为您提供一站式、高并发、低延迟、可靠的游戏同步解决方案,以满足游戏在数据同步方面的严苛需求 。通过优化游戏数据流通的每一个环节,可确保数据的实时性、准确性和安全性,从而不仅能够帮助开发者提升游戏品质与用户体验,还能在激烈的市场竞争中为游戏产品赢得更多的优势与机遇。

以下是UOS Sync 服务的主要特点:

1、多元化传输协议支持,灵活应对不同场景:UOS Sync 服务支持主流传输协议,如 KCP(可靠 UDP 传输协议),WebSocket,UTP(Unity Transport Protocol,专为 Unity 游戏优化的网络传输协议)等。这一多元化支持策略,允许不同客户端根据网络环境、设备性能以及游戏需求,采用最适合的传输协议进行高效同步,从而确保了游戏数据的实时性、准确性和稳定性。

2、云原生部署,动态资源管理,高效节能: 依托于先进的容器化云原生技术,UOS Sync 服务实现了资源的动态缩扩容与即开即用。这意味着服务能够根据游戏实时负载情况,自动调整计算资源与存储资源,确保在高并发场景下依然能够保持高性能与低延迟。

3. 无缝集成主流游戏开发框架,加速游戏上线: UOS Sync 服务深度兼容并支持使 用 Mirror、Netcode 或 FishNet 等主流游戏开发框架开发的游戏项目。通过提供无缝接入的支持,UOS Sync 极大地简化了游戏接入的流程与复杂度,使得开发者无需对原有游戏架构进行大规模改造或重构,即可轻松享受到 UOS Sync 带来的高性能数据同步服务。

UOS Sync 服务模式分为:实时模式(Realtime)和中继模式(Relay)两种。

中继模式(Sync Relay)

Sync Relay 模式的游戏服务逻辑会在某个房主玩家的机器上,会节约云上算力资源,但依赖房主的网络环境。Relay 服务负责每个玩家与房主间的通信,它支持 Netcode,Mirror,FishNet 等开发框架

实时模式(Sync Realtime)

Sync Realtime 模式是轻量级消息转发服务,支持客户端之间的信息同步,比如一对一、一对多、多对多。游戏逻辑的处理发生在每个玩家的客户端。Realtime 模式适用于更新频率低、处理逻辑轻量的游戏

在本教程中,我们主要来学习 Sync Relay 模式结合 Netcode资源包在项目中的具体用法!

教程视频

教程学习大纲

  1. 创建项目工程,并安装 UOS Launcher

  2. 创建 UOS App,启用 Sync-Relay 服务并安装 SDK

  3. 安装 Netcode for GameObjects 资源包,并导入项目示例资源包

  4. 设置 NetworkManager 和 RelayTransport 组件的参数

  5. 以 Host/Client 的模式运行游戏,进行测试游戏数据的同步

  6. 在 UOS 网页端查看房间性能情况

  7. 用户自定义创建新的房间配置

  8. 多人联机服务选型参考指南

教程示例程序

教程内学习用到的项目素材资源,是通过在UOS Launcher窗口中直接在线安装的。大家也可以通过下面提供的链接查看 Sync-Relay SDK 的示例程序。

Sync-Relay SDK代码仓库的地址:

https://unitychina.coding.net/public/uos/SyncRelaySDK/git/files/master/Samples~/SyncRelayNetcodeDemo

教程操作步骤

接下来让我们跟着教程步骤开始学习吧!

1. 创建项目工程,并安装 UOS Launcher

1.1 创建项目工程

打开 Unity hub,新建一个项目工程。选择电脑上已经安装过的 Unity 编辑器版本,自定义设置项目的名称、工程的创建路径。

1.2 安装 UOS Launcher

在 Unity Editor 菜单栏中打开「Window -> Package Manager」,点击左上角的「+」,选择「Add package from git URL」;

然后输入UOS Launcher 的 git 地址,点击「Add」等待安装完成。

https://e.coding.net/unitychina/uos/UOSLauncher.git

注意:该步骤要求当前环境已安装 git,且 UOS Launcher 兼容的最低 Unity 版为 2021.3(LTS)。

2.创建 UOS App ,启用 Sync-Relay 服务并安装 Sync-Relay SDK

2.1创建 UOS App ,并与 Unity 项目工程进行关联

接着前往 UOS 官网 ( https://uos.unity.cn ),创建一个 UOS 应用。选择一个创建好的组织,输入项目的名字,点击「创建并启用」

设置」页面,找到 UOS App 的信息并复制。

然后回到 Unity 编辑器中,点击菜单栏「UOS -> Open Launcher」。在 UOS 面板中填写复制好的 AppID/AppSecret/AppServiceSecret,并点击「Link App」,实现与 UOS APP 进行关联。

2.2 开启 Sync-Relay 服务,并安装Sync-RelaySDK开启 Sync - Relay 服务

在编辑器内 Unity Online Services 窗口的下拉服务列表中,找到 Sync - Relay,点击 Enable 按钮来开启服务。

开启 Sync - Relay 服务后,网页端进来看到默认已经创建好了一个房间配置。

参数含义解释:

  • 房间配置的超时时长——指一个房间从创建到关闭的最长存续时间,无论房间内是否有玩家,在到期时房间都会自动关闭,默认显示是 30 分钟。

  • 空房间的超时时长——指一个房间在没有玩家的状态下最长存续的时间,默认显示是 300 秒。房间在超过设定的时间内没有玩家进入,房间会自动关闭。设置为 0 时,表示不检查空房间超时。

安装 Sync - Relay SDK

点击 Sync - Relay 服务旁边的「Install SDK」的按钮 ,就可以将 Sync - Relay 服务的 SDK 安装到当前项目中。

安装好 Sync - Relay SDK 以后,可以在 Package Manager 页面中看到。

3.安装 Netcode for GameObjects 资源包,并导入项目示例资源包3.1 安装Netcode for GameObjects 资源包

接下来让我们在项目内,安装与联网相关的资源包(Netcode for GameObjects)。

安装方式一:

通过 Window → Package Manager → Unity Registry, 然后找到Netcode for GameObjects资源包 ,直接点击Install」。

安装方式二:

通过打开 Window → Package Manager 窗口,点击「+」-> Add package by name,填写 name: com.unity.netcode.gameobjects,自行选择输入要安装的版本号 version: 比如输入 1.3.1,然后点击「Add」添加。

温馨提醒:这里推荐大家安装1.3.1以上的 Netcode for GameObjects 版本。

3.2导入项目案例 Sample 包,打开 Sample 场景

在 UOS Launcher 中点击import sample按钮,即可导入项目示例工程资源包。

导入资源包后,在 Assets/Samples/UOS Sync Relay/1.1.1/Sync Relay Netcode Demo/Scenes/SampleScene.unity 路径下(其中路径中的 1.1.1 指的是安装的 UOS Sync Relay SDK 的版本号),可以看到示例的 SampleScene 场景并打开。

在弹出的窗口中,点击「Import TMP Essentials」来导入TextMeshPro 的字体资源。

4.设置 NetworkManager 和 RelayTransport(Netcode) 组件的参数

接下来就给大家讲解下,场景中的游戏对象在项目中需要进行的设置啦!

4.1NetworkManager 组件的参数设置

NetworkManager 在整个游戏中必须有且只有一个, 它作为整个网络的核心对网络进行全局管理。

在场景中一般会新建一个空物体并给它添加组件NetworkManager.cs 的。 当前项目中的 NetworkManager 组件上的两个重要参数PlayerPrefab 和 Network Transport已经设置好,如下图红框中所示:

如果是自己新创建的游戏对象添加的组件,需要自己来设置这两个参数。我们需要设置Network Transport参数(它是用于网络通信和数据传输的工具)。找到面板上的 Select transport 参数那里,点击选择 RelayTransportNetcode,选择后 Unity 会自动为其加上对应的 RelayTransport 组件的。

4.2设置 Relay Transport 组件的房间配置 ID 号

从 UOS 网页端 Sync Relay 的「配置」一栏,找到「房间配置 ID」号并复制。

将复制的「房间配置 ID」号粘贴到 Relay Transport(Netcode).cs 组件的Room Profile UUID 变量那里。

4.3设置 Player Prefab 参数

再来给大家讲解一下 Player Prefab 这个参数, 它的作用是:当客户端加入游戏时,系统会为该客户端生成一个 Player Prefab 中绑定的游戏对象。

参数 Player Prefab 这里已经选择好了 PlayerCube 这个预制物体对象。 预制物体已经添加 好 Network Object 和 Network Rigidbody 组件,还添加好了实现其移动和旋转控制的数据同步的 PlayerMovement 脚本。

Network Object 组件每个具有网络行为的物体都必须添加一个 Network Object 组件,该组件的作用是为当前物体生成一个在网络中唯一的 id,方便区别于其它物体。

Network Rigidbody 组件来实现 通过网络同步刚体的速度和其它属性。

4.4场景中的小球对象GrabbableBallShared场景中的小球对象,也添加好了网络相关的组件 Network Object 和 Network Transform。

Network Transform它的作用是将玩家的信息从服务器端同步到客户端。如果我们想要同步位置会进行动态改变的物体,那么就要添加 Network Transform 组件。

4.5场景中的可缩放 Scale 的 Cube 对象

场景中有两个会循环缩放 Scale 的 Cube 对象,网络同步的组件已经添加好了。而且 Cube 上挂载了脚本组件 ScalingCube.cs,可以实现缩放 Scale 的效果。

脚本中的以下示例代码:展示了使用 Repeat 函数实现了 Cube 对象的 x 轴方向的大小重复缩放。

using Unity.Netcode;
using UnityEngine;
public class ScalingCube : MonoBehaviour
{
private void Update()
{
if (NetworkManager.Singleton == null || !NetworkManager.Singleton.IsServer)
{
return;
        }        
        transform.localScale = new Vector3(Mathf.Repeat(Time.time * 2, 3f), transform.localScale.y, transform.localScale.z);    
    }
}

4.6 界面 UI 元素

再来解析下场景中的界面 UI 元素!在 Canvas 下面创建了 5 个 Button 对象。

ExitButton 按钮—— 挂载了 ExitButtonScript.cs 脚本,实现当点击退出按钮时,切换到索引为 0 的第一个场景。

public void OnExitScene()
{
if (NetworkManager.Singleton)
{
NetworkManager.Singleton.Shutdown();
Destroy(NetworkManager.Singleton.gameObject);
}

if (m_SceneMenuToLoad != null && m_SceneMenuToLoad.GetReferencedScenes()[0] != string.Empty)
{
SceneManager.LoadSceneAsync(m_SceneMenuToLoad.GetReferencedScenes()[0], LoadSceneMode.Single);
}
else
{
SceneManager.LoadSceneAsync(0, LoadSceneMode.Single);
}
}

‍ ButtonsRoot 下面的按钮,都已经对应绑定了 ConnectionModeScript.cs 脚本中的方法。比如:

CreateHost 按钮绑定了 OnStartHostButton 方法

Join Game as Client 按钮绑定了 OnStartClientButton 方法

5.以 Host/Client 的模式运行游戏,进行测试游戏数据的同步

我们可以选择一个在编辑器中 Play 模式下测试,另一个提前 Build 成 exe文件。然后以Host/Client 的模式运行游戏。

5.1 构建 Build 文件

将当前场景添加到File → BuildSettings列表中,即可点击【Build】按钮,生成一个可执行文件。 如果有需要,可以在Edit → Project Settings页面,点击Player 按钮,自定义设置修改要导出的 exe 文件的窗口的大小。

例如:这里选择窗口模式【Windowed】,分辨率设置为 1280 * 720 像素大小。

5.2查看代码的初始化首先分析代码的初始化信息,我们需要在创建或加入房间之前,设置好玩家信息,以及需要配置好各个状态下的回调函数可以查看下 ConnectionModeScript.cs 脚本中的代码:

打开网易新闻 查看更多图片
private void Start()
{
    //此处省略其它代码......  
if (UseUSync())
{
//需要在创建或者加入房间之前,设置好玩家信息
uid = Guid.NewGuid().ToString();
var props = new Dictionary

 (); props.Add("icon", "unity"); NetworkManager.Singleton.GetComponent ().SetPlayerData(uid, "Player-" + uid, props); //需要在创建或者加入房间之前,配置好回调函数 var callbacks = new RelayCallbacks(); callbacks.RegisterConnectToRelayServer(OnConnectToRelayServer);//当连接到 Relay 服务器 callbacks.RegisterPlayerEnterRoom(OnPlayerEnterRoom);//当有玩家加入房间 callbacks.RegisterPlayerLeaveRoom(OnPlayerLeaveRoom);//当有玩家离开房间 callbacks.RegisterMasterClientMigrate(OnMasterClientMigrate);//在退出房间或掉线时会触发 callbacks.RegisterSetHeartbeat(OnSetHeartBeat);//设置心跳超时时间 NetworkManager.Singleton.GetComponent ().SetCallbacks(callbacks); } } 
‍然后我们在项目中采用 Netcode for GameObjects 的Host/Client 的模式来进行测试。
5.3在编辑器中点击 Create Host 的按钮

在编辑器中点击Create Host的按钮 ,由于当前编辑器既是作为服务器又是作为客户端启动的,所以会克隆生成一个之前设置好的 PlayerCube 预制物体对象。

由于预制体对象已经提前绑定好了 PlayerMovement.cs 脚本,所以可以通过W/S按键实现前后移动物体,A/D按键实现左右旋转物体 。

房主加进来以后,此时在 Console 控制台,可以看到房间的状态更新为(Ready)已就绪的状态 了。

5.4 异步创建房间的代码

以 Host 身份加入游戏,会创建一个房间。此时 查看 ConnectionModeScript.cs 脚本中的 OnStartHostButton 方法,看到在异步创建房间的代码中,设置了房间的名称、命名空间、最大连接的玩家数等信息。

public void OnStartHostButton()
{
if (NetworkManager.Singleton && !NetworkManager.Singleton.IsListening && m_ConnectionModeButtons)
{
if (UseUSync())
{
StartCoroutine(LobbyService.AsyncCreateRoom(new CreateRoomRequest()
{
Name = "Demo",
Namespace = "Unity",
MaxPlayers = 20,
Visibility = LobbyRoomVisibility.Public,
OwnerId = uid,
CustomProperties = new Dictionary

 () { {"a", "b"}, } }, ( resp) => { if (resp.Code == (uint)RelayCode.OK) { Debug.Log("Create Room succeed."); if (resp.Status == LobbyRoomStatus.ServerAllocated) { NetworkManager.Singleton.GetComponent ().SetRoomData(resp); StartHost(); } else { Debug.Log("Room Status Exception : " + resp.Status.ToString()); } } else { Debug.Log("Create Room Fail By Lobby Service"); } })); } else { StartHost(); } } } 

此时在 UOS 网页端,点击刷新的按钮,可以看到页面上显示的房间的名称 Demo、命名空间 Unity 等信息,和脚本中都是一致的,而且当前房间的状态也是【已就绪】的状态 。

5.5 在客户端可执行文件中点击 Join Game as Client

打开客户端可执行 exe 文件, 点击Join Game as Client,以客户端的身份加入游戏中。

此时会发现又再次克隆出来了一个预制物体对象,而且我们也可以通过 WSAD 按键控制 exe 文件中的 Capsule 对象。同时 发现两个窗口中的两个 Capsule 对象的位置、旋转角度的数据都是一样的,说明我们已经实现游戏对象的网络同步了。

作为客户端加入后,在 Console 控制台窗口,也能看到打印输出的日志信息,显示有玩家加入了房间。

此时在作为 Host 的编辑器的 Hierarchy 窗口,可以看到创建出来了两个名字为 PlayerCube(Clone) 的对象。

5.6 获取大厅房间列表的方法

当有玩家以client的身份加入房间时 ,会执行 OnStartClientButton 方法。在该方法中会异步查询房间列表,找到状态为 Ready 的一个房间加入。

public void OnStartClientButton()//以 client 身份加入游戏
{
if (NetworkManager.Singleton && !NetworkManager.Singleton.IsListening && m_ConnectionModeButtons)
{
if (UseUSync())
{

StartCoroutine(LobbyService.AsyncListRoom(new ListRoomRequest()
{
Namespace = "Unity",
Start = 0,
Count = 10,
}, ( resp) =>
{
if (resp.Code == (uint)RelayCode.OK)
{
Debug.Log("List Room succeed.");
if (resp.Items.Count > 0)
{
foreach (var item in resp.Items)
{
if (item.Status == LobbyRoomStatus.Ready)
{
StartCoroutine(LobbyService.AsyncQueryRoom(item.RoomUuid,
( _resp) =>
{
if (_resp.Code == (uint)RelayCode.OK)
{
Debug.Log("Query Room succeed.");
NetworkManager.Singleton.GetComponent ()
.SetRoomData(_resp);
StartClient();
}
else
{
Debug.Log("Query Room Fail By Lobby Service");
}
}));
break;
}
}
}
}
else
{
Debug.Log("List Room Fail By Lobby Service");
}
}));
}
else
{
StartClient();
}
}
}

5.7 修改Relay Transport 组件的属性信息

接下来让我们再看看,如何在脚本中修改 Relay Transport 组件的属性信息吧!

按下U 键

在编辑器的 Inspector 窗口,可以看到默认的心跳超时时长为 15 秒。

当按下 U 按键后,可以看到心跳超时时长参数被修改为了 30 秒。

可以切换到 PlayerMovement.cs 脚本,查看代码中 SetHeartbeat 方法的调用。下面的示例代码也给大家展示了如何修改玩家的信息(调用 UpdatePlayerInfo 方法)、更改房间的自定义属性信息(调用 UpdateRoomCustomProperties 方法)等功能。

if (Input.GetKeyDown(KeyCode.U))
{
Debug.Log("PRESS U");
NetworkManager.Singleton.GetComponent ().SetHeartbeat( 30, null);
/*
var p = NetworkManager.Singleton.GetComponent ().GetCurrentPlayer();
p.Name = "Update";
p.Properties.Add("newicon", "u3d");
NetworkManager.Singleton.GetComponent ().UpdatePlayerInfo(p);
*/

var p = new Dictionary

 (); p.Add("f", "x"); p.Add("b", "z"); NetworkManager.Singleton.GetComponent ().UpdateRoomCustomProperties(p); } 

‍ 按下 U 键运行后,在 Console 控制台窗口,也可以看到对应输出的日志信息。

按下R 键

下面的代码实现了按下 R 键时,会遍历输出当前的房间的自定义的属性信息。

if (Input.GetKeyDown(KeyCode.R))
{
Debug.Log("PRESS R");
var r = NetworkManager.Singleton.GetComponent ().GetRoomInfo();
Debug.LogFormat("Properties Count : {0}", r.CustomProperties.Count);

foreach (var item in r.CustomProperties)
{
Debug.LogFormat("Property {0} - {1}", item.Key, item.Value);
// var items = item.Value.Properties.Select(kvp => kvp.ToString());
// Debug.Log(string.Join(", ", items));
}
}

‍ 当在编辑器中按下 R 键后,回到 Console 控制台窗口,已经输出了对应的日志信息了。

5.8 断开网络传输后,房间会被回收

此时如果在编辑器中按下 K 键,会断开客户端和服务端之间的连接,我们发现场景中只有一个 Capsule 游戏对象了。

日志信息会显示,有客户端玩家 Player 离开了房间。

此时在编辑器中停止运行游戏后,可以看到日志信息提示-已经关闭了网络传输。

最后等所有的客户端都离开房间之后,根据设定的【空房间时长】,时间到了之后房间的状态会切换到【已回收】状态

空房间的超时时长(秒)——指一个房间在没有玩家的状态下最长存续的时间,默认显示是 300 秒。房间在超过设定的时间内没有玩家进入,房间会自动关闭。 设置为 0 时,表示不检查空房间超时。

在特殊情况下,也可以点击 UOS 页面上的按钮,手动强制关闭房间。

6.在 UOS 网页端查看房间性能情况查看房间性能

为充分利用计算资源,Sync 服务会在一个 Pod 内创建多个房间。我们点击「房间管理」页面的查看性能」按钮,可以查看服务器的性能监控情况。

可以看到 CPU、内存、网络使用率情况,房间规模越大性能越好,开发者可以根据自己的游戏情况选择房间大小。

大家可以进入 UOS 网页端了解更多关于性能测试的用法: https://uos.unity.cn/doc/sync/perf
7.用户自定义创建新的房间配置

大家可以 在Sync Relay 的「配置」页面,根据游戏房间的规模、存在时长、负载与性能,选择房间模板并创建自己的房间配置。点击创建房间配置按钮,自己创建一个房间配置如下图所示,自行选择微型、小型还是中型的房间规模。

  • 房间配置的超时时长——指一个房间从创建到关闭的最长存续时间,无论房间内是否有玩家,在到期时房间都会自动关闭,默认显示是 30 分钟。

  • 空房间的超时时长——指一个房间在没有玩家的状态下最长存续的时间,默认显示是300 秒。房间在超过设定的时间内没有玩家进入,房间会自动关闭。设置为 0 时,表示不检查空房间超时。

此时在 UOS 网页端可以看到新创建的房间配置 sync-room-profile。如果创建了有多个房间配置的话,可以复制一下自己要使用的那个房间配置的 ID 号。

然后在 Unity 编辑器的项目工程内, 找到RelayTransport(Netcode) 组件,将刚才复制的房间配置 ID 号粘贴到Room Profile UUID 参数那里。

8.多人联机服务选型参考指南

如果你对多人联机游戏技术架构选型存在困惑的话,可以前往 UOS 网页端查看多人联机服务选型参考指南,也可以通过 UOS 的公众号文章来查看详情。

UOS 网页端链接:

https://uos.unity.cn/product/multiplayer

UOS 公众号文章

如果你要追求服务器的绝对稳定的话,建议用 Netcode for GameObjects 开发完成后,Build 成Dedicated Server,然后使用UOS Multiverse进行托管。

学习途径

UOS 配套的相关学习教程视频也已同步上传至 Unity 中文课堂和 B 站,搜索 “使用Sync Relay & Netcode轻松构建联机游戏数据同步指南即可找到,欢迎大家前往学习,UOS 更多学习教程持续更新中,敬请期待!

了解更多 UOS 相关信息:

官网:https://uos.unity.cn

技术交流 QQ 群:823878269

公众号:UOS 游戏云服务

Unity 官方微信

第一时间了解Unity引擎动向,学习进阶开发技能

每一个“在看”,都是我们前进的动力