2

On a purposed LAN setup, I want Unity's NetworkTransform to simply send every single frame. (Mode is "Sync Transform", not rigidbody, etc.)

I'm wondering how to do this .. if you just set the send rate to 0.00001 will it in fact do it every frame that the object is moving?

(The Inspector interface only allows up to 29Hz, simply set it in code if you want smaller values.)

I was also thinking something like on a script......

 void Update() { .. SetDirtyBit( ? ); }

would that do it?

(And, what do you use as a bitmask there?)

It really takes forever to determine this sort of thing experimentally. Obviously Unity's doco is a joke. And I couldn't google anyone who'd already figured it. Any experience in this?


Secondly...

Further information. We did some experiments and indeed if you set it to a very small value, it does seem to sync the NetworkTransform every single frame.

(Of course, it doesn't bother sending if it has not moved, as @Programmer points out below.)

H O W E V E R this does not produce smooth results for kinematic objects or camera positions. I don't know why; it has to do with the Update loop and exactly when the change takes place. But what you get is a "very fine jitter".


Thirdly...

I did an experiment, simply using Commands and Rpcs, to send some information every frame (actually from one client to another).

I specifically used a purpose solo ethernet network and the apps do little or nothing other than send the info every frame (ie "manually" just using Command/Rpc), ie on Update().

In fact, it still drops many frames, it's often not sent. (Let's say, it misses 10 or 20 in 10 seconds worth.)

Good grief!

Do not bother doing this if you need some info sent "every frame", say on a LAN system.

Community
  • 1
  • 1
Fattie
  • 27,874
  • 70
  • 431
  • 719
  • Afaik NetworkTransform also tries to a certain limit to predict movements if using RigidBody .. maybe that causes the jitter? For me the NetworkTransform never worked well and I ended up writing a custom one with Commands, Rpcs and interpolation of the position. https://forum.unity.com/threads/network-transform-lags.334718/ – derHugo Nov 14 '18 at 06:42
  • 1
    (Right, I was not using RigidBody. Yes, their system is trash; this is more of a curiosity investigation :) ) – Fattie Nov 14 '18 at 06:54
  • Yeah thats why [they start over](https://support.unity3d.com/hc/en-us/articles/360001252086-UNet-Deprecation-FAQ) – derHugo Nov 14 '18 at 06:58
  • ah I did not know [that](https://support.unity3d.com/hc/en-us/articles/360001252086-UNet-Deprecation-FAQ) @derHugo . I heard it from you first! Geesh ... yet another attempt by Unity at networking :/ – Fattie Nov 14 '18 at 07:01
  • yeah we all hope that will be a better one .. so far we ended up writing our complete own networking using very low level socket connections (after all `c#` is still `c` ;) ) – derHugo Nov 14 '18 at 07:03
  • right :/ well we will see ... – Fattie Nov 14 '18 at 07:06

2 Answers2

2

if you just set the send rate to 0.00001 will it in fact do it every frame that the object is moving?

Yes, but when the Object's transform is not moving or rotating, it's not updated over the network regardless of the value of NetworkTransform.sendInterval.

I was also thinking something like on a script......

void Update() { .. SetDirtyBit( ? ); }

Yes. You need to call SetDirtyBit every frame if you need the network update to be sent for the object.

And, what do you use as a bitmask there?

The SetDirtyBit function is technically the function version of the SyncVar attribute. The argument is not really documented that much but if SyncVar marked variable is changed, its dirty mask is changed to 1. This also means that you should pass 1 to the SetDirtyBit function if you want network update to be sent for the object.

You should also use it from a function marked with the [Command] attribute instead of directly from the Update function. Something like below:

void Update()
{
    if (isLocalPlayer)
    {
        CmdUpdateTransform();
    }
}

[Command]
void CmdUpdateTransform()
{
    SetDirtyBit(1u);
}

While Unity claims you can update the transform by simply using the SetDirtyBit function. I don't think this is true. The statement is missing lots of information. You will need OnSerialize to serialize and send the data and OnDeserialize to receive and de-serialize it. The SetDirtyBit function is simply used to determine which data to send. It's used to reduce bandwidth issues that comes from using uNET.

The code below shows what the use of SetDirtyBit should look like. It might need few changes to work on your side.

public class Example : NetworkBehaviour
{
    private const uint SEND_POSITION = 1u;
    private const uint SEND_ROTATION = 2u;
    private const uint SEND_SCALE = 3u;

    void Update()
    {
        uint dirtyBit = syncVarDirtyBits;

        dirtyBit |= SEND_POSITION;
        dirtyBit |= SEND_ROTATION;
        dirtyBit |= SEND_SCALE;

        if (isLocalPlayer)
        {
            CmdUpdateTransform(dirtyBit);
        }
    }

    [Command]
    void CmdUpdateTransform(uint dirtyBit)
    {
        SetDirtyBit(dirtyBit);
    }

    public override bool OnSerialize(NetworkWriter writer, bool initialState)
    {
        if ((this.syncVarDirtyBits & SEND_POSITION) != 0u)
        {
            writer.Write(this.transform.position);
        }

        if ((this.syncVarDirtyBits & SEND_ROTATION) != 0u)
        {
            writer.Write(this.transform.rotation);
        }

        if ((this.syncVarDirtyBits & SEND_SCALE) != 0u)
        {
            writer.Write(this.transform.localScale);
        }
        return true;
    }

    public override void OnDeserialize(NetworkReader reader, bool initialState)
    {

        if ((this.syncVarDirtyBits & SEND_POSITION) != 0u)
        {
            Vector3 pos = reader.ReadVector3();
        }

        if ((this.syncVarDirtyBits & SEND_ROTATION) != 0u)
        {
            Quaternion rot = reader.ReadQuaternion();
        }

        if ((this.syncVarDirtyBits & SEND_SCALE) != 0u)
        {
            Vector3 scale = reader.ReadVector3();
        }
    }
}
Programmer
  • 121,791
  • 22
  • 236
  • 328
  • Good one. So to summarize, (1) it seems that `SetDirtyBit` is actually only relevant to SyncVars? It does not work for `NetworkTransform` ? – Fattie Nov 14 '18 at 03:15
  • SetDirtyBit is the manual way of sending data instead of using SyncVar which will automatically do that for you when you make a variable with the SyncVars attribute. SetDirtyBit is not related to NetworkTransform. You can use it to send any type of data over the network. I think that the documentation is not clear on this. Calling SetDirtyBit will cause Unity to call OnSerialize() and OnDeserialize() and there you can add the custom data to send like the example in my answer. For example,you can send the position, rotation and scale of the object. – Programmer Nov 14 '18 at 05:15
  • The good side of using it is that you can decide when to update the variable unlike `SyncVar` which simply updates based on the send rate. This is really important when it comes to bandwidth management. Imaging having ~12 variables for each 12 player you want to update over the network and they are marked as `SyncVar`, they will be updating at whatever the send rate is set to. You can't control when to stop them from updating over the network. With `SetDirtyBit`, you can and it saves bandwidth and processing time. – Programmer Nov 14 '18 at 05:21
  • 1
    Ah ok. So SetDirtyBit does not relate to NetworkTransform, and it does not relate to SyncVars ! It's just "a way to send info (using the Serialize functions)". Is that about right??! Thanks! :) – Fattie Nov 14 '18 at 05:25
  • Yes, that's correct and I think the doc is saying that if you want to update the transform manually, you should set `sendInterval` to `0`, call SetDirtyBits then use OnSerialize() and OnDeserialize() to send the transform information. They left that last part out. – Programmer Nov 14 '18 at 05:30
  • Hmm, what does sendInterval relate to SetDirtyBits ? Is it basically the case: *"A value of sendInterval of zero, indicates I want to 'control myself' which frames to use the SetDirtyBits mechanism; I will manually use SetDirtyBits to send when I want to"* ... Is that about right do you think ?? – Fattie Nov 14 '18 at 05:32
  • One thing that confuses me. Say you want to send some info (position, rotation) from A to B. In fact, you can do that fine just using ordinary old Commands and Rpcs. (If you have to go from "client to client" you just send it "via" the server, one line of code no problem.) Why really would one use the "Dirty Bits / Serialization" method, if you can just use an ordinary Command? Perhaps I misunderstand something ............. – Fattie Nov 14 '18 at 05:33
  • They are not related. By setting sendInterval to 0, you are just telling Unity to stop sending and updating the transform. From there, you can decide to send the transform manually with SetDirtyBits if you wish. They just gave you an option to stop updating NetworkTransform and the ability to manually do it yourself if you wish. – Programmer Nov 14 '18 at 05:52
  • There are many reasons to use custom serialization. Quote from [here](https://forum.unity.com/threads/bandwidth-of-clientrpc-vs-sending-messages.429093/#post-2774759) *"And OnSerialize is needed to get the most up-to-date information for new spawned objects and also for late connecting players (otherwise the object will be at the wrong rotation/velocity/etc. until the first message is received)"* – Programmer Nov 14 '18 at 05:53
  • Another reason is bandwidth. Quote from the doc *"The first time an object is sent to a client, it must include a full state snapshot, but subsequent updates can save on bandwidth by including only incremental changes. "*. I am sure there many other reasons but bandwidth the top. Note that you **can't** use custom serialize if you have syncvar in your script. It's worth knowing that. It won't work. – Programmer Nov 14 '18 at 05:53
  • Regarding the "0" flag, that makes perfect sense, got it. – Fattie Nov 14 '18 at 06:27
  • *"you can't use custom serialize if you have syncvar in your script. I won't work."* Got it, good one. – Fattie Nov 14 '18 at 06:34
  • Annoyingly you can't easily use this from client->client, heh! – Fattie Nov 14 '18 at 06:57
  • Wow. the current flood of Unity questions on here are at a really low level! – Fattie Nov 14 '18 at 12:51
  • *"I don't know why; it has to do with the Update loop and exactly when the change takes place. But what you get is a "very fine jitter"."* I've never seen Unity component doing smooth movement fine especially when Rigidbody is involved. You're responsible for smoothing it. Things to know: **1.** You have to send the transform data manually. **2.** Don't synchronize the transform data right when you receive them right away. You buffer them with a **tiny** delay. The delay makes the buffer possible. **3.** Interpolate between the buffered data. – Programmer Nov 14 '18 at 16:30
  • As I said, no Rigidbody – Fattie Nov 14 '18 at 16:32
  • It's hard to smooth between frames :-) – Fattie Nov 14 '18 at 16:32
  • I know you said kinematic objects. What I said above applies to both. You have a add a controllable tiny delay, buffer the data and update them manfully. – Programmer Nov 14 '18 at 16:37
  • Not really, if they're truly *exactly the same* they'll simply render exactly the same. Like playing the same (deterministic) scene twice. (As long as the Unity frame engine is doing everything ths same way on both sides.) I just did it! I don't mean in the case where there's dynamic input from (say) a user, as you say .. – Fattie Nov 14 '18 at 17:31
1

In the Unity Docs It says

If sendInterval is zero, updates will be sent at the end of the frame when dirty bits are set for that script. Note that setting the value of a SyncVar will automatically set dirty bits.

So you could use your own script using NetworkSettings

using UnityEngine.Networking;

[NetworkSettings(channel = 1, sendInterval = 0.0f)]
class MyScript : NetworkBehaviour
{
    [SyncVar]
    int value;
}

However SyncVar only works Server -> Client.

But you can use SetDirtyBit to manually set the behaviour dirty.


Or you probably could also use InvokeSyncEvent to synchronize some values directly.


Here I found a bit more information on Dirty bits.

derHugo
  • 83,094
  • 9
  • 75
  • 115
  • I really did not know about `NetworkSettings`, or `InvokeSyncEvent` ! I believe though those are not for `NetworkTransform` , only SyncVars ?? IDK – Fattie Nov 14 '18 at 03:14
  • Never tried it but it is also possible that if you call a Command in Update it might really be send every frame while sendInterval only applies to the SyncVar. I'm not sure on this. In general you would have to write your own custom NetworkTransform script e.g. using a combination of [SyncVar] and Command (Might be something like this but there are other examples, too). Unfortunately the whole Unity docs concentrates on Server -> Client syncronisation... – derHugo Nov 14 '18 at 04:48