Generación (spawning) de objetos
Sincronización de Estados

Funciones Personalizadas de Generación (Spawn)

El comportamiento por defecto de crear objetos generados de prefabs en el cliente se puede personalizar al utilizar funciones de manejo de generaciones (spawn). De esta manera usted tiene control control total tanto de cómo usted genera (spawn) el objeto al igual que como lo des-genera (un-spawn). Usted puede registrar funciones para generar y des-generar objetos cliente con ClientScene.RegisterSpawnHandler. El servidor crea objetos directamente y luego los genera en los clientes a través de esta funcionalidad. Esta función toma el ID del objeto y dos delegates de función, uno para manejar la creación de objetos en el cliente y el otro para manejar objetos destruidos en el cliente. El asset ID puede ser uno dinámico o simplemente el asset ID encontrado en el objeto prefab que usted quiere generar (si usted tiene uno).

El spawn (generador) / un-spawner (des-generador) necesitan tener esta firma (signature) de objeto, esto se define en el API de alto nivel.

// Handles requests to spawn objects on the client
public delegate GameObject SpawnDelegate(Vector3 position, NetworkHash128 assetId);

// Handles requests to unspawn objects on the client
public delegate void UnSpawnDelegate(GameObject spawned);

El asset ID pasado a la función generador (spawn) se puede encontrar en NetworkIdentity.assetId para prefabs, dónde es poblado automáticamente. El registro para un asset ID dinámico se maneja así:

// generate a new unique assetId 
NetworkHash128 creatureAssetId = NetworkHash128.Parse("e2656f");

// register handlers for the new assetId
ClientScene.RegisterSpawnHandler(creatureAssetId, SpawnCreature, UnSpawnCreature);

// get assetId on an existing prefab
NetworkHash128 bulletAssetId = bulletPrefab.GetComponent<NetworkIdentity>().assetId;

// register handlers for an existing prefab you'd like to custom spawn
ClientScene.RegisterSpawnHandler(bulletAssetId, SpawnBullet, UnSpawnBullet);

// spawn a bullet - SpawnBullet will be called on client.
NetworkServer.Spawn(gameObject, creatureAssetId);

Las funciones de generación (spawn) en sí son implementadas con la firma (signature) del delegate, aquí hay un generador de una bala pero el SpawnCreature se vería igual pero tendría una lógica diferente de generación (spawn)

public GameObject SpawnBullet(Vector3 position, NetworkHash128 assetId)
{
    return (GameObject)Instantiate(m_BulletPrefab, position, Quaternion.identity);
}
public void UnSpawnBullet(GameObject spawned)
{
    Destroy(spawned);
}

Cuando utilice funciones de generación (spawn), a veces es útil ser capaz de des-generar (unspawn) objetos sin destruirlos. Esto se puede hacer al llamar NetworkServer.UnSpawn. Esto causa que un mensaje se envíe a los clientes para des-generar (un-spawn) el objeto, para que la función de des-generación personalizada sea llamada en los clientes. El objeto no se destruye cuando esta función sea llamada.

Tenga en cuenta que el anfitrión , los objetos no son generados para los clientes locales ya que existen en el servidor. Por lo que ninguna función de manejo de generación será llamada.

Configurar un pool (grupo) de objetos con manejadores de generación personalizada

Aquí hay un ejemplo de cómo usted podría configurar un sistema de object pooling simple con manejadores personalizados de generación. Entonces la generación y des-generación simplemente coloca objetos adentro o afuera del pool (grupo).

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

public class SpawnManager : MonoBehaviour
{
    public int m_ObjectPoolSize = 5;
    public GameObject m_Prefab;
    public GameObject[] m_Pool;

    public NetworkHash128 assetId { get; set; }
    
    public delegate GameObject SpawnDelegate(Vector3 position, NetworkHash128 assetId);
    public delegate void UnSpawnDelegate(GameObject spawned);

    void Start()
    {
        assetId = m_Prefab.GetComponent<NetworkIdentity> ().assetId;
        m_Pool = new GameObject[m_ObjectPoolSize];
        for (int i = 0; i < m_ObjectPoolSize; ++i)
        {
            m_Pool[i] = (GameObject)Instantiate(m_Prefab, Vector3.zero, Quaternion.identity);
            m_Pool[i].name = "PoolObject" + i;
            m_Pool[i].SetActive(false);
        }
        
        ClientScene.RegisterSpawnHandler(assetId, SpawnObject, UnSpawnObject);
    }

    public GameObject GetFromPool(Vector3 position)
    {
        foreach (var obj in m_Pool)
        {
            if (!obj.activeInHierarchy)
            {
                Debug.Log("Activating object " + obj.name + " at " + position);
                obj.transform.position = position;
                obj.SetActive (true);
                return obj;
            }
        }
        Debug.LogError ("Could not grab object from pool, nothing available");
        return null;
    }
    
    public GameObject SpawnObject(Vector3 position, NetworkHash128 assetId)
    {
        return GetFromPool(position);
    }
    
    public void UnSpawnObject(GameObject spawned)
    {
        Debug.Log ("Re-pooling object " + spawned.name);
        spawned.SetActive (false);
    }
}

Para utilizar este manejador haga lo siguiente

  • Esto funciona en una escena como la que está en la guía Empezando.
  • Cree un nuevo game object vacío llamado “SpawnManager”
  • Cree un script SpawnManager para el código de arriba y agregue este a un nuevo objeto SpawnManager
  • Arrastre un prefab que usted quiera generar múltiples veces al campo prefab y configure el tamaño (el predeterminado es 5).
  • Configure una referencia al spawn manager (administrador de generación) en el script de movimiento del jugador
SpawnManager spawnManager;

void Start()
{
    spawnManager = GameObject.Find("SpawnManager").GetComponent<SpawnManager> ();
}
  • La lógica del jugador debería contener algo así para moverse y disparar balas
void Update()
{
    if (!isLocalPlayer)
        return;
    
    var x = Input.GetAxis("Horizontal")*0.1f;
    var z = Input.GetAxis("Vertical")*0.1f;
    
    transform.Translate(x, 0, z);

    if (Input.GetKeyDown(KeyCode.Space))
    {
        // Command function is called on the client, but invoked on the server
        CmdFire();
    }
}
  • En la lógica de disparo (fire) del jugador, asegúrese de utilizar el pool de objetos
[Command]
void CmdFire()
{
    // Set up bullet on server
    var bullet = spawnManager.GetFromPool(transform.position + transform.forward);  
    bullet.GetComponent<Rigidbody>().velocity = transform.forward*4;
    
    // spawn bullet on client, custom spawn handler will be called
    NetworkServer.Spawn(bullet, spawnManager.assetId);
    
    // when the bullet is destroyed on the server it wil automatically be destroyed on clients
    StartCoroutine (Destroy (bullet, 2.0f));
}

public IEnumerator Destroy(GameObject go, float timer)
{
    yield return new WaitForSeconds (timer);
    spawnManager.UnSpawnObject(go);
    NetworkServer.UnSpawn(go);
}

La destrucción automática muestra cómo los objetos son devueltos al pool (grupo) y re-utilizados cuando usted dispara nuevamente.

Generación (spawning) de objetos
Sincronización de Estados