unity NGO 代码教程:网络上动态生成物体

这篇具有很好参考价值的文章主要介绍了unity NGO 代码教程:网络上动态生成物体。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

生成一个网络对象有多种办法,但始终

  1. 只能由Server/Host生成/销毁
  2. 必须有network  object组件
  3. 要在NetworkManager中的NetworkPrefebList中注册

建议先看完第一章:unity netcode for gameobject(NGO)逻辑代码教程-CSDN博客

最简单直接的方法

 简单的Debug程序:

private void Update() {
        if (!IsOwner) {
            return;
        }
        if (Input.GetKeyDown(KeyCode.F)) {
            Spawn();
        }
        if (Input.GetKeyDown(KeyCode.G)) {
            DeSpawn();
        }
    }

生成

//  NetworkObject networkObject;
    private void Spawn() {
        //只有服务器可以创建/销毁对象
        if (IsServer) {
//先在本地实例化
            GameObject prefebInstance = Instantiate(SpawnPrefeb);
//获取NetworkObject组件
            networkObject = prefebInstance.GetComponent<NetworkObject>();
//调用Spawn函数,它的参数含义为是否随着场景销毁而销毁,默认为false
            networkObject.Spawn();
        }
    }

销毁

注意Despawn会默认销毁对象,可以理解为在其后调用了Destroy(networkObject.gameObject)

之后我们会修改这个特点让它对象池化

private void DeSpawn() {
        //只有服务器可以创建/销毁对象
        if (IsServer) {
            Debug.Log("Despawn");
            networkObject.Despawn();
        }
    }

演示:注意到右边Hierarchy中NetObject的销毁

unity NGO 代码教程:网络上动态生成物体,unity,游戏引擎

使用对象池

INetworkPrefabInstanceHandler接口介绍

首先,你可以很轻松地验证:在host/server中调用networkObject.Despawn()后,不仅networkObject消失(despawn,不应该理解为摧毁),networkObject.gameObject摧毁

那么INetworkPrefabInstanceHandler允许你取消networkObject.gameObject摧毁的过程,改为networkObject.gameObject.SetActive(false)

这样的过程就像是:物体在主机生成本地实例,出池时networkObject.Spawn()在网络上生成,入池时networkObject.Despawn(),在网络上消失,同时主机本地实例隐藏,但他仍然存在,你可以激活该实例然后重复这个过程。

//官方定义
namespace Unity.Netcode {
    public interface INetworkPrefabInstanceHandler {

        //network object调用Despawn()后,在Host和Client中调用
        void Destroy(NetworkObject networkObject);


        //当networkObject.Spawn()调用后,在Client中调用,注意不会在Host和Server中调用。
        //为什么不在Host中调用?我的解释是Host中存在network.gameobject的Inactive实体,Host在本地调用Spawn()就够了
        NetworkObject Instantiate(ulong ownerClientId, Vector3 position, Quaternion rotation);
    }
}

我们直接来看实际使用场景。

单物体对象池

unity官方给出了单物体对象池的示例代码,它的通用性不强,建议了解就好,完全可以用多物体对象池替代

官方代码

public class SinglePooledDynamicSpawner : NetworkBehaviour, INetworkPrefabInstanceHandler
{
    public GameObject PrefabToSpawn;
    public bool SpawnPrefabAutomatically;

    private GameObject m_PrefabInstance;
    private NetworkObject m_SpawnedNetworkObject;


    private void Start()
    {
        // Instantiate our instance when we start (for both clients and server)
        m_PrefabInstance = Instantiate(PrefabToSpawn);

        // Get the NetworkObject component assigned to the Prefab instance
        m_SpawnedNetworkObject = m_PrefabInstance.GetComponent<NetworkObject>();

        // Set it to be inactive
        m_PrefabInstance.SetActive(false);
    }

    private IEnumerator DespawnTimer()
    {
        yield return new WaitForSeconds(2);
        m_SpawnedNetworkObject.Despawn();
        StartCoroutine(SpawnTimer());
        yield break;
    }

    private IEnumerator SpawnTimer()
    {
        yield return new WaitForSeconds(2);
        SpawnInstance();
        yield break;
    }

    /// <summary>
    /// Invoked only on clients and not server or host
    /// INetworkPrefabInstanceHandler.Instantiate implementation
    /// Called when Netcode for GameObjects need an instance to be spawned
    /// </summary>
    public NetworkObject Instantiate(ulong ownerClientId, Vector3 position, Quaternion rotation)
    {
        m_PrefabInstance.SetActive(true);
        m_PrefabInstance.transform.position = transform.position;
        m_PrefabInstance.transform.rotation = transform.rotation;
        return m_SpawnedNetworkObject;
    }

    /// <summary>
    /// Client and Server side
    /// INetworkPrefabInstanceHandler.Destroy implementation
    /// </summary>
    public void Destroy(NetworkObject networkObject)
    {
        m_PrefabInstance.SetActive(false);
    }

    public void SpawnInstance()
    {
        if (!IsServer)
        {
            return;
        }

        if (m_PrefabInstance != null && m_SpawnedNetworkObject != null && !m_SpawnedNetworkObject.IsSpawned)
        {
            m_PrefabInstance.SetActive(true);
            m_SpawnedNetworkObject.Spawn();
            StartCoroutine(DespawnTimer());
        }
    }

    public override void OnNetworkSpawn()
    {
        // We register our network Prefab and this NetworkBehaviour that implements the
        // INetworkPrefabInstanceHandler interface with the Prefab handler
        NetworkManager.PrefabHandler.AddHandler(PrefabToSpawn, this);

        if (!IsServer || !SpawnPrefabAutomatically)
        {
            return;
        }

        if (SpawnPrefabAutomatically)
        {
            SpawnInstance();
        }
    }

    public override void OnNetworkDespawn()
    {
        if (m_SpawnedNetworkObject != null && m_SpawnedNetworkObject.IsSpawned)
        {
            m_SpawnedNetworkObject.Despawn();
        }
        base.OnNetworkDespawn();
    }

    public override void OnDestroy()
    {
        // This example destroys the
        if (m_PrefabInstance != null)
        {
            // Always deregister the prefab
            NetworkManager.Singleton.PrefabHandler.RemoveHandler(PrefabToSpawn);
            Destroy(m_PrefabInstance);
        }
        base.OnDestroy();
    }
}

我们把目光放在INetworkPrefebInstanceHandler接口的2个函数:Instantiate,Destroy

以及2个控制循环生成销毁对象的协程

 private IEnumerator DespawnTimer() {
        yield return new WaitForSeconds(2);
        m_SpawnedNetworkObject.Despawn();
//Destroy在这里自动调用
        StartCoroutine(SpawnTimer());
        yield break;
    }

    private IEnumerator SpawnTimer() {
        yield return new WaitForSeconds(2);
        SpawnInstance();
        yield break;
    }
    public void SpawnInstance() {
        if (!IsServer) {
            return;
        }

        if (prefabInstance != null && m_SpawnedNetworkObject != null && !m_SpawnedNetworkObject.IsSpawned) {
            prefabInstance.SetActive(true);
            m_SpawnedNetworkObject.Spawn();
            //Instantiate在这里自动调用
            StartCoroutine(DespawnTimer());
        }
    }

想象一下:Host/Server 实例化一个对象->Spawn(Sever and Client) ,SetAcitve(true)(Server)->Despawn(Sever and Client),SetActive(false)(Server),Not Destroy

之后重复这个过程

演示

协程等待时间修改成了1秒

unity NGO 代码教程:网络上动态生成物体,unity,游戏引擎

多物体对象池

Untiy 官方给出了一份多物体网络对象池的通用代码,我们来看看它如何使用

Object Pooling | Unity Multiplayer Networking (unity3d.com)

官方代码

这是一份通用性很强的组件,你可以复制并且通过一点点的配置就能让它管理你的对象

using System;
using System.Collections.Generic;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.Assertions;
using UnityEngine.Pool;

namespace Unity.BossRoom.Infrastructure {
    /// <summary>
    /// Object Pool for networked objects, used for controlling how objects are spawned by Netcode. Netcode by default
    /// will allocate new memory when spawning new objects. With this Networked Pool, we're using the ObjectPool to
    /// reuse objects.
    /// Boss Room uses this for projectiles. In theory it should use this for imps too, but we wanted to show vanilla spawning vs pooled spawning.
    /// Hooks to NetworkManager's prefab handler to intercept object spawning and do custom actions.
    /// </summary>
    public class NetworkObjectPool : NetworkBehaviour {
        public static NetworkObjectPool Singleton { get; private set; }

        [SerializeField]
        List<PoolConfigObject> PooledPrefabsList;
        //已生成对象池的预制体
        HashSet<GameObject> m_Prefabs = new HashSet<GameObject>();
        //预制体与管理它的对象池
        Dictionary<GameObject, ObjectPool<NetworkObject>> m_PooledObjects = new Dictionary<GameObject, ObjectPool<NetworkObject>>();

        public void Awake() {
            if (Singleton != null && Singleton != this) {
                Destroy(gameObject);
            }
            else {
                Singleton = this;
            }
        }

        public override void OnNetworkSpawn() {
            // Registers all objects in PooledPrefabsList to the cache.
            foreach (var configObject in PooledPrefabsList) {
                RegisterPrefabInternal(configObject.Prefab, configObject.PrewarmCount);
            }
        }

        public override void OnNetworkDespawn() {
            // Unregisters all objects in PooledPrefabsList from the cache.
            foreach (var prefab in m_Prefabs) {
                // Unregister Netcode Spawn handlers
                NetworkManager.Singleton.PrefabHandler.RemoveHandler(prefab);
                m_PooledObjects[prefab].Clear();
            }
            m_PooledObjects.Clear();
            m_Prefabs.Clear();
        }
        //这段代码会在你试图添加一个非网络对象时报错
        public void OnValidate() {
            for (var i = 0; i < PooledPrefabsList.Count; i++) {
                var prefab = PooledPrefabsList[i].Prefab;
                if (prefab != null) {
                    Assert.IsNotNull(prefab.GetComponent<NetworkObject>(), $"{nameof(NetworkObjectPool)}: Pooled prefab \"{prefab.name}\" at index {i.ToString()} has no {nameof(NetworkObject)} component.");
                }
            }
        }

        /// <summary>
        /// Gets an instance of the given prefab from the pool. The prefab must be registered to the pool.
        /// </summary>
        /// <remarks>
        /// To spawn a NetworkObject from one of the pools, this must be called on the server, then the instance
        /// returned from it must be spawned on the server. This method will then also be called on the client by the
        /// PooledPrefabInstanceHandler when the client receives a spawn message for a prefab that has been registered
        /// here.
        /// </remarks>
        /// <param name="prefab"></param>
        /// <param name="position">The position to spawn the object at.</param>
        /// <param name="rotation">The rotation to spawn the object with.</param>
        /// <returns></returns>
        public NetworkObject GetNetworkObject(GameObject prefab, Vector3 position, Quaternion rotation) {
            var networkObject = m_PooledObjects[prefab].Get();

            var noTransform = networkObject.transform;
            noTransform.position = position;
            noTransform.rotation = rotation;

            return networkObject;
        }

        /// <summary>
        /// Return an object to the pool (reset objects before returning).
        /// </summary>
        public void ReturnNetworkObject(NetworkObject networkObject, GameObject prefab) {
            m_PooledObjects[prefab].Release(networkObject);
        }

        /// <summary>
        /// Builds up the cache for a prefab.
        /// </summary>
        void RegisterPrefabInternal(GameObject prefab, int prewarmCount) {
            NetworkObject CreateFunc() {
                return Instantiate(prefab).GetComponent<NetworkObject>();
            }

            void ActionOnGet(NetworkObject networkObject) {
                networkObject.gameObject.SetActive(true);
            }

            void ActionOnRelease(NetworkObject networkObject) {
                networkObject.gameObject.SetActive(false);
            }

            void ActionOnDestroy(NetworkObject networkObject) {
                Destroy(networkObject.gameObject);
            }

            m_Prefabs.Add(prefab);

            // Create the pool
            m_PooledObjects[prefab] = new ObjectPool<NetworkObject>(CreateFunc, ActionOnGet, ActionOnRelease, ActionOnDestroy, defaultCapacity: prewarmCount);
            // Populate the pool
            var prewarmNetworkObjects = new List<NetworkObject>();
            for (var i = 0; i < prewarmCount; i++) {
                prewarmNetworkObjects.Add(m_PooledObjects[prefab].Get());
            }
            foreach (var networkObject in prewarmNetworkObjects) {
                m_PooledObjects[prefab].Release(networkObject);
            }

            // Register Netcode Spawn handlers
            NetworkManager.Singleton.PrefabHandler.AddHandler(prefab, new PooledPrefabInstanceHandler(prefab, this));
        }
    }

    [Serializable]
    struct PoolConfigObject {
        public GameObject Prefab;
        public int PrewarmCount;
    }
    //PooledPrefabInstanceHandler will handle it on the client(s) when the network object's Spawn or Despawn method is called,
    //via its Instantiate and Destroy methods. Inside those methods, the PooledPrefabInstanceHandler simply calls the pool to get the corresponding object, or to return it.

    class PooledPrefabInstanceHandler : INetworkPrefabInstanceHandler {
        GameObject m_Prefab;
        NetworkObjectPool m_Pool;

        public PooledPrefabInstanceHandler(GameObject prefab, NetworkObjectPool pool) {
            m_Prefab = prefab;
            m_Pool = pool;
        }
        //你可以很轻易的验证这2个函数的调用时机
        //当networkObject.Spawn()调用后,在Client中调用,注意不会在Host和Server中调用。
        NetworkObject INetworkPrefabInstanceHandler.Instantiate(ulong ownerClientId, Vector3 position, Quaternion rotation) {
            Debug.Log("  NetworkObject INetworkPrefabInstanceHandler.Instantiate");
            return m_Pool.GetNetworkObject(m_Prefab, position, rotation);
        }
        //network object调用Despawn()后,在Host和Client中调用
        void INetworkPrefabInstanceHandler.Destroy(NetworkObject networkObject) {
            Debug.Log("void INetworkPrefabInstanceHandler.Destroy");
            m_Pool.ReturnNetworkObject(networkObject, m_Prefab);
        }
    }

}

配置

创建一个空物体,挂载脚本,添加NetworkObject组件

设置池化预制体,它需要在NetworkPrefebList中注册。

PrewarmCount指的是预制体初始时拥有多少实例,他们在生成时都会被设置成Inactive

unity NGO 代码教程:网络上动态生成物体,unity,游戏引擎

使用它

在unity官方示例游戏中,它的使用非常简单

以下是我写的Debug版本

生成对象:从池中取出对象,再调用Spawn()即可

private void Spawn() {
        //只有服务器可以创建/销毁对象
        if (IsServer) {
            //public GameObject SpawnPrefeb;
            networkObject= NetworkObjectPool.Singleton.GetNetworkObject(SpawnPrefeb, transform.position, Quaternion.identity);

            Debug.Log("Spawn");
            networkObject.Spawn();
        }
    }

销毁对象:获得networkObject的引用,直接调用Despawn()

注意NetworkObjectPoo.ReturnNetworkObject是不需要的,它应该被改成private,只为内部的INetworkPrefabInstanceHandler服务。

private void DeSpawn() {
        //只有服务器可以创建/销毁对象
        if (IsServer) {
            Debug.Log("Despawn");
            networkObject.Despawn();
        }
    }

演示

在控制栏中能够看到各个函数的调用情况,INetworkPrefabInstanceHandler.Instantiate明显没有在Host中调用,可以验证它会在client中调用,这里不再展示unity NGO 代码教程:网络上动态生成物体,unity,游戏引擎

RPC实现法?

RPC也能够实现,但是不清楚性能怎么样

基本思路是把SetAcitve同步给其他客户端就行

这里就留个悬念吧,日后有空尝试一下。

如果有人能有兴趣实现,请一定联系我,我会毫不吝啬地加上你的大名!文章来源地址https://www.toymoban.com/news/detail-784962.html

到了这里,关于unity NGO 代码教程:网络上动态生成物体的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Unity上接入手柄,手柄控制游戏物体移动

    1、unity软件上安装system input 组件。菜单栏【window】-【Packag Manager】打开如下界面,查找Input System,并且安装。 2、安装成功后插入手柄到windows上,打开菜单栏上【window】--【Analysis】--【Input Debuger】 进入Input Debug界面,可以看到手柄设备能被Unity识别。 3、双击【XinputControllerW

    2024年04月15日
    浏览(31)
  • Unity场景物体动态合批

    1)Unity场景物体动态合批 ​2)Unity内置音频Android平台播放延迟问题 3)对Unity Package中的Shader打包避免冗余的方案 4)UnityEditor PropertyField并排显示错误 这是第307篇UWA技术知识分享的推送。今天我们继续为大家精选了若干和开发、优化相关的问题,建议阅读时间10分钟,认真读

    2024年02月08日
    浏览(28)
  • Unity之NetCode多人网络游戏联机对战教程(1)

    官网链接:https://docs-multiplayer.unity3d.com/netcode/current/about/ Netcode for GameObjects(NGO)是专为Unity构建的高级网络库。它能够在网络会话中将 GameObject 和 世界数据 同时发送给多名玩家。使用NGO不必关心低级协议和网络框架。 打开一个unity项目,在菜单栏中选择 Window Package Manager

    2024年02月07日
    浏览(37)
  • Unity切割图片:把一张图片中的物体裁成多个单独的游戏物体

    首先选中需要切割的图片素材,将Inspector面板中的 Sprite Mode 由默认的 Single改为Multiple ; 然后点击 Sprite Editor ; 第一步之后便会弹出下例面板,然后 点击Slice菜单 ,继续点击,便 会自动被分割好 ,最后 点击右上角的Apply 。 点击Apply后在右下角会弹出一个小的面板,你可以

    2024年02月05日
    浏览(34)
  • 【Unity】Unity开发学习和项目实践02——创建第一个Unity项目和游戏物体

    创建第1个Unity项目 打开Unity hub,点击新项目 以下有四处地方需要注意选择: 1.Unity编辑器版本 2.项目模板 3.项目名称 4.项目保存位置 点击创建项目 ok,进入编辑器了 把编辑器界面布局稍微改一下,改成2by3 点击Edit 点击 project settings,这是对我们所创建工程的设置 此外还有对

    2024年01月25日
    浏览(34)
  • Unity 3D游戏开发+脚本编程完整指南:Unity脚本概览-控制物体的运动

    教程相关资源 Unity 3D游戏开发+脚本编程完整指南(工程文件+PPT).zip Unity 脚本概览 脚本编写并不困难,但是如果直接从细节开始讲起,会让 读者难以看到脚本编程的全貌。因此本章不急于阐述脚本编写 的细节,只介绍简单的修改物体位置、处理用户输入和检测碰 撞的方法

    2024年02月21日
    浏览(51)
  • Unity之获取游戏物体对象或组件的几个方法

    在unity的实际项目开发中,往往都要获取游戏内的各种物体,比如玩家(Player),怪物(Monster),或者其他,那有什么方法可以快速获取呢,下面就来介绍unity获取游戏对象的几个方法 GameObject.Find GameObject.Find 通过游戏物体的名称获取对象 使用方法 缺点: 会遍历当前整个场

    2024年02月01日
    浏览(30)
  • unity给子物体动态添加boxcollider(碰撞盒)

    在工作中遇到过给物体拍照截图的功能,由于物体是动态加载并且大小不一,但是要求拍的照片正好被物体填充,因此需要动态计算物体大小,从而进行调整,这种方法也可以进行相机检测物体是否在视野内。使用方法非常简单,只需要将要计算的物体放到空物体的子物体下

    2024年02月11日
    浏览(26)
  • Unity之NetCode多人网络游戏联机对战教程(4)--连接申请ConnectionApproval

    没看过前面的教程请先阅读前面的教程,本期将会讲到Netcode联机的申请,当一个Client想连接进来,应向Server发送申请联机的信息,然后由服务端向客户端下发数据,所有数据应该在服务端,而不是在客户端。 举几个常见的例子需要用到 ConnectionApproval 的场景 使用密码加入房

    2024年02月03日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包