Author Topic: 3 questions about TNSyncRigidBody  (Read 3313 times)

sunriser

  • Newbie
  • *
  • Thank You
  • -Given: 0
  • -Receive: 0
  • Posts: 17
    • View Profile
3 questions about TNSyncRigidBody
« on: March 11, 2014, 04:47:00 PM »
I have a game I'm working on that involves the rolling of actual dice objects on a scene with two players in a game channel observing the die rolls via TNETCreateEx and TNSyncRigidBody for syncing. I am using the technique explained by ArenMook of separating RigidBody from Mesh Renderer and using the SpringTransform for interpolation.

I require accurate sync of physics since the die roll must end up being the same for both players (both must have die showing same number facing up). There is also an occasion where the dice roll is done with high AddForce to simulate it being shot like a bullet. That, also, I am trying to sync accurately.

ArenMook had posted this concerning the SyncRigidbody:

Quote
SyncRigidbody works only on the host, or the object's owner

My first question is are you sure it always works on object's owner? At one time I had each player (whether he was the host or not) initiate the Create of the die prefab AND THE APPLIED FORCE/ROTATION/TORQUE like so (the other player would just do the create part but not any applied force):

  1. [RCC(12)]
  2.     static GameObject OnRollDie(GameObject prefab, Vector3 pos, Quaternion rot, Vector3 RandomRotation, Vector3 RandomTorque, string DieType, string DieColor, bool UseBanner, string GameDieTypeName)
  3.     {
  4.         GameObject _DiePrefab = Instantiate(Resources.Load("Prefabs/"+DieType+"_"+DieColor), pos, rot) as GameObject;
  5.         _DiePrefab.name = GameDieTypeName;        
  6.         if(_DiePrefab.GetComponent<TNObject>().isMine)
  7.         {          
  8.             GameManagerMethods.DieRollFromActiveHost(_DiePrefab, RandomRotation, RandomTorque, DieType, GameDieTypeName);
  9.         }              
  10.         return _DiePrefab;
  11. }

As you can see, only the Object owner should be doing the applied force. I was only able to get this to work with one player only (the other player would instantiate the object but no force/rotation/torque would be applied. It would just plop down where it was created). After some investigation, I discovered it was ALWAYS THE HOST that would do the create and force apply correctly and not the non-host. Therefore I had to change my scripts to always have the host do the create with applied force, altering the above script from
Quote
_DiePrefab.GetComponent<TNObject>().isMine
to:

  1. if (TNManager.isHosting)
  2.         {            
  3.             _DiePrefab.GetComponent<TNSyncRigidbody>().Sync();
  4.             GameManagerMethods.DieRollFromActiveHost(_DiePrefab, RandomRotation, RandomTorque, DieType, GameDieTypeName);
  5.         }      

Although this technically works, the die response on the non-host is sluggish especially on the bullet-like force applied to certain die rolls.
Am I missing something here?

The second question is about how the physics engine work in Unity3d. If I were to have a player send the same force,rotation,and torque settings to itself and the other player, and each player has an exact replica of the playing field, shouldn't the die roll behave EXACTLY THE SAME without the need for syncing the rigidbodies? I tried this at first and would end up with different die rolls about 20% of the time.

The third question is about how to better sync the die object as explained above. Instead of just having the host issue the AddForce/rotate/torque command and have the other player die go along for the ride, should BOTH players initiate the same addforce/rotate/torque and have the non-hosted object get sync updates? Will this cause the die to "battle" with the sync updates if there is too much difference between them?

Thank you in advance!

EDIT: I am using TNET v1.8.4 BTW

RFCsDriveMeNuts

  • Newbie
  • *
  • Thank You
  • -Given: 3
  • -Receive: 0
  • Posts: 12
    • View Profile
Re: 3 questions about TNSyncRigidBody
« Reply #1 on: March 11, 2014, 07:12:54 PM »
Call me crazy, but this seems like crazy overkill for rolling dice.  To me, this is the equivalent to: I want to start a fire, instead of getting a match, I am going to build a nuclear weapon, detonate it, and go find a flaming sheet of paper from a destroyed office building. :-P

In gaming, perception > reality.  Games lie to you all the time, but you never would realize it.

Simply use an RFC to send the rolled dice value from the host/authority, then simulate the dice roll (without physics) to match the result on each client.  This is more secure, saves you a ton of physics madness, and is far more straight forward.

How you achieve the simulation?  There are many ways.  If you're more of an editory person (I generally avoid Unity's editors), you could use the animation tool to create a dice roll, and then manipulate the dice cube orientation before you run the animation ensure you get the desired output, since it will be rolling the same way each time.

An intermediate solution: technically, if you drop the dice from the same orientation, velocity, angular velocity, position, etc. you should get the same outcome.  You could combine that with my previous suggestion of sending the outcome first and manipulating the throw variables to ensure you get the same result, or, simply send those variables in an RFC call and hopefully they should all end up the same on each client.

Long story short, in networking, you try to remove all randomness (make everything deterministic), so that you can simply send the start state, and all clients end up with the same result.  This is how real-time strategies work.  You cannot send 10,000 units worth of data each frame.  So, you simply send the few things that do change, each player's orders, and each client runs the exact same simulation.

I am not sure if Unity's physics (uses PhysX for 3D), is deterministic.  I would imagine it is.  But who knows, there is a ton of floats, which could introduce rounding errors.

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: 3 questions about TNSyncRigidBody
« Reply #2 on: March 11, 2014, 08:05:33 PM »
  1. GameObject _DiePrefab = Instantiate(Resources.Load("Prefabs/"+DieType+"_"+DieColor), pos, rot) as GameObject;
  2.         _DiePrefab.name = GameDieTypeName;        
  3.         if(_DiePrefab.GetComponent<TNObject>().isMine)
That code won't work. You are instantiating a local object there, so TNObject will not have a proper TNObject ID set, and 'isMine' will always be true, because it's a local object.

You need to use CreateEx passing all the parameters you need. This way the tno.isMine will actually be valid. Not that you should care in this case, since you should do everything inside the creation callback. Don't call anything only on the object's owner inside the creation call. If you want something to happen only on the owner, then do it inside your script attached to that object. In Start() check tno.isMine and do what you need.

And by the way, if you send the same exact position/rotation/velocity/force to apply on creation, everyone's physics simulation will be the same anyway.

sunriser

  • Newbie
  • *
  • Thank You
  • -Given: 0
  • -Receive: 0
  • Posts: 17
    • View Profile
Re: 3 questions about TNSyncRigidBody
« Reply #3 on: March 14, 2014, 07:22:14 PM »
Eureka!

After a LOT of experimentation, I got something that works fairly well.

In case anything is interested, these are the changes i did:

1. Took out the
  1. if(_DiePrefab.GetComponent<TNObject>().isMine)
and moved it to the an actual prefab script. Now owner of object is accurately represented
2. Instead of using any autosync or syncrigidbody, each player in the channel rolls die with the same force/etc. settings. But, this does NOT MAKE IT DETERMINISTIC (See below)
3. I have some code on my dies that tells me which number is facing up and when the die is close to stopping. During that time only, I have object owner do a sync using RFC:
  1. public void UpdateDieRotationPosition()
  2.     {        
  3.         tno.Send(52, TNet.Target.Others, gameObject.transform.localPosition,gameObject.transform.rotation);      
  4.     }
  5.     [TNet.RFC(52)]
  6.     void OnSendDieRollToOtherPlayer(Vector3 UpdatedPosition,Quaternion UpdatedRotation)
  7.     {
  8.         if (!gameObject.rigidbody.isKinematic)
  9.         {
  10.             gameObject.rigidbody.isKinematic = true;
  11.         }
  12.         if (gameObject.collider.enabled)
  13.         {
  14.             gameObject.collider.enabled = false;
  15.         }
  16.         gameObject.transform.localPosition = UpdatedPosition;
  17.         gameObject.transform.rotation = UpdatedRotation;        
  18.     }
4. I updated ArenMook's SpringTransform.cs to include an "IgnoreOnObjectOwner" so only non-owner does lerp updates:
  1. /// <summary>
  2.     /// Whether this script's effect will be ignored on the player that owns the object.
  3.     /// </summary>
  4.  
  5.  public bool ignoreOnObjectOwner = true;
and
  1. void LateUpdate ()
  2.         {
  3.                 if (!mStarted) return;
  4.                 if ((ignoreOnHost && TNManager.isHosting) || (ignoreOnObjectOwner && mParent.GetComponent<TNObject>().isMine))
  5.                 {

This all works together quite nicely. Thanks, ArenMook, for the nudge in the right direction! TNet really is an excellent system once you understand its intended use!

Other info I discovered:


Thanks to all for the advice and suggestions!