Version: 2020.3
상태 동기화
네트워크 가시성

고급 상태 동기화

Important: UNet is a deprecated solution, and a new Multiplayer and Networking Solution (MLAPI) is under development. For more information and next steps see the information on the Unity MLAPI website.

대부분의 경우 SyncVar만 사용해도 게임 스크립트가 자체 상태를 클라이언트에 직렬화할 수 있습니다. 하지만 좀 더 복잡한 직렬화 코드가 필요한 경우도 있습니다. 이 페이지는 Unity의 일반 SyncVar 기능 이외에 커스터마이즈된 동기화 솔루션이 필요한 숙련된 개발자를 위해 작성되었습니다.

커스텀 직렬화 함수

자체 커스텀 직렬화를 수행하기 위해 NetworkBehaviour에서 가상 함수를 구현하여 SyncVar 직렬화에 사용할 수 있습니다. 다음과 같은 함수를 예로 들 수 있습니다.


public virtual bool OnSerialize(NetworkWriter writer, bool initialState);

public virtual void OnDeSerialize(NetworkReader reader, bool initialState);

initialState 플래그를 사용하면 게임 오브젝트가 처음으로 직렬화된 시점과 점진적 업데이트를 보낼 수 있는 시점을 구분할 수 있습니다. 게임 오브젝트가 처음으로 클라이언트에 전송되는 시점에서는 전체 상태의 스냅샷을 포함해야 하지만, 그 이후 발생하는 업데이트의 경우 점진적 변화만을 전송하여 대역폭을 절약할 수 있습니다. SyncVar 후크 함수는 initialState가 true인 경우 호출되지 않으며, 점진적 업데이트의 경우에만 호출됩니다.

한 클래스에 SyncVar이 있는 경우, 이러한 함수의 구현이 클래스에 자동으로 추가됩니다. 즉, SyncVar가 있는 클래스는 커스텀 직렬화 함수를 가질 수 없습니다.

OnSerialize 함수는 업데이트가 보내져야 한다는 점을 표시하기 위해 true를 반환해야 합니다. true를 반환하게 되면 스크립트의 잔류 부분이 0으로 설정되며, false를 반환하면 이는 변경되지 않습니다. 따라서 이 기능을 통해 스크립트에 대한 여러 변경점을 프레임마다 보내는 대신 시스템이 준비될 때까지 축적한 다음 한 번에 보낼 수 있습니다.

직렬화 과정

Network Identity 컴포넌트가 있는 게임 오브젝트는 NetworkBehaviour에서 파생된 다수의 스크립트를 가질 수 있습니다. 이 게임 오브젝트를 직렬화하는 과정은 아래와 같습니다.

서버 측:

  • NetworkBehaviour는 더티 마스크가 있습니다. 이 마스크는 OnSerialize에서 syncVarDirtyBits으로서 사용할 수 있습니다.

  • NetworkBehaviour 스크립트의 각 SyncVar에 더티 마스크 비트가 하나씩 할당됩니다.

  • SyncVar의 값이 변경되면 더티 마스크에서 이 SyncVar에 해당하는 비트가 설정됩니다.

  • 또는 SetDirtyBit()를 호출하면 더티 마스크에 직접 작성할 수 있습니다.

  • 서버 업데이트 루프의 일부로서 NetworkIdentity 게임 오브젝트가 서버에서 검사됩니다.

  • NetworkIdentity에 더티로 설정된 NetworkBehaviours가 있는 경우 해당 게임 오브젝트에 대해 UpdateVars 패킷이 생성됩니다.

  • UpdateVars 패킷은 해당 게임 오브젝트의 각 NetworkBehaviourOnSerialize를 호출함으로써 채워집니다.

  • 더티로 설정되지 않은 NetworkBehaviours는 자신의 더티 비트 패킷에 0을 작성합니다.

  • 더티로 설정된 NetworkBehaviours는 자신의 더티 마스크를 쓴 후, 변경된 SyncVars에 대한 값을 씁니다.

  • OnSerializeNetworkBehaviour에 대해 true를 반환하면 더티 마스크는 해당 NetworkBehaviour에 대해 초기화되며, 이 값이 변경될 때까지 다시 전송되지 않습니다.

  • 게임 오브젝트를 찾고 있는 준비된 클라이언트들에 UpdateVars 패킷이 전송됩니다.

클라이언트 측:

  • 어떤 게임 오브젝트에 대한 UpdateVars 패킷을 수신합니다.

  • 해당 게임 오브젝트의 각 NetworkBehaviour 스크립트에 대해 OnDeserialize 함수가 호출됩니다.

  • 해당 게임 오브젝트의 각 NetworkBehaviour 스크립트는 더티 마스크를 읽습니다.

  • NetworkBehaviour의 더티 마스크가 0 이면 OnDeserialize 함수는 더 이상 읽지 않고 반환합니다.

  • 더티 마스크가 0이 아닌 값이면 OnDeserialize 함수는 설정된 더티 비트에 대응하는 SyncVar 값을 읽습니다.

  • SyncVar 후크 함수가 있을 경우, 스트림으로부터 읽은 값을 가지고 이 함수를 호출합니다.

따라서 이 스크립트의 경우에는 다음과 같게 됩니다.


public class data : NetworkBehaviour
{

    [SyncVar]
    public int int1 = 66;

    [SyncVar]
    public int int2 = 23487;

    [SyncVar]
    public string MyString = "Example string";
}

다음 코드 예제는 생성되는 OnSerialize 함수를 보여줍니다.


public override bool OnSerialize(NetworkWriter writer, bool forceAll)
{
    if (forceAll)
    {
        // The first time a GameObject is sent to a client, send all the data (and no dirty bits)
        writer.WritePackedUInt32((uint)this.int1);
        writer.WritePackedUInt32((uint)this.int2);
        writer.Write(this.MyString);
        return true;
    }
    bool wroteSyncVar = false;
    if ((base.get_syncVarDirtyBits() & 1u) != 0u)
    {
        if (!wroteSyncVar)
        {
            // Write dirty bits if this is the first SyncVar written
            writer.WritePackedUInt32(base.get_syncVarDirtyBits());
            wroteSyncVar = true;
        }
        writer.WritePackedUInt32((uint)this.int1);
    }
    if ((base.get_syncVarDirtyBits() & 2u) != 0u)
    {
        if (!wroteSyncVar)
        {
            // Write dirty bits if this is the first SyncVar written
            writer.WritePackedUInt32(base.get_syncVarDirtyBits());
            wroteSyncVar = true;
        }
        writer.WritePackedUInt32((uint)this.int2);
    }
    if ((base.get_syncVarDirtyBits() & 4u) != 0u)
    {
        if (!wroteSyncVar)
        {
            // Write dirty bits if this is the first SyncVar written
            writer.WritePackedUInt32(base.get_syncVarDirtyBits());
            wroteSyncVar = true;
        }
        writer.Write(this.MyString);
    }

    if (!wroteSyncVar)
    {
        // Write zero dirty bits if no SyncVars were written
        writer.WritePackedUInt32(0);
    }
    return wroteSyncVar;
}

다음 코드 예제는 OnDeserialize 함수를 보여줍니다.


public override void OnDeserialize(NetworkReader reader, bool initialState)
{
    if (initialState)
    {
        this.int1 = (int)reader.ReadPackedUInt32();
        this.int2 = (int)reader.ReadPackedUInt32();
        this.MyString = reader.ReadString();
        return;
    }
    int num = (int)reader.ReadPackedUInt32();
    if ((num & 1) != 0)
    {
        this.int1 = (int)reader.ReadPackedUInt32();
    }
    if ((num & 2) != 0)
    {
        this.int2 = (int)reader.ReadPackedUInt32();
    }
    if ((num & 4) != 0)
    {
        this.MyString = reader.ReadString();
    }
}

NetworkBehaviour에 마찬가지로 직렬화 함수가 있는 베이스 클래스가 있다면 베이스 클래스 함수 역시 호출되어야 합니다.

게임 오브젝트 상태 업데이트를 위해 생성된 UpdateVar 패킷은 클라이언트로 전송되기 이전 버퍼에 축적될 수 있다는 점을 상기해야 합니다. 따라서 한 개의 전송 레이어 패킷에는 여러 게임 오브젝트에 대한 업데이트를 포함할 수 있습니다.

상태 동기화
네트워크 가시성
Copyright © 2023 Unity Technologies
优美缔软件(上海)有限公司 版权所有
"Unity"、Unity 徽标及其他 Unity 商标是 Unity Technologies 或其附属机构在美国及其他地区的商标或注册商标。其他名称或品牌是其各自所有者的商标。
公安部备案号:
31010902002961