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.
  2. Dismiss Notice

Question Networking the Platformer sample - Player Character can't stand on moving platforms correctly

Discussion in 'NetCode for ECS' started by EmmaPrats, Jul 26, 2023.

  1. EmmaPrats

    EmmaPrats

    Joined:
    Apr 19, 2019
    Posts:
    8
    In editor, the character jitters. In build, the character "glides" against the platform's movement instead of keeping its position still. The difference is probably due to much better performance in build than in editor. But the expected behaviour is that the character stays still relative to the platform. When I inspect it, it seems to work well in the Server, but the Client is "jittering" and setting-unsetting the character's parent.

    I grabbed the Platformer sample, simplified it (removed a bunch of states, I only need GroundMove, AirMove, Dashing, LedgeGrab and LedgeStanding), and networked it following the instructions in the manual.

    This is the moving platform:
    Moving Platform.png
    The TestMovingPlatformSystem updates in BeforePhysicsSystemGroup in both Client and Server.

    This is the Player Character:
    Captura de Pantalla 2023-07-26 a les 8.25.52.png
    Player Character 2.png

    And here are the ghost variants:
    Code (CSharp):
    1.     [GhostComponentVariation(typeof(CharacterControlComponent))]
    2.     [GhostComponent]
    3.     public struct CharacterControlComponentGhostVariant
    4.     {
    5.         [GhostField] public float3 MoveVector;
    6.     }
    Code (CSharp):
    1.  
    2. [GhostComponentVariation(typeof(CharacterInterpolation))]
    3. [GhostComponent(PrefabType = GhostPrefabType.PredictedClient)]
    4. public struct CharacterInterpolationGhostVariant
    5. {
    6.    
    7. }
    8.  
    Code (CSharp):
    1.  
    2. [GhostComponentVariation(typeof(CustomGravityComponent), "Custom Gravity")]
    3. [GhostComponent]
    4. public struct CustomGravityGhostVariant
    5. {
    6.     [GhostField] public float3 Direction;
    7.     [GhostField] public float Strength;
    8.  
    9.     [GhostField] public bool AffectedByGravityZone;
    10.     [GhostField] public Entity CurrentZone;
    11.     [GhostField] public Entity PreviousZone;
    12. }
    13.  
    Code (CSharp):
    1.  
    2. [GhostComponentVariation(typeof(KinematicCharacterBody), "Velocity, Grounded, Parenting")]
    3. [GhostComponent]
    4. public struct KinematicCharacterBodyGhostVariant
    5. {
    6.     [GhostField] public float3 RelativeVelocity;
    7.     [GhostField] public bool IsGrounded;
    8.     [GhostField] public Entity ParentEntity;
    9.     [GhostField] public float3 ParentLocalAnchorPoint;
    10.     [GhostField] public float3 ParentVelocity;
    11. }
    12.  
    Code (CSharp):
    1.  
    2. [GhostComponentVariation(typeof(PlayerCharacterComponent))]
    3. [GhostComponent]
    4. public struct PlayerCharacterComponentGhostVariant
    5. {
    6.     [GhostField] public bool IsSprinting;
    7.     [GhostField] public byte CurrentUngroundedJumps;
    8. }
    9.  
    Code (CSharp):
    1.  
    2. [GhostComponentVariation(typeof(PlayerCharacterStateMachine))]
    3. [GhostComponent]
    4. public struct PlayerCharacterStateMachineGhostVariant
    5. {
    6.     [GhostField] public PlayerCharacterStates CurrentState;
    7. }
    8.  
    (I use PlayerCharacterComponentGhostVariant and PlayerCharacterStateMachineGhostVariant for animating non-local players)

    Code (CSharp):
    1.  
    2. [GhostComponentVariation(typeof(TrackedTransform), "PreviousFixedRateTransform")]
    3. [GhostComponent]
    4. public struct TrackedTransformGhostVariant
    5. {
    6.     [GhostField] public RigidTransform PreviousFixedRateTransform;
    7. }
    8.  
    Code (CSharp):
    1.  
    2. public partial class DefaultVariantSystem : DefaultVariantSystemBase
    3. {
    4.     protected override void RegisterDefaultVariants(Dictionary<ComponentType, Rule> defaultVariants)
    5.     {
    6.         defaultVariants.Add(typeof(CustomGravityComponent), Rule.ForAll(typeof(CustomGravityGhostVariant)));
    7.         defaultVariants.Add(typeof(KinematicCharacterBody), Rule.ForAll(typeof(KinematicCharacterBodyGhostVariant)));
    8.         defaultVariants.Add(typeof(LocalTransform), Rule.ForAll(typeof(TransformDefaultVariant)));
    9.     }
    10. }
    11.  
    And these are the character controller systems and when they update:

    Code (CSharp):
    1.  
    2. //[UpdateInGroup(typeof(KinematicCharacterVariableUpdateGroup))]
    3. [UpdateInGroup(typeof(PredictedSimulationSystemGroup))]
    4. [UpdateAfter(typeof(PredictedFixedStepSimulationSystemGroup))]
    5. [BurstCompile]
    6. public partial struct PlayerCharacterVariableUpdateSystem : ISystem
    7.  
    Code (CSharp):
    1.  
    2. //[UpdateInGroup(typeof(SimulationSystemGroup), OrderFirst = true)]
    3. //[UpdateBefore(typeof(FixedStepSimulationSystemGroup))]
    4. [UpdateInGroup(typeof(PredictedSimulationSystemGroup))]
    5. [UpdateBefore(typeof(PlayerCharacterVariableUpdateSystem))]
    6. [BurstCompile]
    7. public partial struct PlayerCharacterVariableStepControlSystem : ISystem
    8.  
    Code (CSharp):
    1.  
    2. //[UpdateInGroup(typeof(FixedStepSimulationSystemGroup), OrderFirst = true)]
    3. [UpdateInGroup(typeof(PredictedFixedStepSimulationSystemGroup), OrderFirst = true)]
    4. [BurstCompile]
    5. public partial struct PlayerCharacterFixedStepControlSystem : ISystem
    6.  
    Code (CSharp):
    1.  
    2. [UpdateInGroup(typeof(KinematicCharacterPhysicsUpdateGroup))]
    3. [BurstCompile]
    4. public partial struct PlayerCharacterPhysicsUpdateSystem : ISystem
    5.  
    Code (CSharp):
    1.  
    2. [UpdateInGroup(typeof(SimulationSystemGroup))] // update in variable update because the camera can use gravity to adjust its up direction
    3. [UpdateBefore(typeof(KinematicCharacterVariableUpdateGroup))]
    4. public partial class CustomGravitySystem : SystemBase
    5.  
    In the case of CustomGravitySystem, which updates in KinematicCharacterVariableUpdateGroup, this is not in a "predicted" loop. Is that a problem? I'm only testing with global gravity (constant toward down vector), I haven't tried with spherical gravity yet.

    So... Is there anything else I might have missed?

    Are there any plans of adding moving platofms to the FPS sample so that we can have an example on how to network them?
     
  2. philsa-unity

    philsa-unity

    Unity Technologies

    Joined:
    Aug 23, 2022
    Posts:
    113
    Hi,

    There is actually an error in the documentation at the moment, which will be fixed in the next release. For the TrackedTransform component, it is the CurrentFixedRateTransform that must be a ghost field, and not the PreviousFixedRateTransform.

    CharacterControlComponentGhostVariant doesn't need to be a ghost component or to have anything synced, because its values will always be recomputed from scratch by prediction systems.

    At first glance, the rest seems good, but....

    Even though I see you've simplified the Platformer Sample character, I think it might be a better idea to just start from the Standard Characters (first/third person) if you want a networked character. The thing is; since the Platformer character wasn't built from the ground-up for netcode, there are pretty high chances that it does a bunch of things that aren't a great fit for netcode. The custom gravity system for example is at risk of having issues with netcode (I think some of it relies on LocalToWorld positions if I remember correctly, which can cause trouble in prediction loops), and perhaps the ledge grabbing and platformer camera system have similar problems as well, etc... And since character movement is relative to the camera, then the camera rotation must be part of prediction too

    ---

    I'm currently preparing a complete step-by-step tutorial on how to network both the First and Third person characters and how to create a minimal netcode game project with those. It's a pretty complex task and the slightest little mistake might result in jitter, so I'm hoping this will help make the character netcodification process clearer for users in the future. This also comes with an update to the Standard Characters that should make the netcode transition easier than it is right now (especially when it comes to input handling, and networking the third person camera). This should come out in the near future
     
    Last edited: Jul 27, 2023
    HiddenTess, EmmaPrats and NikiWalker like this.
  3. QuadrantGames

    QuadrantGames

    Joined:
    Jan 10, 2016
    Posts:
    13
    Great to hear phil. I am in the process of setting up the third person character and orbit camera to work with netcode, but I believe the camera has a slight jitter whereas the character looks smooth (it looks smooth in the editor window).
    I'll be eagerly waiting for the updated examples/guide if I can't figure it out.
    -On a side note, the guide says you only need to serialize/ghost-field the camera rotation, and that doesn't work unless I am doing something wrong. Serializing the transform seems to work, except for the jitter ofc.
     
  4. EmmaPrats

    EmmaPrats

    Joined:
    Apr 19, 2019
    Posts:
    8
    Thank you so much!

    This fixed it!*

    Also:
    • I deleted the CharacterControlComponentGhostVariant, you're right, I don't need it.
    • I can live without ledge grabbing mechanics, so I deleted those states too.
    • Regarding the custom gravity: I'm okay with only vertical gravity. I did modify the gravity/jump mechanics too to have the character "move downwards slower than it moves upwards" to get that "platformer feel", and it seems to work fine. Also, I forgot to mention that I moved the CustomGravitySystem to PredictedSimulationSystemGroup.
    • I also forgot to mention that I changed the camera as well, I'm using a top-down camera that follows the player. For now, it doesn't rotate. I don't want the player to "control" the camera. The camera is not a ghost, nothing is synched, each client moves their own camera. It seems to work well.

    *As long as the platform doesn't move too fast, but I can live with it until you release that sample :)