🚀 30+款热门AI模型一站整合,DeepSeek/GLM/Claude 随心用,限时 5 折。 👉 点击领海量免费额度
这类项目最值得先看的不是功能列表,而是能不能在普通服务器环境里稳定跑起来,以及网络同步的延迟和稳定性能不能达到联机游戏的基本要求。一个基于 Mirror 组件、部署在 Linux 服务器上的 Unity 多人游戏,核心要解决的就是客户端与服务器、客户端与客户端之间状态同步的可靠性和效率问题。如果你正在做毕业设计、实习项目,或者想从零搭建一个可联机的游戏 Demo,这篇文章会拆解从项目准备、Mirror 配置、Linux 服务器部署到联机测试的全流程,重点不是复述官方文档,而是告诉你每一步最容易卡住的地方在哪里,以及怎么判断你的同步是否真的“跑通了”。
1. 先搞清楚 Mirror 和 Linux 服务器部署到底要解决什么问题
很多人一上来就急着装环境、导包,结果跑起来才发现网络延迟高得没法玩,或者服务器一开就崩。所以第一步不是动手,而是先明确这个技术栈组合的核心目标。
1.1 Mirror 组件在网络同步里扮演什么角色
Mirror 不是一个底层网络协议,它是一个建立在 Unity 之上的高层网络库。你可以把它理解为一个“网络管理器”和“同步规则制定者”。它的核心工作是帮你处理:
- 对象生成与销毁同步:服务器生成一个敌人,所有客户端都能看到这个敌人。
- 变量同步:服务器上玩家的血量从 100 变成 80,所有客户端看到的该玩家血条也会更新。
- 远程过程调用 (RPC):客户端 A 开枪,需要告诉服务器和其他客户端“这里开了一枪并播放音效”。
- 网络消息:自定义一些游戏逻辑消息,比如聊天、游戏状态切换。
它底层默认使用 KCP 或 Telepathy 等传输层,但也可以替换成像Unity Transport Package (UTP)这样的新传输方案。UTP 是基于 UDP 的底层库,提供了连接管理、可靠性、排序等更精细的控制。你项目标题里提到的“网络同步”,其流畅度和可靠性,很大程度上取决于你选择了 Mirror 底层的哪种传输方式,以及参数如何配置。
1.2 为什么要把服务器部署在 Linux 上
对于实习作品、毕业设计或个人项目,选择 Linux 服务器通常基于以下几点考虑:
- 成本与资源:云服务商的 Linux 虚拟机通常比 Windows Server 便宜,且对命令行操作友好,资源占用相对更低。
- 稳定性与自动化:Linux 服务器更适合长时间运行后台服务,配合
systemd或supervisor可以方便地实现进程守护、日志管理和自动重启。 - 生产环境常见选择:很多中小型游戏项目的实际生产服务器就是 Linux,提前熟悉这个环境对求职有帮助。
但这里有个关键点:你部署的是“专有游戏服务器”,而不是用某个客户端当主机。Mirror 支持两种模式:监听服务器 (Listen Server) 和专有服务器 (Dedicated Server)。监听服务器就是其中一个客户端同时兼任服务器,这对主机网络和性能要求高,且主机退出游戏就结束。专有服务器则是一个独立的后台进程,只负责游戏逻辑和同步,更稳定、更公平。我们讨论的 Linux 部署,指的就是运行这个“专有服务器”构建版本。
1.3 项目成功的几个关键判断标准
在开始之前,先想清楚怎么算“成功部署”:
- 能构建出 Linux 平台的服务器版本:Unity 能成功打出
Server.x86_64这样的可执行文件。 - 服务器能在 Linux 上无头运行:不需要图形界面,通过命令行启动,能持续运行不崩溃。
- 客户端能成功连接:在 Windows/Mac 上运行的 Unity 编辑器或 PC 构建版本,能通过网络 IP 和端口连接到 Linux 服务器。
- 基础同步功能正常:玩家移动、生成物体、状态变化能在所有客户端间基本同步。
- 具备一定的容错能力:客户端异常断开,服务器能正确处理;短时间网络波动不会导致游戏状态混乱。
下面我们就围绕这五个目标,一步步拆解操作和避坑点。
2. 环境准备与 Unity 项目配置:别在第一步就选错版本
环境是最大的坑。版本不匹配会导致各种诡异的编译错误和运行时崩溃。
2.1 Unity 编辑器版本与模块安装
根据搜索材料中 Unity Mirror 示例的要求,它支持2020.3 或更高版本。对于新项目,我建议选择Unity 2022.3 LTS版本。LTS 代表长期支持,更稳定,社区资源也多。
关键操作:安装 Linux 构建支持模块
- 打开 Unity Hub,在“安装”页面找到你安装的 Unity 版本。
- 点击右侧的齿轮图标,选择“添加模块”。
- 在弹出窗口中,找到“Linux Build Support (IL2CPP)”和“Linux Build Support (Mono)”。强烈建议两个都勾选安装。IL2CPP 能带来更好的性能,但某些插件可能兼容性有问题;Mono 作为备用方案。
- 等待下载和安装完成。这一步是后续能在 Unity 中为 Linux 平台构建的前提,很多人忘了装,导致构建目标里根本没有 Linux 选项。
2.2 导入 Mirror 网络库
Mirror 可以通过 Unity 的 Package Manager 从 Git URL 添加,但更稳定的方式是从 Asset Store 或 GitHub 发布页下载.unitypackage文件导入。
- 方式一(推荐,版本明确):访问 Mirror 的 GitHub Releases 页面,下载最新的
.unitypackage文件(如Mirror-75.0.0.unitypackage)。在 Unity 编辑器中,Assets -> Import Package -> Custom Package...,选择该文件导入。 - 方式二:在 Unity 编辑器中,
Window -> Package Manager,点击左上角“+”号,选择“Add package from git URL”,输入https://github.com/vis2k/Mirror.git。但这种方式拉取的是最新开发版本,可能不稳定。
导入后,你的项目Assets文件夹下会出现一个Mirror文件夹。检查Window -> Package Manager,在“My Assets”或“In Project”中应该能看到 Mirror。
2.3 配置 Network Manager 和场景
这是 Mirror 的核心配置环节。
- 创建 Network Manager:在场景中创建一个空的 GameObject,命名为“NetworkManager”。为其添加
Network Manager组件(在 Inspector 中点击Add Component,搜索NetworkManager)。 - 设置玩家预制体:在
Network Manager组件的Player Prefab槽中,拖入你准备好的玩家角色预制体。这个预制体必须挂载Network Identity组件。 - 设置生成位置:将场景中的空 GameObject(作为出生点)拖到
Network Manager的Start Positions列表中。 - 创建基础 UI(可选但建议):创建一个 Canvas,添加两个按钮(“Host”主机、“Client”客户端)和一个输入框(用于输入服务器 IP)。为按钮编写脚本,调用
NetworkManager.singleton.StartHost()和NetworkManager.singleton.StartClient(),并将输入框的文本赋值给NetworkManager.singleton.networkAddress。
注意:很多新手在这里卡住,是因为玩家预制体没有
Network Identity,或者Network Manager没有正确配置玩家预制体和出生点。第一个错误会导致玩家无法生成,第二个错误会导致玩家生成在奇怪的位置。
2.4 为网络对象添加同步组件
要让一个 GameObject 在网络间同步,它需要:
Network Identity:这是该对象在网络中的唯一身份证。必须挂载。- 同步组件:根据需要选择。
Network Transform:同步位置、旋转。这是最常用也最容易出问题的组件。需要仔细设置“同步间隔”、“插值方式”和“压缩”。Network Animator:同步动画状态和参数。自定义 NetworkBehaviour 脚本:在脚本中定义[SyncVar]属性来同步变量,使用[Command]和[ClientRpc]来执行远程调用。
一个关键避坑点:Network Transform的同步默认设置下,Network Transform的同步频率可能过高,给网络带来不必要的压力。对于移动缓慢或不需要每帧精确同步的对象(比如环境装饰物),可以增大“同步间隔”。对于玩家角色,可以启用“插值”来平滑移动,但要注意这会在客户端带来一定的预测延迟。
3. 构建 Linux 专有服务器版本:从编辑器到可执行文件
这一步的目标是生成一个能在 Linux 命令行下运行的、没有图形界面的服务器程序。
3.1 构建设置与参数
- 在 Unity 编辑器中,打开
File -> Build Settings。 - 在“Platform”列表中选择Linux。如果没看到,说明你之前没安装 Linux Build Support 模块。
- 在左下角,务必勾选 “Server Build”。这个选项至关重要,它告诉 Unity 构建一个专有服务器版本,不包含任何客户端渲染、输入处理等代码,体积更小,运行更高效。
- 点击“Player Settings...”,打开 Player Settings 窗口。
- 在“Resolution and Presentation”下,确保“Fullscreen Mode”设置为“Windowed”或“Exclusive Fullscreen”对于服务器构建影响不大,但通常选“Windowed”。更重要的是,确认“Run In Background”被勾选,这样即使终端失去焦点,服务器也能继续运行。
- (可选但建议)在“Other Settings”下的“Configuration”中,将“Scripting Backend”设置为“IL2CPP”,并将“Target Architecture”设置为“x86_64”。这能提供更好的性能和兼容性。
- 回到 Build Settings 窗口,点击“Build”或“Build And Run”。选择一个空文件夹(例如
Builds/LinuxServer)来存放构建结果。
构建完成后,你会在目标文件夹中看到类似以下结构的文件:
LinuxServer/ ├── Server.x86_64 # 主可执行文件 ├── Server_Data/ # 资源数据文件夹 ├── UnityPlayer.so # Unity 运行时库 └── ... (其他 .so 库文件)这个Server.x86_64就是你的 Linux 游戏服务器程序。
3.2 处理可能出现的构建错误
- 错误:缺少某些 .dll 或插件:检查项目中是否有仅支持 Windows 的第三方插件。尝试在插件的导入设置(Import Settings)中,取消勾选 Linux 平台,或者寻找该插件的 Linux 版本。
- 错误:脚本编译错误:确保所有脚本在编辑器模式下能正常编译。服务器构建使用的是相同的脚本,但可能会因为
UNITY_SERVER这样的编译定义而暴露问题。如果你的代码中有#if UNITY_EDITOR或#if !UNITY_SERVER的区块,要确保服务器构建时,必要的逻辑不会被排除。 - 构建出的文件在 Linux 上无法执行:首先在 Linux 上使用
chmod +x Server.x86_64命令给可执行文件添加运行权限。如果还不行,可能是动态链接库问题,需要检查 Linux 系统是否安装了必要的运行库,如libc6、libstdc++6等。对于干净的 Linux 系统(如 Docker 容器),可能需要安装libgcc、libstdc++等基础库。
4. 部署到 Linux 服务器并运行:让服务在后台稳定跑起来
假设你已经有一台安装了 Linux(如 Ubuntu 22.04)的云服务器或本地虚拟机,并且可以通过 SSH 连接。
4.1 上传文件到服务器
将整个构建输出的LinuxServer文件夹打包,上传到你的 Linux 服务器。可以使用scp命令或 SFTP 工具(如 FileZilla)。
# 在本地终端执行,将文件夹上传到服务器的 /home/yourname/ 目录 scp -r /path/to/your/LinuxServer yourname@your_server_ip:/home/yourname/上传后,通过 SSH 登录服务器,进入该目录。
4.2 首次运行与权限设置
- 进入目录:
cd /home/yourname/LinuxServer - 赋予执行权限:
chmod +x Server.x86_64 - 尝试直接运行:
./Server.x86_64- 如果看到 Unity 的 Logo 和日志输出,并且最后停留在类似
Server started on port: 7777的日志,说明服务器启动成功。 - 如果提示缺少共享库,比如
error while loading shared libraries: libxxx.so.xx,你需要安装对应的库。在 Ubuntu/Debian 上,可以用apt-get install libxxx来安装。一个常见的解决方法是安装libgdiplus(如果用到某些图形相关功能)和ca-certificates(用于 HTTPS 请求)。
- 如果看到 Unity 的 Logo 和日志输出,并且最后停留在类似
4.3 以无头模式与后台服务运行
直接运行会占用当前终端,且关闭终端进程就结束了。我们需要让它以后台服务运行。
使用 nohup 简单后台运行:
nohup ./Server.x86_64 -batchmode -nographics -logFile server.log &-batchmode:以批处理模式运行,不弹出任何窗口。-nographics:不初始化图形设备,纯命令行。-logFile server.log:将日志输出到server.log文件,而不是控制台。&:让命令在后台运行。nohup:确保即使 SSH 断开连接,进程也不会被终止。 运行后,可以通过tail -f server.log实时查看日志。
使用 systemd 管理服务(生产环境推荐): 创建一个服务配置文件:
sudo vim /etc/systemd/system/my-unity-server.service[Unit] Description=My Unity Game Server After=network.target [Service] Type=simple User=yourname # 运行服务的用户 WorkingDirectory=/home/yourname/LinuxServer # 服务器程序所在目录 ExecStart=/home/yourname/LinuxServer/Server.x86_64 -batchmode -nographics -logFile /home/yourname/LinuxServer/server.log Restart=on-failure # 失败时自动重启 RestartSec=10s [Install] WantedBy=multi-user.target然后执行:
sudo systemctl daemon-reload sudo systemctl start my-unity-server sudo systemctl enable my-unity-server # 设置开机自启 sudo systemctl status my-unity-server # 查看服务状态使用
journalctl -u my-unity-server -f可以跟踪日志。
4.4 防火墙与端口开放
Mirror 默认使用7777端口(TCP/UDP)。确保你的 Linux 服务器防火墙允许该端口。
- 如果使用 UFW(Ubuntu 常见):
sudo ufw allow 7777/tcp sudo ufw allow 7777/udp sudo ufw reload - 如果使用 firewalld(CentOS/RHEL 常见):
sudo firewall-cmd --permanent --add-port=7777/tcp sudo firewall-cmd --permanent --add-port=7777/udp sudo firewall-cmd --reload - 云服务器控制台:别忘了在云服务商(如阿里云、腾讯云)的安全组规则中,也添加入站规则,允许 7777 端口。
5. 客户端连接与网络同步测试:验证游戏是否真的“联”起来了
服务器跑起来只是第一步,关键看客户端能不能连上,并且游戏逻辑能正确同步。
5.1 客户端连接配置
在你的 Unity 编辑器或 PC 构建的客户端项目中:
- 确保客户端的
Network Manager组件中,Network Address字段填写了你Linux 服务器的公网 IP 地址(如果是本地测试,可以是局域网 IP,如192.168.1.xxx)。 Network Port字段填写7777(或你自定义的端口)。- 运行客户端,点击连接按钮。
连接成功的关键日志:
- 服务器端日志:应出现
Client connected, connectionId: 1之类的信息。 - 客户端日志:应出现
Connected to server或类似信息。
如果连接失败,按以下顺序排查:
- 网络可达性:在客户端机器上,用
telnet 服务器IP 7777或nc -zv 服务器IP 7777命令测试端口是否开放。如果不通,检查服务器防火墙和云服务商安全组。 - 服务器进程是否存活:在服务器上执行
ps aux | grep Server.x86_64查看进程是否存在,或用netstat -tulnp | grep 7777查看端口是否被监听。 - 地址与端口:确认客户端填写的 IP 和端口完全正确,包括有无多余空格。
5.2 基础同步功能测试
连接成功后,需要系统性地测试同步:
- 玩家生成与位置同步:
- 客户端A连接后,其玩家角色应在所有客户端(包括服务器视图,如果有的话)的同一出生点出现。
- 客户端A移动,客户端B应能看到其平滑移动(如果开了插值)或至少位置更新。
- 常见问题:玩家预制体缺少
Network Transform,或Network Transform的同步目标设置错误(应同步玩家自身的Transform)。
- 变量同步测试:
- 在玩家脚本中创建一个
[SyncVar] int health = 100;。 - 在服务器端逻辑中修改
health(例如受到伤害)。观察其他客户端上该玩家的health值是否同步更新。 - 注意:
[SyncVar]的修改只有服务器端有权直接更改,客户端修改不会同步给其他人。这是 Mirror 的权威服务器模型决定的。
- 在玩家脚本中创建一个
- RPC 调用测试:
- 在玩家脚本中创建一个
[Command]方法,用于请求开枪。 - 在该
[Command]方法中调用一个[ClientRpc]方法,用于在所有客户端播放开枪音效和特效。 - 在客户端A按下开枪键,触发
[Command]。验证客户端A、B以及服务器(如果可视)是否都收到了[ClientRpc]并播放了效果。
- 在玩家脚本中创建一个
- 断开重连与场景切换:
- 测试客户端非正常断开(直接关闭窗口),服务器是否会正确调用
OnServerDisconnect并销毁对应的玩家对象。 - 测试使用
NetworkManager.singleton.ServerChangeScene切换场景时,所有客户端是否都能正确加载新场景,并且玩家对象被保留或在新场景中重新生成。
- 测试客户端非正常断开(直接关闭窗口),服务器是否会正确调用
5.3 性能与延迟观察
对于实习作品,可能不需要严格的性能测试,但至少要观察:
- 服务器 CPU/内存占用:在 Linux 服务器上,使用
top或htop命令查看Server.x86_64进程的资源使用情况。一个简单的 2D 或轻量 3D 游戏,在空闲状态下 CPU 占用应很低(<5%),内存占用相对稳定。 - 网络延迟感:在客户端移动时,观察其他客户端视角下的移动是否流畅,有无明显的“瞬移”或“回弹”。这通常与
Network Transform的“同步间隔”和“插值”参数,以及网络本身的延迟有关。 - 多客户端压力:同时连接 2-4 个客户端,进行简单的交互(移动、生成物体),观察服务器日志是否有错误,客户端是否依然能保持同步。
6. 进阶考量与问题排查清单
当基础功能跑通后,可以考虑以下进阶优化,并收藏这份排查清单。
6.1 传输层选择:KCP vs UTP
Mirror 默认使用 KCP 传输。但根据搜索材料,Unity 官方更推荐新的Unity Transport Package (UTP),特别是需要与 Unity Relay 服务集成时。
- 切换到 UTP:
- 通过 Package Manager 安装
com.unity.transport和com.unity.netcode.adapter.utp包。 - 在
NetworkManagerGameObject 上,移除默认的KcpTransport或TelepathyTransport组件。 - 添加
Unity.Netcode.Transports.UTP.UTPTransport组件。 - 将
NetworkManager的Transport字段拖拽赋值到这个新的UTPTransport组件。
- 通过 Package Manager 安装
- 如何选择:
- KCP:成熟,社区示例多,对于小规模、对延迟敏感的动作游戏可能表现不错。
- UTP:Unity 官方现代网络栈,与 Relay、Netcode for GameObject 集成更好,未来维护更有保障。对于新项目,尤其是考虑使用 Relay 服务的,建议直接上 UTP。
6.2 使用 Unity Relay 服务简化 NAT 穿透
对于实习作品,如果想让身处不同局域网的朋友也能方便地连接,而不需要复杂的端口映射,可以使用Unity Relay。它相当于一个由 Unity 运营的中转服务器。
- 在 Unity Dashboard 中创建组织和服务项目,启用 Relay 服务。
- 在 Unity 编辑器项目中,通过 Package Manager 安装
com.unity.services.relay包。 - 参考搜索材料中的
RelayNetworkManager示例代码,修改你的网络管理器逻辑。 - 核心流程变为:主机向 Relay 服务申请一个“加入码”,然后将这个码分享给其他客户端。客户端使用该码直接连接 Relay 服务,由 Relay 负责中转数据。
- 注意:Relay 服务有免费额度,超出后会产生费用。对于学习和 demo 演示通常够用。
6.3 常见问题排查清单
当遇到问题时,按照以下顺序检查,可以解决大部分情况:
| 问题现象 | 可能原因 | 排查步骤 |
|---|---|---|
| 构建失败 | 1. 平台模块未安装。 2. 脚本编译错误。 3. 插件平台不支持。 | 1. 在 Unity Hub 中检查并安装 Linux Build Support。 2. 查看 Console 窗口中的具体错误信息。 3. 检查第三方插件的导入设置,禁用 Linux 平台或寻找替代。 |
| 服务器启动后立即退出 | 1. 缺少运行库。 2. 程序参数错误。 3. 脚本中存在仅在编辑器下运行的逻辑。 | 1. 在 Linux 上运行ldd Server.x86_64检查动态库依赖。2. 检查启动命令,确保路径正确。 3. 检查代码中是否有 #if UNITY_EDITOR包裹了服务器运行必需的核心逻辑。 |
| 客户端无法连接 | 1. 服务器 IP/端口错误。 2. 防火墙/安全组未开放端口。 3. 服务器进程未运行或崩溃。 | 1. 客户端使用telnet/nc测试服务器端口。2. 检查服务器本地防火墙 ( ufw/iptables) 和云平台安全组。3. 登录服务器查看进程 ( ps aux) 和端口监听 (netstat) 状态。 |
| 连接成功但玩家不同步 | 1. 玩家预制体缺少Network Identity。2. Network Transform未正确挂载或配置。3. 生成玩家的逻辑未在服务器端调用。 | 1. 确认玩家预制体根节点有Network Identity。2. 确认 Network Transform组件存在且目标 Transform 设置正确。3. 玩家生成应通过 NetworkServer.Spawn()调用,确保在服务器执行。 |
| 变量不同步 | 1. 变量未标记[SyncVar]。2. 在客户端直接修改了 [SyncVar]。3. Hook 方法未正确声明。 | 1. 在服务器端脚本中,给需要同步的变量加上[SyncVar]特性。2. 牢记 [SyncVar]只能由服务器修改。客户端修改需通过[Command]请求服务器。3. [SyncVar(hook = nameof(OnHealthChanged))]的 hook 方法签名需正确。 |
| RPC 不执行 | 1. 方法未标记[Command]/[ClientRpc]。2. [Command]方法名未以Cmd开头。3. 调用 [Command]的对象没有Network Identity或不是本地玩家。 | 1. 检查方法前的特性标记是否正确。 2. Mirror 默认要求 [Command]方法名以Cmd开头(可配置)。3. 确保调用 [Command]的脚本挂载在具有Network Identity且isLocalPlayer为 true 的对象上。 |
| 延迟很高或卡顿 | 1. 网络本身延迟高。 2. Network Transform同步频率过高或过低。3. 同步的数据量过大。 | 1. 测试客户端到服务器的网络延迟 (ping)。2. 调整 Network Transform的“同步间隔”,在流畅度和网络负载间平衡。3. 优化同步策略,例如非关键状态降低同步频率,使用压缩。 |
这个项目从技术选型到最终部署,每一步都有明确的验证点。我建议的实操顺序是:先在 Unity 编辑器内用多个实例测试同步逻辑(使用ParrelSync工具可以方便地打开多个编辑器实例),确保逻辑无误;然后构建 Windows/Mac 的专有服务器版本在本地跑通;最后再挑战 Linux 服务器的构建、部署和远程连接。这样能把问题拆分开,更容易定位。Linux 部署环节,最需要耐心的是环境配置和权限问题,一旦跑通,后续的更新部署就会非常顺畅。
🚀 30+款热门AI模型一站整合,DeepSeek/GLM/Claude 随心用,限时 5 折。 👉 点击领海量免费额度