Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.

Unity Multiplayer Encoding Vector2 and Vector3 variables into single int or float and back

Discussion in 'Multiplayer' started by emotitron, Dec 28, 2016.

  1. emotitron

    emotitron

    Joined:
    Oct 9, 2016
    Posts:
    41
    I am starting to optimize a game for mobile pvp, and to that end I am looking to reduce my traffic as much as possible.

    The maps will rarely exceed a cube of (-50, -50, -50) to (50, 50, 50) units with a required resolution of 1 decimal point. Vector3 data for each projectile fire needs even less data. That means I could nicely fit my coordinates into 10bit chunks that give me a range of values from -51.2 to 51.2 for each axis.

    Integers are 32bits, and I need 30bits per vector.

    So my first easiest path seems to be to strip all V3 data down to a 3 digit integer (the decimal removed by multiplying times 10) and then crush all of that into a single integer for transmission, and then decode it back to a V3 on the receiving end.

    So before I go doing this, my question is - am I recreating someone else's work I can just download and can I save myself a couple hours of learning how to bitwise encode/decode this? If not, does anyone have some code snippets they would recommend for doing this efficiently? I am sure I will not be the last person looking to do this.
     
  2. Apparaten_

    Apparaten_

    Joined:
    Jul 9, 2013
    Posts:
    45
  3. emotitron

    emotitron

    Joined:
    Oct 9, 2016
    Posts:
    41
    It is intended to be people in a small space moving at higher than should work speeds for PvP over mobile, so if I can cut my V3 data in a third - it should be of value. It might not be noticeable on its own, but as I start piling up syncvars and command/rpcs every little bit is likely going to matter. I need every millisecond I can get. I also will be relying less on player authority than games of this time for hiding the latency. Player movement is likely going to be the only item under player authority - the rest is all server - so all latency will be felt and seen.

    I intend to squeeze every bit down as much as humanly possible. Thanks for the link, will add that to my reading when I attack this.
     
    Apparaten_ likes this.
  4. emotitron

    emotitron

    Joined:
    Oct 9, 2016
    Posts:
    41
    Code (CSharp):
    1.     public int encodeVector3ToInt(Vector3 v) {
    2.         //Vectors must stay within the -512 to 512 range per axis - no error handling coded here
    3.         //Add 512 to get numbers into the 0-1024 range rather than -512 to 512 range
    4.         //Multiply by 10 to save one decimal place from rounding
    5.         int xcomp = Mathf.RoundToInt((v.x * 10)) + 512;
    6.         int ycomp = Mathf.RoundToInt((v.y * 10)) + 512;
    7.         int zcomp = Mathf.RoundToInt((v.z * 10)) + 512;
    8.         return xcomp + ycomp * 1024 + zcomp * 1048576;
    9.     }
    10.     public Vector3 decodeVector3FromInt(int i) {
    11.         //Get the leftmost bits first. The fractional remains are the bits to the right.
    12.         // 1024 is 2 ^ 10 - 1048576 is 2 ^ 20 - just saving some calculation time doing that in advance
    13.         float z = Mathf.Floor(i / 1048576);
    14.         float y = Mathf.Floor ((i - z * 1048576) / 1024);
    15.         float x = (i - y * 1024 - z * 1048576);
    16.         // subtract 512 to move numbers back into the -512 to 512 range rather than 0 - 1024
    17.         return new Vector3 ((x - 512) / 10, (y - 512) / 10, (z - 512) / 10);
    18.     }
    This is my dirty working code to solve the problem. If you have a world that is smaller than 102.4x102.4x102.4 units and a resolution of .1 units is good enough - this will reduce your network traffic. If anyone has faster/cleaner code methods to do this same thing - PLEASE pass them along.

    There are also two unused bits on the left of the int. So they could be used to double the x & z limits to 204.8 units if your level is more wide than tall. Just replace the 1048576 and 1024 numbers with the appropriate 2^X result for the number of bits you want for each axis.
     
    Last edited: Dec 28, 2016
  5. emotitron

    emotitron

    Joined:
    Oct 9, 2016
    Posts:
    41
    The resolution was a bit low, the alternative is to use shorts as a cheap half-float. That knocks 12 bytes per tick down to 6 (plus whatever unet overhead there is in each serialized command).

    Code (CSharp):
    1.     [Command(channel = Channels.DefaultUnreliable)]
    2.     private void CmdSendPositionShort(short x, short y, short z)
    3.     {
    4.         _lastPosition.Set(
    5.             (float)x / 100f,
    6.             (float)y / 100f,
    7.             (float)z / 100f);
    8.     }
    9.  
    10.         CmdSendPositionShort (
    11.                 (short)(transform.position.x * 100),
    12.                 (short)(transform.position.y * 100),
    13.                 (short)(transform.position.z * 100));
     
  6. emotitron

    emotitron

    Joined:
    Oct 9, 2016
    Posts:
    41
    And my latest resting place for truncating vectors and encoding them into a single variable (and back). Note I am only using 3/4s of the ulongs 64bits. 16 bits per axis. The remaining unused flags I may use to pass other info later such things as animation states and other flags.

    Code (CSharp):
    1.     public ulong encodeVector3ToULong(Vector3 v) {
    2.         //Vectors must stay within the -320.00 to 320.00 range per axis - no error handling is coded here
    3.         //Adds 32768 to get numbers into the 0-65536 range rather than -32768 to 32768 range to allow unsigned
    4.         //Multiply by 100 to get two decimal place
    5.         ulong xcomp = (ulong)(Mathf.RoundToInt((v.x * 100f)) + 32768);
    6.         ulong ycomp = (ulong)(Mathf.RoundToInt((v.y * 100f)) + 32768);
    7.         ulong zcomp = (ulong)(Mathf.RoundToInt((v.z * 100f)) + 32768);
    8.         //Debug.Log ("comps " + xcomp + " " + ycomp + " " + zcomp);
    9.         return xcomp + ycomp * 65536 + zcomp * 4294967296;
    10.     }
    11.     public Vector3 decodeVector3FromULong(ulong i) {
    12.         //Debug.Log ("ulong " +i);
    13.         //Get the leftmost bits first. The fractional remains are the bits to the right.
    14.         // 1024 is 2 ^ 10 - 1048576 is 2 ^ 20 - just saving some calculation time doing that in advance
    15.         ulong z = (ulong)(i / 4294967296);
    16.         ulong y = (ulong)((i - z * 4294967296) / 65536);
    17.         ulong x = (ulong)(i - y * 65536 - z * 4294967296);
    18.         //Debug.Log (x + " " + y + " " + z);
    19.         // subtract 512 to move numbers back into the -512 to 512 range rather than 0 - 1024
    20.         return new Vector3 (((float)x - 32768f) / 100f, ((float)y - 32768f) / 100f, ((float)z - 32768f) / 100f);
    21.     }
    Hopefully this wasn't all for nothing - my understanding of what goes on with Syncvar behind the scenes is limited so I don't know if regular network compression would be condensing a V3 to a comparable size. Again, there must be a more efficient way of doing this than decimal math, but right now my concern is crushing network usage more than sparing the CPU.
     
    Stiffx likes this.
  7. abhayaagrawal

    abhayaagrawal

    Joined:
    Sep 30, 2016
    Posts:
    8
    @emotitron
    this works for me. but I am unable to understand why are you multiplying
    return xcomp + ycomp * 65536 + zcomp * 4294967296;
    in case of encoding.

    and in case of decoding how are you getting value of z and y

    1. ulong z = (ulong)(i / 4294967296);
    2. ulong y = (ulong)((i - z * 4294967296) / 65536);

      it will be great if you can explain the logic