네트워크

바뀔 때 알려주는 동기화 값과, 누구나 보낼 수 있는 형식 있는 메시지. 씬에 준비할 것은 없습니다.

업데이트 2026-09-10

한 월드의 두 클라이언트가 무언가를 맞추는 방법은 두 가지이고, 잘못 고르면 조용히 어긋납니다.

동기화 변수는 모두가 들고 있는 사실입니다. 점수, 문이 열렸는지, 누구 차례인지. 십 분 뒤에 들어온 사람도 누가 다시 보내주지 않아도 현재 값을 받습니다.

메시지는 한 번 일어난 일입니다. 공격이 맞았다, 라운드가 시작됐다, 버튼을 눌렀다. 이것을 변수로 보내면 아무 일도 없어 보이는 방식으로 어긋납니다. 같은 값을 두 번 넣어도 변화는 한 번이라 같은 공격 두 번이 한 번이 됩니다. 늦게 들어온 사람은 보지도 못한 전투의 마지막 공격을 방금 일어난 일처럼 받습니다. 그리고 쓸 수 있는 쪽은 소유자뿐이라, 두 사람이 같은 대상을 치면 변수를 두고 다투다 한쪽 공격이 아무 말 없이 사라집니다.

이 패키지는 그 둘 다이고, 보통 어긋나는 부분을 대신 맡습니다.

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 이 오브젝트 아무것도 쌓이지 않고 아무것도 전달되지 않습니다. 콘솔에 한 번 알립니다
NetworkBuschannel.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/에 있고, 제외 목록과 마찬가지로 커밋됩니다. 개발자 한 사람의 취향이 아니라 씬을 누가 소유하는가에 관한, 프로젝트 자체의 사실이기 때문입니다.

문제가 발생했습니다 새로 고침