Help understanding update frequency & activeSelf==false behavior

I am sync’ing a public property shown below:

public Color ShapeColor
{
    get => m_shapeColor;
    set
    {
        m_shapeColor = value;
        Debug.Log($"color is now: {value}");
        ColorDidChange();
    }
}

ClientA creates a shape, gives it a color. ClientB gets the info sent over, and the shape shows up. So far so good.
Then a few odd things happen:

  1. I get that log printed every frame.
  2. If I change the color on ClientA, then disable the gameObject, ClientB does not get the color change, but the log keeps printing (with the old color)
  3. if I manage to get the GameObject to be disabled on ClientB (requires some hackery at the moment), the logs stop.

So, my questions:

  1. How do I know what data is actually being sent across the wire, and how often? I would assume I only send changed data, but I see no way to validate that.
  2. Can I force “send data” just before an object is disabled? This is a concern in general, but especially for setting a “this thing is disabled” flag. Right now the only way I’ve found to do it is replace gameObject.SetActive(false) with a method that sets a synchronized variable ShouldBeInactive to true, then waits 2 seconds, and actually turns the game object off.

Thanks,
Bill

1 Like

Hi Bill, welcome! Thanks for posting your (very good) question. I’ve asked someone to come and take a look as soon as they can.

Hey Bill, welcome to the forums! To answer your questions:

  1. Yes, data is sent over the network only when it changes. However you do see the log being printed (i.e. the setter is called) because of interpolation. Even if no new values are coming from remote, there might still be a new value from the interpolation algorithm.
    On the CoherenceSync you can find a setting to decide when to run interpolation. I believe it can be changed at runtime too.
  2. Generally, we don’t support a lot the idea of disabling the whole synced GameObject, if you still want to do things with it. Obviously this would create issues because if you disable the whole gameobject, then CoherenceSync doesn’t work as intended and you might get a desync. We suggest other workflows, like disabling components (which is a flag that can be synced).
    One way to do it could be to have a child object that represents graphics/interactivity, and a parent that has the CoherenceSync. To disable an object, you would use a SendCommand in which you tell the other CoherenceSync to disable its child. Just an idea, there could be other ways.

My colleague @Valentino also suggested to look into the [OnValueSynced] attribute. In his words:

That is invoked not for every packet, but for every value change. So if interpolation is enabled, then it will invoke on every frame when there was a change. If interpolation is None, then it will be invoked for every packet (as he is expecting).

But this is just a collection of thoughts. If you describe the use case in more detail, I’ll be happy to help with more concrete ideas!

2 Likes

Hey Ciro, thanks for the reply.

  1. I set interpolation to “none”, but it still calls my setter every frame. From testing, it would seem that the interpolation setting determines what value to set, but not how often.
    It seems obvious now, but it’d be nice if the docs for [OnValueSynced] specified that the method being called has to be public. It took me a while to figure out my callback wasn’t being called due to access limitations. That being said, now that it’s working, it is working well, and meets my needs.
  2. From your comments, and then some experiments, I’ve figured out that SetActive won’t get passed to the client, but Destroy will. There are two reasons I don’t Destroy the game objects in question. One is pooling, which I understand will have some features in your 1.0 release. The other is undo. Ours is a drawing app, and a user can choose to “delete” any part of the drawing. For the sake of undo, I just turn it off so that hitting undo simply turns it back on. For now I can use a SendCommand to handle this, but it’d be really nice if activeSelf was sync-able.

One follow up question for SendCommand usage: Is it safe for a given object to call SendCommand, then immediately disable (SetActive(false)) itself? Or do I need that object to tell some persistent controller to SendCommand?

Thanks,
Bill

Thanks for the inputs!

  1. You’re right, we should mention that the method needs to be public! I think the same happens with SendCommand, or maybe there’s a warning in that case? But yeah, needs to go into the docs in both cases.
  2. Can you sync the enabled flag of components? I imagine the stroke is rendered by a MeshRenderer at the end of the day.

On your last question, I’m asking the team for confirmation.

hmm, I guess that would work. My gut says that it’s bad to leave game objects on but invisible when I don’t want them to exist. I don’t know that I have any actual justification for that opinion though. Unity will call a few extra Update() methods, but that’s probably all? Performance is so tight in VR that I’m hesitant to have extra things on in the hierarchy, but realistically it probably doesn’t matter. So this is a viable alternative.

I’d prefer a checkbox to sync active flag, but this is an ok bandaid while I wait for that feature :slight_smile:

1 Like