请尊重原作者的工作,转载时请务必注明转载自:www.xionggf.com
 

Custom Spawn functions

 
 
The default behaviour of creating spawned objects from prefabs on the client can be customized by using spawn handler functions. This way you have full control of how you spawn the object as well as how you un-spawn it. You can register functions to spawn and un-spawn client objects with ClientScene.RegisterSpawnHandler. The server creates objects directly and then spawn them on the clients though this functionality. This function takes the asset ID of the object and two function delegates, one to handle creating objects on the client and one to handler destroying objects on the client. The asset ID can be a dynamic one or just the asset ID found on the prefab object you want to spawn (if you have one). 在客户端上,spawned object的默认行为,可以使用spawn handler函数进行定制。你可以完全控制如何去spawn和unspawn一个object,在客户端上使用ClientScene.RegisterSpawnHandler()函数注册Spawn和unspawn回调函数。这个函数接收两个delegate类型的变量,一个用来处理object的创建,另一个处理object的销毁。服务端直接创建object,然后在客户端上,通过这些回调函数spawn对应的object。这些回调函数使用一个asset的ID和两个delegate。这个asset ID可以是一个动态的数值,或者是预设上的一个asset ID值
 
The spawn / un-spawner need to have this object signature, this is defined in the high level API. 这些spawn / unspawner函数需要拥有object signature。这在HLAPI中定义
 
// 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);
 
The asset ID passed to the spawn function can be found on NetworkIdentity.assetId for prefabs, where it is populated automatically. The registration for a dynamic asset ID is handled liked his: 传给spawn函数的asset ID可以从预设中的NetworkIdentity组件的assetId属性获取。这些都是自动生成的,注册这些动态的asset ID可以以下的方式去操作:
 
// 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);
 
The spawn functions themselves are implemented with the delegate signature, here is the bullet spawner but the SpawnCreature would look the same but have different spawn logic 用来进行spawn操作的函数本身是通过delegate的签名实现的。下面是一个子弹生成器的代码:
 
public GameObject SpawnBullet(Vector3 position, NetworkHash128 assetId)
{
    return (GameObject)Instantiate(m_BulletPrefab, position, Quaternion.identity);
}

public void UnSpawnBullet(GameObject spawned)
{
    Destroy(spawned);
}
 
When using custom spawn functions, it is sometimes useful to be able to unspawn objects without destroying them. This can be done by calling NetworkServer.UnSpawn. This causes a message to be sent to clients to un-spawn the object, so that the custom un-spawn function will be called on the clients. The object is not destroyed when this function is called. 当使用自定义的spawn函数时,有时候有必要在unspawn一个object的时候,不destroy它。这个方式可以通过调用NetworkServer.UnSpawn完成之,这个调用操作将会发送一个消息给客户端,通知客户端去unspawn这个object。所以自定义的un-spawn函数将会调用。如果自定义的unspwan函数不destroy object本身的话,这个object将会不被destroy
 
Note that on the host, object are not spawned for the local client as they already exist on the server. So no spawn handler functions will be called. 要注意的是,在HOST上。由于在server上已经存在了一个object,所以object不会再被spawned。所以没有spawn handler函数会被调用
 
Setting up an object pool with custom spawn handlers
 
Here is an example of how you might set up a very simple object pooling system with custom spawn handlers. Spawning and unspawning then just puts objects in or out of the pool. 下面的示例代码演示了使用一个很简单的对象池系统(object pooling system)作为自定义的对象生成器。spawn和unspawn操作仅仅是从池中取出object,或者放回object到池中。
 
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);
    }
}
 
To use this manager do the following 以下的流程演示了如何使用这个管理器
 
This works in a scene like the one in the Getting Started guide. 如同在Getting Started guide章节中一样,以下的流程也是在一个scene中进行
 
1 Create a new empty game object called “SpawnManager”
2 Create a SpawnManager script for the code above and add that to a new SpawnManager object
3 Drag a prefab you want to spawn multiple times to the prefab field and set the size (default is 5).
4 Set up a reference to the spawn manager in the player movement script
1 新创建一个空的game object,命名为“SpawnManager”
2 用上面的代码,创建一个SpawnManager脚本,并把这个脚本挂接上SpawnManager game object中去。
3 拖放上你想spawn的object的预设体到对应的面板的槽位中去,并且设置数量
4 在控制player移动的脚本中,创建一个对该spawn manager的引用。
 
SpawnManager spawnManager;

void Start()
{
    spawnManager = GameObject.Find("SpawnManager").GetComponent<SpawnManager> ();
}
 
The player logic could contain something like this to move and fire bullets 在Player的逻辑中包含了如下的移动和发射子弹的代码
 
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();
    }
}
 
In the fire logic on the player, make it use the object pool 在Player的开火逻辑代码中,使用这个对象池
 
[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);
}
 
The automatic destruction shows how the objects are returned to the pool and re-used when you fire again. 上面的自动析构操作展示了如何让object返回到池中,然后又重用之