참고: UNet은 지원이 중단되었으며 향후 Unity에서 삭제될 예정입니다. 현재 새로운 시스템이 개발 중입니다. 자세한 내용과 다음 단계는 이 블로그 포스트와 FAQ를 참조하십시오. |
대부분의 경우 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
패킷은 해당 게임 오브젝트의 각 NetworkBehaviour
에 OnSerialize
를 호출함으로써 채워집니다.
더티로 설정되지 않은 NetworkBehaviours
는 자신의 더티 비트 패킷에 0을 작성합니다.
더티로 설정된 NetworkBehaviours
는 자신의 더티 마스크를 쓴 후, 변경된 SyncVars에 대한 값을 씁니다.
OnSerialize
가 NetworkBehaviour
에 대해 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
패킷은 클라이언트로 전송되기 이전 버퍼에 축적될 수 있다는 점을 상기해야 합니다. 따라서 한 개의 전송 레이어 패킷에는 여러 게임 오브젝트에 대한 업데이트를 포함할 수 있습니다.
Did you find this page useful? Please give it a rating:
Thanks for rating this page!
What kind of problem would you like to report?
Thanks for letting us know! This page has been marked for review based on your feedback.
If you have time, you can provide more information to help us fix the problem faster.
Provide more information
You've told us this page needs code samples. If you'd like to help us further, you could provide a code sample, or tell us about what kind of code sample you'd like to see:
You've told us there are code samples on this page which don't work. If you know how to fix it, or have something better we could use instead, please let us know:
You've told us there is information missing from this page. Please tell us more about what's missing:
You've told us there is incorrect information on this page. If you know what we should change to make it correct, please tell us:
You've told us this page has unclear or confusing information. Please tell us more about what you found unclear or confusing, or let us know how we could make it clearer:
You've told us there is a spelling or grammar error on this page. Please tell us what's wrong:
You've told us this page has a problem. Please tell us more about what's wrong:
Thank you for helping to make the Unity documentation better!
Your feedback has been submitted as a ticket for our documentation team to review.
We are not able to reply to every ticket submitted.