I would like to share my thoughts about sync problems that you can not avoid in VRChat world creation.
First of all, I'll talk about the two most important things to know. VRChat doesn't synchronize anything without specific treatments except for player positions but synchronizes with either BroadCastType or VRC_ObjectSync in most cases.
When you set other than Local to VRC_Trigger BroadCastType, the action synchronizes to other players. The important thing here is that only an action, which fulfilled its requirements, synchronizes. It doesn't matter whether other player clients fulfilled or not.
All the players in the same instance can see the same situation while they do the same actions. That's how BroadCastType works. However, actions don't synchronize immediately because of latency. It would be messed up if there were other effects, such as gravity. Therefore, you need to use the VRC_ObjectSync component. It literally synchronizes objects in real time.
You might think it's useful. Okay, now I have a question for you.
"BroadCastType works with the player who fulfilled the requirements. Who is the player VRC_ObjectSync works with?"
The answer is the player who has ownership.
Ownership moves when a player picks it up, or VRC client runs the TakeOwnerShip function, etc. Spawn objects are owned by a player who runs the SpawnObject action.
You should know who has ownership if you want to synchronize objects that move with scripts.
As I noted, VRChat objects are synchronized by one player. If a number of players engaged, it would be messed up because of latency. Basically, only one player should engage.
Local BroadCastType doesn't do anything to other players. You can make a specific player client do something while enabling a component or changing animator parameters in Local. You should prevent needless synchronizations taking advantage of Local since some VRC_Trigger requirements would be fulfilled by some players.
- All players, Always
- Some players, Not Always
It would be better that only master client handles it unless specific player has to do it. When BroadCastType is set as Master, master client does actions and sync while master client fulfills requirements. You can narrow it to master client with firing your custom trigger, which is set as Master, from ActivateCustomTrigger, which is set as Always.
It might be a problem with ownership if an object with VRC_ObjectSync had complicated structure. In that case, you should simplify synchronization. You shouldn't synchronize the object itself but synchronize other simple object which isn't related to the object and has VRC_ObjectSync and follows the object by AutoCam or FollowTarget and LookatTarget. The method is good for the problem of quaking while moving players with VRC_Station.
I uploaded my Unity package on GoogleDrive. Import it to your project and see the following sections.
It's a door that opens/closes by animation.
Changing animator parameters with Always is enough to synchronize simple animation, like the door has. I didn't use Toggle but used True/False on two objects.
Recently the sync method with VRC_SyncAnimation was established. It would be better to use it if there were various animations.
It's a simple count down timer.
When the timer starts, the custom trigger, which is named Timer, runs every second by the animation event on animator. If it ran in Local, it would get time difference because of each client processing speed.
At first, the timer runs in Always. Then it processes in Master. Only master client does it and it synchronizes every client.
It's a gun that doesn't spawn bullets but recycles with VRC_SceneResetPosition.
You must notice the trigger actions OnPickUp. At first, a player who picks it up takes ownership. Second, it disables every player bullets' colliders and FollowTarget in Always. Third, it enables them in Local. That's how to separate the player who synchronizes and the other players who don't.
FollowTarget on the players who don't synchronize is disabled since it would overwrite the VRC_ObjectSync work. FollowTarget on the player who synchronizes is enabled in Local since it stores the bullets in the sky while not being used. When the player shoots, the ResetPosition function and the custom trigger which has the AddForce action run by animation event.
There are a lot of sync methods out there. Since my method is just one of them, you have to find the most appropriate method for your world. When you find, could you share it for us?