赞
踩
生成一个网络对象有多种办法,但始终
建议先看完第一章: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的销毁
,
首先,你可以很轻松地验证:在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秒
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官方示例游戏中,它的使用非常简单
以下是我写的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中调用,这里不再展示
RPC也能够实现,但是不清楚性能怎么样
基本思路是把SetAcitve同步给其他客户端就行
这里就留个悬念吧,日后有空尝试一下。
如果有人能有兴趣实现,请一定联系我,我会毫不吝啬地加上你的大名!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。