Search Unity

Question [NetCode] Cannot share the Rotation to other clients

Discussion in 'NetCode for ECS' started by raphaelbp12, May 28, 2020.

  1. raphaelbp12

    raphaelbp12

    Joined:
    Apr 22, 2019
    Posts:
    5
    After following the NetCube Video and Getting Started Guide, I can move my red cube with WASD. It works really fine. I even added a new cube, controlled by the server, which the players can collide and pull this cube around.

    The problem arises when I try to rotate the player cube. The owner client can see the cube rotating, but the others can't. And I couldn't figure out what I missed. I downloaded the other samples, but they are too different from NetCube.

    I cannot understand how the server can only exchange the Translation info, but can't exchange the Rotation, I already dug all the code, the only point that seems to be responsible to do this is the auto-generated code.

    You can find my code here: https://github.com/raphaelbp12/dots-netcode-sample

    upload_2020-5-28_18-31-44.png
     
  2. Kelevra

    Kelevra

    Joined:
    Dec 27, 2012
    Posts:
    87
    Hello @raphaelbp12
    Are you sure that your MouseRaycast code correctly works on the server?
    What I can see is that your server doesn't handle rotation correctly but you don't see that on the client because you are constantly overwriting object rotation.
     
  3. raphaelbp12

    raphaelbp12

    Joined:
    Apr 22, 2019
    Posts:
    5
    Thank you so much for your answer!

    I'm not sure about it, but it works on client. I tried to share just the input to the server, but I don't know if I did it on the proper way, I tried to follow the translation method.

    I could test something... I could stop to try to calculate the rotation from the mouse... but make it rotate a little amount through the time, what do you think? Maybe it could be a valid test.

    Is it enough to change the rotation as I'm doing? How would you change it?

    I get the mouse position and store it as a float3.

    Code (CSharp):
    1. [UpdateInGroup(typeof(ClientSimulationSystemGroup))]
    2. public class SampleCubeInput : ComponentSystem
    3. {
    4.     protected override void OnCreate()
    5.     {
    6.         RequireSingletonForUpdate<NetworkIdComponent>();
    7.         RequireSingletonForUpdate<EnabledotsnetcodesampleGhostReceiveSystemComponent>();
    8.     }
    9.  
    10.     protected override void OnUpdate()
    11.     {
    12.         var localInput = GetSingleton<CommandTargetComponent>().targetEntity;
    13.         if (localInput == Entity.Null)
    14.         {
    15.             var localPlayerId = GetSingleton<NetworkIdComponent>().Value;
    16.             Entities.WithNone<CubeInput>().ForEach((Entity ent, ref MovableCubeComponent cube) =>
    17.             {
    18.                 if (cube.PlayerId == localPlayerId)
    19.                 {
    20.                     PostUpdateCommands.AddBuffer<CubeInput>(ent);
    21.                     PostUpdateCommands.SetComponent(GetSingletonEntity<CommandTargetComponent>(), new CommandTargetComponent { targetEntity = ent });
    22.                 }
    23.             });
    24.             return;
    25.         }
    26.         var input = default(CubeInput);
    27.         input.tick = World.GetExistingSystem<ClientSimulationSystemGroup>().ServerTick;
    28.         input.mousePosition = Input.mousePosition;
    29.         if (Input.GetKey("a"))
    30.             input.horizontal -= 1;
    31.         if (Input.GetKey("d"))
    32.             input.horizontal += 1;
    33.         if (Input.GetKey("s"))
    34.             input.vertical -= 1;
    35.         if (Input.GetKey("w"))
    36.             input.vertical += 1;
    37.         var inputBuffer = EntityManager.GetBuffer<CubeInput>(localInput);
    38.         inputBuffer.AddCommandData(input);
    39.     }
    40. }
    Then, I use this mousePosition in the MoveCubeSystem, just like the demo.


    Code (CSharp):
    1.     Quaternion newRotation = new Quaternion();
    2.     protected override void OnUpdate()
    3.     {
    4.         var raycaster = new MouseRayCast() { pw = World.GetOrCreateSystem<BuildPhysicsWorld>().PhysicsWorld };
    5.  
    6.         var group = World.GetExistingSystem<GhostPredictionSystemGroup>();
    7.         var tick = group.PredictingTick;
    8.         var deltaTime = Time.DeltaTime;
    9.         Entities.ForEach((DynamicBuffer<CubeInput> inputBuffer, ref Translation trans, ref PredictedGhostComponent prediction, ref Rotation rot) =>
    10.         {
    11.             if (!GhostPredictionSystemGroup.ShouldPredict(tick, prediction))
    12.                 return;
    13.             CubeInput input;
    14.             inputBuffer.GetDataAtTick(tick, out input);
    15.  
    16.             Unity.Physics.RaycastHit? movDestination = raycaster.CheckRay(input.mousePosition, trans.Value, 1000f);
    17.             if (movDestination != null)
    18.             {
    19.                 Vector3 relativePos = movDestination.Value.Position - trans.Value;
    20.                 Vector3 eulerAngles = Quaternion.LookRotation(relativePos, Vector3.up).eulerAngles;
    21.  
    22.                 float3 oldRotation = rot.Value.value.xyz;
    23.                 newRotation = Quaternion.Euler(oldRotation.x, eulerAngles.y, oldRotation.z);
    24.             }
    25.             rot.Value = newRotation;
    26.  
    27.             if (input.horizontal > 0)
    28.                 trans.Value.x += deltaTime;
    29.             if (input.horizontal < 0)
    30.                 trans.Value.x -= deltaTime;
    31.             if (input.vertical > 0)
    32.                 trans.Value.z += deltaTime;
    33.             if (input.vertical < 0)
    34.                 trans.Value.z -= deltaTime;
    35.         });
    36.     }
     
  4. raphaelbp12

    raphaelbp12

    Joined:
    Apr 22, 2019
    Posts:
    5
    @Kelevra I made this test now, the cubes can be rotated just once, I cannot keep it rotating over time. The problem, as you said, seems to be the method that I'm using to rotate them. How would you do this?

    First, I create a variable to hold the angle value and initiate it as 45. After that, I try to change it over time getting the input from the server.

    If I do this thing, the cube change to a 45 degrees rotation on the Y axis, but I cannot change it over time.

    Code (CSharp):
    1. [UpdateInGroup(typeof(GhostPredictionSystemGroup))]
    2. public class MoveCubeSystem : ComponentSystem
    3. {
    4.  
    5.     Quaternion newRotation = new Quaternion();
    6.     float angle = 45;
    7.     protected override void OnUpdate()
    8.     {
    9.         var group = World.GetExistingSystem<GhostPredictionSystemGroup>();
    10.         var tick = group.PredictingTick;
    11.         var deltaTime = Time.DeltaTime;
    12.         Entities.ForEach((DynamicBuffer<CubeInput> inputBuffer, ref Translation trans, ref PredictedGhostComponent prediction, ref Rotation rot) =>
    13.         {
    14.             if (!GhostPredictionSystemGroup.ShouldPredict(tick, prediction))
    15.                 return;
    16.             CubeInput input;
    17.             inputBuffer.GetDataAtTick(tick, out input);
    18.  
    19.             if (input.rotation < 0)
    20.                 angle -= deltaTime;
    21.             if (input.rotation > 0)
    22.                 angle += deltaTime;
    23.  
    24.             rot.Value = Quaternion.Euler(0, angle, 0);
    25.         });
    26.     }
    27. }

    As you can see in the image, I change the rotation in the right client, but it was not registered by the server and transmitted to the left client.

    upload_2020-5-29_15-17-21.png
     
  5. SebLazyWizard

    SebLazyWizard

    Joined:
    Jun 15, 2018
    Posts:
    234
    Try to change the rotation directly without using the "angle" variable.
    The "angle" can and will run out of sync since the prediction system group can run multiple time during a frame on the client, but only once per frame on the server.
    So either make "angle" a ghosted component or get rid of it.
     
  6. raphaelbp12

    raphaelbp12

    Joined:
    Apr 22, 2019
    Posts:
    5
    Thank you for your answer! :D

    Perfect! So I would need to create an AuthoringComponent (or an actually Ghost Component?) to store the player angle, right? Where should I write this value, in which step of my game?
     
  7. SebLazyWizard

    SebLazyWizard

    Joined:
    Jun 15, 2018
    Posts:
    234
    As I told you you don't need it, just change the rotation directly;
    Code (CSharp):
    1. [UpdateInGroup(typeof(GhostPredictionSystemGroup))]
    2. public class MoveCubeSystem : ComponentSystem
    3. {
    4.     protected override void OnUpdate()
    5.     {
    6.         var group = World.GetExistingSystem<GhostPredictionSystemGroup>();
    7.         var tick = group.PredictingTick;
    8.         var deltaTime = Time.DeltaTime;
    9.         Entities.ForEach((DynamicBuffer<CubeInput> inputBuffer, ref Translation trans, ref PredictedGhostComponent prediction, ref Rotation rot) =>
    10.         {
    11.             if (!GhostPredictionSystemGroup.ShouldPredict(tick, prediction))
    12.                 return;
    13.             CubeInput input;
    14.             inputBuffer.GetDataAtTick(tick, out input);
    15.             rot.Value = math.mul(quaternion.AxisAngle(math.up(), input.rotation * deltaTime), rot.Value);
    16.         });
    17.     }
    18. }
    If you want to spawn the ghosts with an initial angle of 45, then do it in the "GoInGameServerSystem" (just set the rotation component accordingly)
     
  8. raphaelbp12

    raphaelbp12

    Joined:
    Apr 22, 2019
    Posts:
    5
    I did it, but I had the same output... It seems that something is reseting the object rotation before every tick, do you have any clue where could it be? Thank you, man!
     
  9. SebLazyWizard

    SebLazyWizard

    Joined:
    Jun 15, 2018
    Posts:
    234
    Check your other systems for any rotation changes and debug your "input.rotation", there's no reference of how and where you set it.
     
    raphaelbp12 likes this.
  10. bb8_1

    bb8_1

    Joined:
    Jan 20, 2019
    Posts:
    100
    This is what i do in my code and it works :
    Code (CSharp):
    1. rot.Value = math.mul(math.normalizesafe(rot.Value),
    2.                     math.normalizesafe(quaternion.AxisAngle(new float3(0,0,1),
    3.                     math.radians(deltaTime * 85))));
    4. rot.Value = math.normalizesafe(rot.Value);
    Dont know what is difference between normalize and normalizesafe but it seems normalize usually does not work. btw angles are calculated in radians in quaternion. AxisAngle - i think
    maybe i have put too much normalizesafe here though
     
    Last edited: May 31, 2020