[VR Chat] 특정 플레이어에게 이벤트 보내기

정강산·2023년 2월 9일
0

VR Chat 개발일지

목록 보기
1/1

SendCustomNetworkEvent의 약점

Udon의 SendCustomNetworkEvent는 두 가지 옵션 밖에 없다. NetworkEventTarget.All 또는 NetworkEventTarget.Owner. 즉 임의의 플레이어에게 이벤트를 보낼 수는 없다.

Remote Player를 의자에 앉혀보자

(잘못된) 첫번째 시도

[UdonBehaviourSyncMode(BehaviourSyncMode.Manual)]
public class Chair : UdonSharpBehaviour
{
    [SerializeField] private VRCStation station;

    public void Seat(VRCPlayerApi player)
    {
    	station.UseStation(player); // 권한문제!
    }
}

Udon은 Remote player를 텔레포트, 앉게 할 수 없다. 따라서 해당 플레이어가 직접 함수를 호출해야한다.

(잘못된) 두번째 시도

[UdonBehaviourSyncMode(BehaviourSyncMode.Manual)]
public class Chair : UdonSharpBehaviour
{
    [UdonSynced] private int _playerId;

    [SerializeField] private VRCStation station;

    public void Seat(VRCPlayerApi player)
    {
    	RequestSerialization(); // _playerId 동기화 요청
        SendCustomNetworkEvent(NetworkEventTarget.ALl, "SeatEvent");
    }
    
    public void SeatEvent()
    {
    	// _playerId가 동기화 됐을까???
    	if (!VRCPlayerApi.GetPlayerById(_playerId).isLocal)
        	return;
           
    	station.UseStation(Networking.LocalPlayer);
    }
}

의자에 앉히고 싶은 _playerId를 동기화하고, SeatEvent를 모든 플레이어에게 요청했다.

무엇이 문제인지 알겠는가?
RequestSerialization에 의한 _playerId의 동기화가 SeatEvent호출보다 먼저 일어날 것이라는 보장이 없다!

성공한 시도 (줄 알았던)

[UdonBehaviourSyncMode(BehaviourSyncMode.Manual)]
public class Chair : UdonSharpBehaviour
{
    [SerializeField] private VRCStation station;

    public override void OnOwnershipTransferred(VRCPlayerApi player)
    {
        if (!Networking.IsOwner(gameObject)) return;
        
        station.UseStation(Networking.LocalPlayer);
    }

    public void Seat(VRCPlayerApi player)
    {
        if (player.isLocal)
        {
            station.UseStation(player);
        }
        else
        {
            Networking.SetOwner(player, gameObject);
        }
    }
}

소유권이 넘어가고 호출되는 이벤트인 OnOwnershipTransferred에서 이 의자가 자신의 소유인지 확인하고, 스스로 앉는다.

VRC Docs 참고

회고

Udon에서는 특정 플레이어에게만 이벤트를 보낼 수 있는 방법이 없어서, 소유권 이전을 통한 꼼수가 (지금까지 발견한) 최선의 방법인 것 같다.

+ 버그 & 추가 수정 사항

세 번째 방법의 문제점:
플레이어가 나가거나 할 때 소유권 이전이 일어난다.
이 때도, 플레이어를 앉게 해버리는 버그가 발생한다.

[UdonBehaviourSyncMode(BehaviourSyncMode.Manual)]
public class Chair : UdonSharpBehaviour
{
	[SerializeField] private int _playerId;
    [SerializeField] private VRCStation station;
    
    public override void OnDeserialization()
    {
        if (_playerId == Networking.LocalPlayer.playerId)
            station.UseStation(Networking.LocalPlayer);
    }

    public void Seat(VRCPlayerApi player)
    {
    	_playerId = player.playerId;
        RequestSerialization();
        if (player.isLocal)
        {
            station.UseStation(player);
        }
    }
}

OnDeserialization()을 통해 동기화 타이밍과 버그 모두 해결할 수 있었다.

0개의 댓글