네트워크
바뀔 때 알려주는 동기화 값과, 누구나 보낼 수 있는 형식 있는 메시지. 씬에 준비할 것은 없습니다.
한 월드의 두 클라이언트가 무언가를 맞추는 방법은 두 가지이고, 잘못 고르면 조용히 어긋납니다.
동기화 변수는 모두가 들고 있는 사실입니다. 점수, 문이 열렸는지, 누구 차례인지. 십 분 뒤에 들어온 사람도 누가 다시 보내주지 않아도 현재 값을 받습니다.
메시지는 한 번 일어난 일입니다. 공격이 맞았다, 라운드가 시작됐다, 버튼을 눌렀다. 이것을 변수로 보내면 아무 일도 없어 보이는 방식으로 어긋납니다. 같은 값을 두 번 넣어도 변화는 한 번이라 같은 공격 두 번이 한 번이 됩니다. 늦게 들어온 사람은 보지도 못한 전투의 마지막 공격을 방금 일어난 일처럼 받습니다. 그리고 쓸 수 있는 쪽은 소유자뿐이라, 두 사람이 같은 대상을 치면 변수를 두고 다투다 한쪽 공격이 아무 말 없이 사라집니다.
이 패키지는 그 둘 다이고, 보통 어긋나는 부분을 대신 맡습니다.
public class Door : UdoniteBehaviour
{
private Synced<bool> open = new Synced<bool>();
public override void Start()
{
open.Subscribe(OnOpenChanged);
Room.Subscribe<Knocked>(OnKnocked);
}
public override void Interact()
{
open.Value = !open.Value; // 복제되고, 각 클라이언트의 핸들러가 돈다
Room.Send(new Knocked()); // 보낸 쪽까지 포함해 모두가 한 번씩 듣는다
}
private void OnOpenChanged(bool value) => panel.SetActive(value);
private void OnKnocked(Knocked message) => sound.Play();
}
Room.Send 는 룸 전체로 가고, 누구나 보낼 수 있습니다. 특정 오브젝트에 관한 메시지는 그 오브젝트 위를 흐르며, 오브젝트 그 자체인 비헤이비어에 this.Send 라고 씁니다. 그쪽은 그 오브젝트의 소유가 필요합니다. 둘 다 메시지에 있습니다.
open.Value = ... 는 변수에 쓰고 복제를 요청합니다. 잊기 쉬운 RequestSerialization 은 없습니다. Subscribe 는 값이 도착했을 때 모든 클라이언트에서 핸들러를 부르는데, 절반만 도착한 상태가 아니라 갱신이 다 들어온 뒤입니다.
무엇을 누가 소유해야 하는가
소유권은 이 패키지의 것이 아니라 VRChat 의 것입니다. 복제되는 것은 소유자의 쓰기뿐이고, 다른 것은 소유자가 아닐 때 무슨 일이 벌어지느냐입니다.
| 하는 일 | 소유가 필요한 것 | 소유하지 않으면 |
|---|---|---|
Synced<T> 값 쓰기 |
그 필드를 가진 오브젝트 | 쓰기는 자기 클라이언트에 머물고, 다음에 도착한 값이 덮어씁니다 |
이 오브젝트의 채널로 this.Send |
이 오브젝트 | 아무것도 쌓이지 않고 아무것도 전달되지 않습니다. 콘솔에 한 번 알립니다 |
NetworkBus 로 channel.Send |
그 버스의 오브젝트 | 같습니다 |
Room.Send |
필요 없음 | 각 클라이언트가 자기 플레이어 오브젝트의 버스에 쓰므로 다툴 것이 없습니다 |
NetworkTransform 움직이기 |
그 오브젝트 | 소유자가 보내고 나머지는 모두 그쪽으로 보간합니다 |
소유자가 필요 없는 것은 룸 메시지뿐이고, 어느 클라이언트나 움직여도 될 때 그것을 고르는 주된 이유입니다. 오브젝트의 채널도 동기화 필드도 결국 쓰는 쪽이 하나입니다. 누구나 움직여도 되는 월드는 먼저 소유를 가져옵니다.
if (!Ownership.IsMine(gameObject))
Ownership.Claim(gameObject);
픽업이나 Interact 라면 대개 이미 가져와 있습니다. MasterOwned 는 반대 경우로, 이것을 상속하면 조율하는 클라이언트만 소유할 수 있습니다. 다른 누구도 쓰면 안 되는 상태를 위한 것입니다.
여기 있는 것
Synced<T> |
모든 클라이언트가 가지며, 바뀌면 알려주는 변수 |
Room |
형식 있는 메시지 보내기와 구독. 누가 있는지, 누가 조율하는지 |
NetworkEvent |
바이트 뭉치가 아니라 필드를 가진 메시지 |
NetworkBus |
오브젝트 자신의 채널. 그 오브젝트에 관한 이야기용 |
NetworkTransform |
위치·회전·스케일. 보간과 순간이동 감지 포함 |
Ownership |
누가 가졌는지, 가져오기, 넘기기 |
MasterOwned |
조율하는 클라이언트만 가질 수 있는 오브젝트의 기반 클래스 |
준비하지 않아도 되는 것
룸 메시지는 씬의 오브젝트 하나에 실려야 합니다. 쓰는 대상은 클라이언트가 소유해야 하고, 월드가 도는 중에는 그런 것을 만들 수 없기 때문입니다. 패키지가 알아서 추가하고, 그 이유를 콘솔에 한 번 남깁니다. 보내지 않는 월드에는 아무것도 추가되지 않습니다.
직접 두고 싶다면 VRCPlayerObject 가 붙은 오브젝트에 NetworkBus 를 넣으면 그쪽이 쓰입니다. 이름이 아니라 컴포넌트로 찾습니다.
Udonite → Open Window → Settings → Net에 이것을 끄는 토글이 있습니다. 끄면 월드가 무엇을 보내든 패키지는 씬을 전혀 건드리지 않습니다. 버스를 두는 일은 이제 온전히 당신 몫입니다. 기본값은 켜짐입니다. 이 설정은 ProjectSettings/에 있고, 제외 목록과 마찬가지로 커밋됩니다. 개발자 한 사람의 취향이 아니라 씬을 누가 소유하는가에 관한, 프로젝트 자체의 사실이기 때문입니다.