gpt4 book ai didi

unity3d - 在 Unity Networking - UNET 中同步复杂的游戏对象

转载 作者:行者123 更新时间:2023-12-01 01:49:12 26 4
gpt4 key购买 nike

我正在开发第一人称游戏,玩家可以在其中构建复杂的对象。结构示例:

Train
- Wagon
- Table
- Chair
- Chest (stores items)
- Workshop (manufactures items, has build queue)

玩家可以创建 火车 , 添加 货车 , 地点 对象 进入 货车 ,修改放置 对象 .整列火车可以移动,物体在变换层次中。

玩家可以与放置的对象进行交互(例如将元素放入胸部,修改车间构建队列),因此我需要一种跨网络识别它们的方法。这表明所有对象都应该有 NetworkIdentity .一些对象也有需要同步的状态(存储项目、构建队列)。

建议的同步方法是什么?哪些对象应该有 NetworkIdentity ?

添加 NetworkIdentity他们所有人都阻止我在编辑器中创建火车预制件(预制件可以有 NetworkIdentity 仅在根目录下),但我可能会接受。当客户端上生成货车或对象时,我还必须“手动”设置父级。

另一种解决方案可能是添加 NetworkIdentity仅限 火车 然后通过 中的某个 ID 识别对象火车 .我无法想象如何使用 SyncVar使用这种方法,因为一切都必须在 上火车 .

最佳答案

解决方案

  • 添加 NetworkIdentity到层次结构中的所有对象
  • 忽略警告Prefab 'xxx' has several NetworkIdentity components attached to itself or its children, this is not supported.
  • 通过脚本手动处理网络上的层次结构

  • 我们需要确保客户端仅在有父对象时才接收子对象。我们还需要确保客户端在收到父对象时尽快收到子对象。

    这是通过 OnRebuildObservers 实现的和 OnCheckObserver .这些方法检查客户端是否有父对象,当它有父对象时,它将玩家连接添加到观察者列表,这导致玩家接收对象。

    我们还需要调用 NetworkIdentity.RebuildObservers当父对象产生时。这是通过自定义连接类实现的,它会通知 MultiplayerGame当对象在客户端产生时(连接发送 Spawn 消息)。

    完整的脚本如下。

    网络 child

    子对象上的网络组件的基类,例如马车,马车里的物体。

    using System;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.Networking;

    /// <summary>
    /// Base component for network child objects.
    /// </summary>
    public abstract class NetworkChild : NetworkBehaviour
    {
    private NetworkIdentity m_networkParent;

    [SyncVar(hook = "OnNetParentChanged")]
    private NetworkInstanceId m_networkParentId;

    public NetworkIdentity NetworkParent
    {
    get { return m_networkParent; }
    }

    #region Server methods
    public override void OnStartServer()
    {
    UpdateParent();
    base.OnStartServer();
    }

    [ServerCallback]
    public void RefreshParent()
    {
    UpdateParent();
    GetComponent<NetworkIdentity>().RebuildObservers(false);
    }

    void UpdateParent()
    {
    NetworkIdentity parent = transform.parent != null ? transform.parent.GetComponentInParent<NetworkIdentity>() : null;
    m_networkParent = parent;
    m_networkParentId = parent != null ? parent.netId : NetworkInstanceId.Invalid;
    }

    public override bool OnCheckObserver(NetworkConnection conn)
    {
    // Parent id might not be set yet (but parent is)
    m_networkParentId = m_networkParent != null ? m_networkParent.netId : NetworkInstanceId.Invalid;

    if (m_networkParent != null && m_networkParent.observers != null)
    {
    // Visible only when parent is visible
    return m_networkParent.observers.Contains(conn);
    }
    return false;
    }

    public override bool OnRebuildObservers(HashSet<NetworkConnection> observers, bool initialize)
    {
    // Parent id might not be set yet (but parent is)
    m_networkParentId = m_networkParent != null ? m_networkParent.netId : NetworkInstanceId.Invalid;

    if (m_networkParent != null && m_networkParent.observers != null)
    {
    // Who sees parent will see child too
    foreach (var parentObserver in m_networkParent.observers)
    {
    observers.Add(parentObserver);
    }
    }
    return true;
    }
    #endregion

    #region Client Methods
    public override void OnStartClient()
    {
    base.OnStartClient();
    FindParent();
    }

    void OnNetParentChanged(NetworkInstanceId newNetParentId)
    {
    if (m_networkParentId != newNetParentId)
    {
    m_networkParentId = newNetParentId;
    FindParent();
    OnParentChanged();
    }
    }

    /// <summary>
    /// Called on client when server sends new parent
    /// </summary>
    protected virtual void OnParentChanged()
    {
    }

    private void FindParent()
    {
    if (NetworkServer.localClientActive)
    {
    // Both server and client, NetworkParent already set
    return;
    }

    if (!ClientScene.objects.TryGetValue(m_networkParentId, out m_networkParent))
    {
    Debug.AssertFormat(false, "NetworkChild, parent object {0} not found", m_networkParentId);
    }
    }
    #endregion
    }

    网络通知连接

    通知 MultiplayerGame 的自定义连接类当 Spawn and Destroy 消息发送到客户端时。

    using System;
    using UnityEngine;
    using UnityEngine.Networking;

    public class NetworkNotifyConnection : NetworkConnection
    {
    public MultiplayerGame Game;

    public override void Initialize(string networkAddress, int networkHostId, int networkConnectionId, HostTopology hostTopology)
    {
    base.Initialize(networkAddress, networkHostId, networkConnectionId, hostTopology);
    Game = NetworkManager.singleton.GetComponent<MultiplayerGame>();
    }

    public override bool SendByChannel(short msgType, MessageBase msg, int channelId)
    {
    Prefilter(msgType, msg, channelId);
    if (base.SendByChannel(msgType, msg, channelId))
    {
    Postfilter(msgType, msg, channelId);
    return true;
    }
    return false;
    }

    private void Prefilter(short msgType, MessageBase msg, int channelId)
    {
    }

    private void Postfilter(short msgType, MessageBase msg, int channelId)
    {
    if (msgType == MsgType.ObjectSpawn || msgType == MsgType.ObjectSpawnScene)
    {
    // NetworkExtensions.GetObjectSpawnNetId uses reflection to extract private 'netId' field
    Game.OnObjectSpawn(NetworkExtensions.GetObjectSpawnNetId(msg), this);
    }
    else if (msgType == MsgType.ObjectDestroy)
    {
    // NetworkExtensions.GetObjectDestroyNetId uses reflection to extract private 'netId' field
    Game.OnObjectDestroy(NetworkExtensions.GetObjectDestroyNetId(msg), this);
    }
    }
    }

    多人游戏
    NetworkManager 上的组件,它在服务器启动时设置自定义网络连接类。

    using System;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.Networking;

    /// <summary>
    /// Simple component which starts multiplayer game right on start.
    /// </summary>
    public class MultiplayerGame : MonoBehaviour
    {
    HashSet<NetworkIdentity> m_dirtyObj = new HashSet<NetworkIdentity>();

    private void Start()
    {
    var net = NetworkManager.singleton;

    var host = net.StartHost();
    if (host != null)
    {
    NetworkServer.SetNetworkConnectionClass<NetworkNotifyConnection>();
    }
    }

    /// <summary>
    /// Reliable callback called on server when client receives new object.
    /// </summary>
    public void OnObjectSpawn(NetworkInstanceId objectId, NetworkConnection conn)
    {
    var obj = NetworkServer.FindLocalObject(objectId);
    RefreshChildren(obj.transform);
    }

    /// <summary>
    /// Reliable callback called on server when client loses object.
    /// </summary>
    public void OnObjectDestroy(NetworkInstanceId objectId, NetworkConnection conn)
    {
    }

    void RefreshChildren(Transform obj)
    {
    foreach (var child in obj.GetChildren())
    {
    NetworkIdentity netId;
    if (child.TryGetComponent(out netId))
    {
    m_dirtyObj.Add(netId);
    }
    else
    {
    RefreshChildren(child);
    }
    }
    }

    private void Update()
    {
    NetworkIdentity netId;
    while (m_dirtyObj.RemoveFirst(out netId))
    {
    if (netId != null)
    {
    netId.RebuildObservers(false);
    }
    }
    }
    }

    关于unity3d - 在 Unity Networking - UNET 中同步复杂的游戏对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45908172/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com