Version: 2019.3
언어: 한국어
게임 오브젝트 스폰
네트워크 권한

커스텀 스폰 함수

참고: UNet은 지원이 중단되었으며 향후 Unity에서 삭제될 예정입니다. 현재 새로운 시스템이 개발 중입니다. 자세한 내용과 다음 단계는 이 블로그 포스트FAQ를 참조하십시오.

스폰 핸들러 함수를 사용하면 클라이언트의 프리팹에서 스폰된 게임 오브젝트를 만들 때 기본 동작을 커스터마이즈할 수 있습니다. 스폰 핸들러 함수를 사용하면 게임 오브젝트를 스폰하거나 삭제하는 방식을 온전히 제어할 수 있습니다.

ClientScene.RegisterSpawnHandler로 함수를 등록하여 클라이언트 게임 오브젝트를 스폰하거나 삭제하십시오. 서버는 게임 오브젝트를 직접 생성한 후 이 기능을 통해 클라이언트에 스폰합니다. 이 기능은 게임 오브젝트의 에셋 ID와 두 개의 함수 델리게이트를 사용합니다. 하나는 클라이언트에서 게임 오브젝트를 생성하는 데 사용되고, 다른 하나는 클라이언트에서 게임 오브젝트를 삭제하는 데 사용됩니다. 에셋 ID는 동적일 수 있습니다. 또는 스폰하고자 하는 프리팹 게임 오브젝트에서 찾은 에셋 ID를 사용할 수도 있습니다.

스포너와 언스포너는 게임 오브젝트 서명을 가지고 있어야 하며, 이 서명은 고수준 API에서 정의됩니다.


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

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

스폰 함수에 전달된 에셋 ID는 프리팹의 경우 NetworkIdentity.assetId에서 찾을 수 있으며, 이는 자동으로 생성됩니다. 동적 에셋 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 coinAssetId = coinPrefab.GetComponent<NetworkIdentity>().assetId;

// register handlers for an existing prefab you'd like to custom spawn
ClientScene.RegisterSpawnHandler(coinAssetId, SpawnCoin, UnSpawnCoin);

// spawn a coin - SpawnCoin is called on client
NetworkServer.Spawn(gameObject, coinAssetId);

스폰 함수 자체는 델리게이트 서명을 통해 구현됩니다. 아래는 SpawnCreature는 동일하게 보이나 다른 스폰 로직을 사용하는 코인 스포너입니다.


public GameObject SpawnCoin(Vector3 position, NetworkHash128 assetId)
{
    return (GameObject)Instantiate(m_CoinPrefab, position, Quaternion.identity);
}
public void UnSpawnCoin(GameObject spawned)
{
    Destroy(spawned);
}

커스텀 스폰 함수를 사용하는 경우 게임 오브젝트를 제거하는 대신 언스폰하는 것이 유용할 때가 있습니다. NetworkServer.UnSpawn를 호출하면 됩니다. 호출하게 되면 클라이언트에 메시지가 보내져 게임 오브젝트를 언스폰하도록 하므로 클라이언트에서 언스폰 함수가 호출됩니다. 이 함수가 호출되더라도 게임 오브젝트가 제거되지는 않습니다.

호스트의 경우 서버에 이미 존재하므로 로컬 클라이언트에 대해 게임 오브젝트가 스폰되지 않는다는 점을 상기해야 합니다. 다시 말해, 스폰 핸들러 함수는 호출되지 않습니다.

커스텀 스폰 핸들러로 게임 오브젝트 풀 설정

아래는 커스텀 스폰 핸들러를 사용하여 아주 간단한 게임 오브젝트 풀링 시스템을 만드는 예시입니다. 스포닝이나 언스포닝을 하면 게임 오브젝트가 풀에 추가되거나 풀에서 삭제됩니다.


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 GameObject " + obj.name + " at " + position);
                obj.transform.position = position;
                obj.SetActive (true);
                return obj;
            }
        }
        Debug.LogError ("Could not grab GameObject 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 GameObject " + spawned.name);
        spawned.SetActive (false);
    }
}

이 관리자를 사용하려면 비어 있는 새 게임 오브젝트를 생성하고 “SpawnManager”라고 이름을 지정합니다. SpawnManager란 이름의 새 스크립트를 생성한 후 새 SpawnManager 게임 오브젝트에 연결합니다. 그런 다음 여러 번 스폰하고자 하는 프리팹을 P*refab 필드로 드래그한 후 오브젝트 풀 크기(기본: 5)를 설정합니다.

마지막으로 플레이어 이동에 사용하는 스크립트에서 SpawnManager에 대한 레퍼런스를 설정합니다.


SpawnManager spawnManager;

void Start()
{
    spawnManager = GameObject.Find("SpawnManager").GetComponent<SpawnManager> ();
}

플레이어 로직은 아래와 같은 내용을 포함할 수 있으며, 이를 통해 코인을 움직이고 발사합니다.


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();
    }
}
  • 플레이어 발사 로직에서는 게임 오브젝트 풀을 활용해야 합니다.

[Command]
void CmdFire()
{
    // Set up coin on server
    var coin = spawnManager.GetFromPool(transform.position + transform.forward);  
    coin.GetComponent<Rigidbody>().velocity = transform.forward*4;
    
    // spawn coin on client, custom spawn handler is called
    NetworkServer.Spawn(coin, spawnManager.assetId);
    
    // when the coin is destroyed on the server, it is automatically destroyed on clients
    StartCoroutine (Destroy (coin, 2.0f));
}

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

자동 제거 과정을 통해 게임 오브젝트가 풀로 돌아오는 방법과 재발사 시 재사용되는 방법을 알 수 있습니다.

게임 오브젝트 스폰
네트워크 권한