网络同步插件FishNet

1.0介绍

Fish-Net(原名 FishNetworking)是一个 高性能、开源的 Unity 网络框架,专为多人联机游戏设计。它提供了比 Unity 官方 Netcode for GameObjects (NGO) 更强大的同步机制,特别适合 低延迟、高精度交互 的多人游戏,如 VR 对战、大空间线下体验、竞技游戏 等。

  1. 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以及相机将其定义为一个网络对象并将其定义为同步位置的对象

image.png

image.png

其中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进行设置


image.png

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;

      //仅在客户端上执行
    }
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容