Author Topic: Sync player states and animations  (Read 4487 times)

lynohd

  • Newbie
  • *
  • Thank You
  • -Given: 0
  • -Receive: 0
  • Posts: 21
    • View Profile
Sync player states and animations
« on: May 25, 2016, 02:34:27 PM »
Hello, back at it again with another problem ::)

I want to tell other players whether im running or walking or standing still and make other players see the appropiate animation for this,
but I dont fully understand how I would go about doing it. I have tried like 5 different methods

Destroying the Animation GO on the local client and still having it on other players client and assigning the players speed too the animator but that doesnt seem to work,
Im really confsed and just dont understand how I would og about doing this


does scripts and gameobjects that doesnt belong too other players be disabled or destroyed?
or simply just put a if(!tno.isMine) return; on them so that the code wont run on them?


ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: Sync player states and animations
« Reply #1 on: May 26, 2016, 01:07:01 PM »
I always suggest keeping animations local. Have animations be based on whatever you're doing -- for example you have a movement axis set to 0.7 meaning you are moving at 70% velocity, which will automatically be used to blend your animation state between walking and running. This will look the most natural. Sync your input axes, have clients interpret that data to control animations.

lynohd

  • Newbie
  • *
  • Thank You
  • -Given: 0
  • -Receive: 0
  • Posts: 21
    • View Profile
Re: Sync player states and animations
« Reply #2 on: May 26, 2016, 05:15:34 PM »
Okay thanks alot, I dont have a rigidbody controller so checking for movements and velocity isnt that easy so this is what i went ahead and did instead.
I dont know if this is good or reliable but for NOW its working atleast.. You see for yourself and tell me what i could improve :-)



  1. public class LocalPlayerController : PlayerController {
  2.     [System.NonSerialized]
  3.     public TNObject tno;
  4.  
  5.     public override void Start()
  6.     {
  7.        
  8.         tno = GetComponent<TNObject>();
  9.         if (tno.isMine)
  10.         {
  11. //to prevent players from being stuck in the air when theres no movement / inputs being sent
  12.             InvokeRepeating("SyncMyPos", 1.5f, 1.5f);
  13.             base.Start();
  14.         }else this.enabled = false;
  15.     }
  16.  
  17.     public void SyncMyPos()
  18.     {
  19.         if(tno.isMine)
  20.         tno.SendQuickly(1, Target.Others, transform.position, transform.rotation, state);
  21.     }
  22.     public override void Update()
  23.     {
  24.         if (tno.isMine)
  25.         {
  26.             base.Update();
  27.             if (base.moveDirection.x != 0 || base.moveDirection.z != 0 || base.moveDirection.y >= 0 || base.velMagnitude >= 3f)
  28.             {
  29.                 tno.SendQuickly(1, Target.Others, transform.position, transform.rotation, base.state);
  30.             }
  31.         }
  32.     }
  33.     [RFC(1)]
  34.     public void UpdatePlayer(Vector3 pos, Quaternion rot, int playerState)
  35.     {
  36.         if (tno.isMine) return;
  37.         Vector3 newpos = Vector3.Lerp(transform.position, pos, 5f);
  38.         Quaternion newrot = Quaternion.Slerp(transform.rotation, rot, 5f);
  39.         int NewPlayerState = playerState;
  40.         transform.position = newpos;
  41.         transform.rotation = newrot;
  42.     }
  43. }
  44.  

« Last Edit: May 26, 2016, 05:33:00 PM by lynohd »

cmifwdll

  • Global Moderator
  • Sr. Member
  • *****
  • Thank You
  • -Given: 0
  • -Receive: 149
  • Posts: 285
  • TNet Alchemist
    • View Profile
Re: Sync player states and animations
« Reply #3 on: May 26, 2016, 06:33:28 PM »
You're sending an RFC in Update, that's going to wreak havoc on your PC and at every hop between you and your server. Don't do that ever.
Also, you're sending your full position and rotation each time, this is exactly against Aren's advice.
I think this is more inline with what Aren always talks about w/ syncing inputs:
  1. public static class ExtensionMethods
  2. {
  3.         public static short ToShort(this float value)
  4.         {
  5.                 return (short)UnityEngine.Mathf.Round(value * short.MaxValue / 1000);
  6.         }
  7.  
  8.         public static float ToSingle(this short value)
  9.         {
  10.                 return (float)((double)value) * 1000 / short.MaxValue;
  11.         }
  12. }
  13.  
  14. public class SyncMovement : TNBehaviour
  15. {
  16.         [SerializeField]
  17.         float PacketsPerSecond = 10f;
  18.        
  19.         void Start()
  20.         {
  21.                 StartCoroutine(SyncInput());
  22.         }
  23.        
  24.         void Update()
  25.         {
  26.                 // probably do some animation stuff here according to the CharacterController
  27.         }
  28.        
  29.         IEnumerator SyncInput()
  30.         {
  31.                 while(TNManager.isInChannel)
  32.                 {
  33.                         // note: most Input.Getblahblah calls should be made in the Update() function, GetAxis is the exception.
  34.                         int packedInputs = 0;
  35.                         packedInputs |= Input.GetAxis("Horizontal").ToShort();
  36.                         packedInputs <<= 16;
  37.                         packedInputs |= Input.GetAxis("Vertical").ToShort();
  38.                         tno.Send(NetworkManager.NETID_SYNC_INPUT, Target.OthersSaved, packedInputs);
  39.                         yield return new WaitForSeconds(1f / PacketsPerSecond);
  40.                 }
  41.         }
  42.        
  43.         [RFC(NetworkManager.NETID_SYNC_INPUT)]
  44.         public void ReceiveInput(int packed)
  45.         {
  46.                 short verticalCompact = (short)(packed & short.MaxValue);
  47.                 packed >>= 16;
  48.                 short horizontalCompact = (short)(packed & short.MaxValue);
  49.                 float verticalInput = verticalCompact.ToSingle();
  50.                 float horizontalInput = horizontalCompact.ToSingle();
  51.                 // set the CharacterController's values according to the input received
  52.         }
  53. }
  54.  

It's not complete, but the idea is there at least. Send your inputs instead of your results. Receive the inputs and calculate the results on each client. With this, handling animations should be literally the exact same as handling it on the local player. Absolutely no distinction between the two: no extra code, nada.

Note: I've found this method of float compression to be accurate from the hundredth (0.01f) to the hundreds (999f); this can be tweaked. You don't need to use float compression or bit packing, but I'm in a show-off mood today :P

lynohd

  • Newbie
  • *
  • Thank You
  • -Given: 0
  • -Receive: 0
  • Posts: 21
    • View Profile
Re: Sync player states and animations
« Reply #4 on: May 27, 2016, 01:14:57 AM »
I legit have  no idea wtf i just read...  :o
Im kinda "new" to programming and I havent gotten that far into the advanced stuff. i have no idea what "<<=" means, could u please explain? :)
Thanks

cmifwdll

  • Global Moderator
  • Sr. Member
  • *****
  • Thank You
  • -Given: 0
  • -Receive: 149
  • Posts: 285
  • TNet Alchemist
    • View Profile
Re: Sync player states and animations
« Reply #5 on: May 27, 2016, 02:44:17 AM »
Sorry, you don't have to use bitpacking.
Fixed up the relevant parts for you:
  1. IEnumerator SyncInput()
  2. {
  3.         while(TNManager.isInChannel)
  4.         {
  5.                 // note: most Input.Getblahblah calls should be made in the Update() function, GetAxis is the exception.
  6.                 short horizontalInput = Input.GetAxis("Horizontal").ToShort();
  7.                 short verticalInput = Input.GetAxis("Vertical").ToShort();
  8.                 tno.Send(NetworkManager.NETID_SYNC_INPUT, Target.OthersSaved, horizontalInput, verticalInput);
  9.                 yield return new WaitForSeconds(1f / PacketsPerSecond);
  10.         }
  11. }
  12.  
  13. [RFC(NetworkManager.NETID_SYNC_INPUT)]
  14. public void ReceiveInput(short horizontal, short vertical)
  15. {
  16.         float horizontalInput = horizontal.ToSingle();
  17.         float verticalInput = vertical.ToSingle();
  18.         // set the CharacterController's values according to the input received
  19. }
  20.  

But to answer your question, packedInputs <<= 16; is the same as writing packedInputs = packedInputs << 16;
The "<<" operator in this context shifts packedInputs to the left 16 bits.
  1. byte packedByte = 9;
  2. // packedByte bits:
  3. // 00001001
  4. // if you were to do Debug.Log(packedByte.ToString()) the output would be 9.
  5. packedByte = (byte)((int)packedByte << 4);
  6. // packedByte bits:
  7. // 10010000
  8. // if you were to do Debug.Log(packedByte.ToString()) the output would be 144.
  9. // you can then fit another value between 0-15 into packedByte
  10. // inserting the value via the OR operator
  11. packedByte = (byte)((int)packedByte | 6);
  12. // packedByte bits:
  13. // 10010110
  14. // if you were to do Debug.Log(packedByte.ToString()) the output would be 150.
  15. // now you have two values stored in a single byte: 9 and 6.
  16.  
More info on MSDN: https://msdn.microsoft.com/en-us/library/a1sway8w.aspx

In case you were confused by the |= operator as well, that's a bitwise OR.
The wikipedia article explains the bitwise operators really well: https://en.wikipedia.org/wiki/Bitwise_operation#OR

But the relevant part of my previous post stands: Send your inputs instead of your results. Receive the inputs and calculate the results on each client. With this, handling animations should be literally the exact same as handling it on the local player. Absolutely no distinction between the two: no extra code, nada.

lynohd

  • Newbie
  • *
  • Thank You
  • -Given: 0
  • -Receive: 0
  • Posts: 21
    • View Profile
Re: Sync player states and animations
« Reply #6 on: May 27, 2016, 02:55:52 AM »
Ahh I see! looks like I've got new stuff to learn then!
thanks alot for your help! i appreciate it alot!

also one more question if I may,
what is the ? :o
  1. NetworkManager.NETID_SYNC_INPUT

cmifwdll

  • Global Moderator
  • Sr. Member
  • *****
  • Thank You
  • -Given: 0
  • -Receive: 149
  • Posts: 285
  • TNet Alchemist
    • View Profile
Re: Sync player states and animations
« Reply #7 on: May 27, 2016, 01:30:21 PM »
  1. public class NetworkManager
  2. {
  3.     public const byte NETID_SYNC_INPUT = 1;
  4. }
  5.  

Just makes it easier to maintain / read.

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: Sync player states and animations
« Reply #8 on: May 27, 2016, 09:56:42 PM »
Premature optimizations are the root of all evil, cmifwdll :)

Keep it simple unless you really need some extra boost to performance. Packing 4 byte floats into 2 bytes to save a bit of bandwidth is quite silly. It's better to come up with an optimization that sends packets less frequently. For example instead of simply sending packets every X milliseconds, check to see if the value has actually changed significantly since the last time you sent it. If not, don't send anything. Most often when you have input axes and players use a keyboard, they generally just hold at least one key (W) to move the character forward for several seconds at a time or even longer. Imagine how much bandwidth you can save by not sending all those extra packets...

Also if you really want to be clever, input axes are always -1 to 1 range, so it would make sense to convert -1 to 1 float values into a 0-255 range byte instead. That's 1 byte instead of 4 per axis value, and with a precision of 0.0078.