1.0介绍
Fish-Net(原名 FishNetworking)是一个 高性能、开源的 Unity 网络框架,专为多人联机游戏设计。它提供了比 Unity 官方 Netcode for GameObjects (NGO) 更强大的同步机制,特别适合 低延迟、高精度交互 的多人游戏,如 VR 对战、大空间线下体验、竞技游戏 等。
- Fish-Net 的核心功能
功能 说明
预测 & 回滚(Prediction) 客户端提前执行动作,服务器校正误差,减少延迟影响(类似格斗/射击游戏)。
物理同步 精确同步 Rigidbody 运动、碰撞,避免“穿模”。
服务器权威(Server Authoritative) 关键逻辑由服务器计算,防作弊。
灵活的序列化 支持自定义数据压缩,减少带宽占用。
跨平台支持 可在 Windows、Linux、Android、iOS 等平台运行服务器和客户端。
开源 & 免费 MIT 许可证,可商用且允许修改代码。
1.1快速开始
前期准备
创建CreateEmpty命名NetworkManager在物体上挂载NetworkManager和PlayerSpawner,其中PlayerSpawner可以自己写生成逻辑,然后再在其下挂载NetworkHudCanvas预制体,这个也可以不需要(可以自己写启动服务器和客户端的逻辑)创建CreateEmpty玩家生成的初始点Point,将其赋值给PlayerSpawner下的Spawns,创建玩家预制体在其上挂载NetworkObject脚本和NetworkTransform以及相机将其定义为一个网络对象并将其定义为同步位置的对象
其中NetworkTransform参数中的ClientAuthoritative代表的是客户端授权,勾选代表这个网络对象受客户端的控制,取消勾选代表受服务器控制
在网络对象Player上创建脚本PlayerController
public class PlayerController : NetworkBehaviour
{
public float rotateSpeed = 2f;
public float moveSpeed = 10f;
public Camera playerCamera;
public GameObject bullet;
public Transform FirePoint;
private void Start()
{
}
public override void OnStartClient()
{
base.OnStartClient();
if (base.IsOwner)
{
playerCamera.gameObject.SetActive(true);
}
}
//Update is called once per frame命 Unity 消息|0 个引用
private void Update()
{
if (!base.IsOwner) return;
float horizontal = Input.GetAxis("Horizontal");
float vertical = Input.GetAxis("Vertical");
Vector3 moveDirection = new Vector3(horizontal, 0f, vertical);
if (moveDirection.magnitude > 1f)
moveDirection.Normalize();
transform.position += moveSpeed * Time.deltaTime * moveDirection;
if (Input.GetKeyDown(KeyCode.Space))
{
OnFire();
}
}
//ServerRpc属性:这个属性至关重要。它表示此方法旨在由客户端调用,但仅在服务器上执行。客户端会向服务器发送一条消息,请求运行此方法。
[ServerRpc]
private void OnFire()
{
var obj=Instantiate(bullet, FirePoint.position, FirePoint.rotation);
obj.GetComponent<BulletController>().color.Value = Random.ColorHSV();
ServerManager.Spawn(obj);
}
}
创建子弹Bullet在其上挂载NetworkObject、Rigidbody、BulletController将其上的BoxCollider设置为trigger,将其Rigidbody上的重力取下勾选
public class BulletController : NetworkBehaviour
{
private Rigidbody rig;
public float force = 1000;
public float lifeTime = 5f;
//同步变量SyncVar<Color>。它需要被设置为readonly,但是一样可以获取和设置它的Value。
public readonly SyncVar<Color> color = new SyncVar<Color>();
private void Awake()
{
color.OnChange += OnColorChanged;
}
// Start is called before the first frame update
void Start()
{
rig = GetComponent<Rigidbody>();
rig.AddForce(transform.forward * force);
}
public override void OnStartServer()
{
if (base.IsServerInitialized)
StartCoroutine(DespawnAfterSeconds());
}
//当 color 的值发生变化时,服务器和所有客户端 都会触发 OnColorChanged 方法。
private void OnColorChanged(Color previous, Color next, bool asServer)
{
GetComponent<MeshRenderer>().material.color = color.Value;
}
private IEnumerator DespawnAfterSeconds()
{
yield return new WaitForSeconds(lifeTime);
Despawn(); // NetworkBehaviour shortcut for ServerManager.Despawn(gameObject);
}
[Server]
private void OnTriggerEnter(Collider other)
{
Despawn();
}
}
1.2代码示例(知识点)
NetworkManager:这是 FishNet 应用程序的核心。它管理网络生命周期,包括启动和停止服务器和客户端、处理连接以及监督网络对象的生成。
NetworkObject组件是通过网络连接游戏对象所必需的。它使对象能够在客户端和服务器之间同步其状态。Unity 场景中任何需要通过网络连接的游戏对象都必须附加 NetworkObject 组件。该组件会分配唯一的网络 ID,并管理对象的网络生命周期,确保其存在状态和状态在所有连接的客户端上保持一致。
远程过程调用 (RPC): RPC 是客户端与服务器之间通信的核心机制。它们允许您调用特定 NetworkBehaviour 实例上的方法,这些方法会在该实例的远程计算机链接版本上执行。任何脚本都可以调用 RPC,即使是常规的 MonoBehaviour、ScriptableObject 或其他类。
SyncVars:这是一个强大的功能,用于自动在网络上同步变量值和复杂的数据结构(例如列表或字典)。当服务器上某个 SyncVar 的值发生变化时,它会自动复制到所有观察它的客户端。
启动服务器(Server)
using FishNet;
public class MyServer : MonoBehaviour
{
void Start()
{
InstanceFinder.ServerManager.StartConnection(7777); // 监听 7777 端口
}
}
客户端连接(Client)
using FishNet;
public class MyClient : MonoBehaviour
{
void Start()
{
InstanceFinder.ClientManager.StartConnection("127.0.0.1", 7777); // 连接服务器
}
}
// 客户端调用,服务器执行
[ServerRpc]
//是否是本地玩家对象
base.IsOwner
//客户端启动时调用 所有网络对象的角色都会调用 所有需要判定是否是自身的玩家
public override void OnStartClient()
//用于检查 当前服务器是否已完成初始化,仅服务器能执行
base.IsServerInitialized
//只在服务器中执行
[Server]
//SyncVar<T> 是一个 自动同步的变量,用于在服务器和客户端之间保持数据一致。
//SyncVar<Color> 是一个 网络同步变量,当服务器修改它的值时,所有客户端会自动更新。
//服务器修改 → 所有客户端自动更新。
//无论服务器还是客户端,都会在本地创建这个变量。
//但只有 服务器有权修改值,客户端只能读取。
public readonly SyncVar<Color> color = new SyncVar<Color>();
标签 执行位置 调用方向 用途
[ServerRpc] 服务器执行 客户端 → 服务器 客户端触发服务器逻辑(如玩家攻击请求)。
[ObserversRpc] 所有客户端执行 服务器 → 所有客户端 服务器广播事件给所有客户端(如同步玩家死亡动画)。
[TargetRpc] 特定客户端执行 服务器 → 指定客户端 服务器通知单个客户端(如私聊消息、个人奖励)。
[Server] 仅服务器 标记方法/属性 仅在服务器端生效(自动过滤客户端调用)
[Client] 仅客户端 标记方法/属性 仅在客户端生效(服务器调用无效)
如需设置公网IP可以添加组件Tugboat进行设置
SyncTypes同步类型
同步类型 (SyncType) 驻留在对象上,是数据驱动的变量或集合。同步类型 (SyncType) 的发送间隔可调整。当对象上的同步类型 (SyncType) 被修改时,更改会自动从服务器发送到客户端。客户端将在同一对象上本地接收更改。健康变量就是一个很好的例子。您可以在玩家受到伤害时更新健康变量,新的值将发送到客户端。
using FishNet.Object;
using FishNet.Object.Synchronizing;
public class Player : NetworkBehaviour
{
readonly SyncVar<int> _health = new SyncVar<int>(100);
public void StepOnLego()
{
if (!IsServerInitialized)
return;
_health. Value -= 10;
}
}
RPC远程调用
远程过程调用是另一种对象绑定的通信类型。SyncType 用于同步变量,而 RPC 则允许您在服务器和客户端上运行逻辑。RPC 不像状态那样受限于时间间隔,它可以在下一个网络 tick 时立即发送,也可以尽快发送。必须继承 NetworkBehaviour 类中。
using FishNet.Object;
public class Player : NetworkBehaviour
{
[ServerRpc]
void UpdatePlayerName(string newName)
{
print($"Player {OwnerId} has updated their name to {newname}");
}
}
Broadcast 广播
与状态不同,广播不受对象约束。广播可以用于任意数量的任务,但更常用于在服务器和客户端之间传递数据组。广播可以在代码中的任何位置接收和发送,而不必像 RPC 那样位于 NetworkBehaviour 类中。
using FishNet;
using UnityEngine;
public class ChatSystem : MonoBehaviour
{
public void SendChatMessage(string text)
{
ChatBroadcast msg = new ChatBroadcast()
{
Message = text,
FontColor = Color.white
};
InstanceFinder.ClientManager.Broadcast(msg);
}
}
IsClientStarted检测运行代码的实例是否是客户端,也可能是服务器
IsClientOnlyStarted可用于检测运行代码的实例是否仅为客户端而不是服务器。
IsServerStarted检测运行代码的实例是否是服务器。它也可能是客户端。
IsServerOnlyStarted返回运行代码的实例是否仅为服务器而不是客户端。
using FishNet.Object;
public class Player : NetworkBehaviour
{
public void OnStruck()
{
if (!IsClientStarted)
return;
//仅在客户端上执行
}
}