Search Unity

How to structure in combination with new Network System?

Discussion in 'Input System' started by Jawsarn, Aug 15, 2019.

  1. Jawsarn

    Jawsarn

    Joined:
    Jan 12, 2017
    Posts:
    245
    Hey. I'm using the new Input System in combination with the network system. The current way I'm doing it is to have set up Input Actions that I use action.performed on. Since its decoupled simulation, I accumulate some input like rotation, and consume it when used (resetting only the rotation), while other inputs are simply updated to the most recent value, or if triggered stay triggered. Is there any better way to do this than haivng these two systems, one that runs in Update loop to collect input, and other on ticks?

    One example of problem is that when the client is catching up, there will be multiple sim ticks before any input gather, so all after first will have 0 rotational movement.

    Code (CSharp):
    1. public struct FrameInput
    2. {
    3.     public byte Jump;
    4.     public Vector2 playerMovementAxis;
    5.     public Vector2 playerRotationAxis;
    6.     public quaternion playerRotation;
    7.     public void Reset()
    8.     {
    9.         Jump = 0;
    10.         //playerMovementAxis = Vector2.zero;
    11.         playerRotationAxis = Vector2.zero;
    12.         playerRotation = quaternion.identity;
    13.     }
    14. }
    15. [NotClientServerSystem]
    16. [UpdateBefore(typeof(TickClientSimulationSystem))]
    17. [AlwaysUpdateSystem]
    18. public class InputCollectingSystem : BaseComponentSystem
    19. {
    20.     static InputCollectingSystem Instance;
    21.     InputSetup m_inputSetup;
    22.     FrameInput m_collectingFrameInput;
    23.     FrameInput m_collectedFrameInput;
    24.     NetworkTimeSystem m_networkTimeSystem;
    25.     bool used = false;
    26.     public static ref FrameInput GetCurrentInput()
    27.     {
    28.         // Considering this is used in FixedUpdates, index is + 1 as we start to fill up next frame after this Update(if this would be done async).
    29.         return ref Instance.GetInputForFrameInt();
    30.     }
    31.     ref FrameInput GetInputForFrameInt()
    32.     {
    33.         // Considering this is used in FixedUpdates, index is + 1 as we start to fill up next frame after this Update(if this would be done async).
    34.         used = true;
    35.         return ref m_collectedFrameInput;
    36.     }
    37.     public static void ResetInput()
    38.     {
    39.         Instance.ResetInputInt();
    40.     }
    41.     void ResetInputInt()
    42.     {
    43.         m_collectedFrameInput.playerRotationAxis = Vector2.zero;
    44.     }
    45.     protected override void OnCreate()
    46.     {
    47.         base.OnCreate();
    48.         m_networkTimeSystem = World.GetOrCreateSystem<NetworkTimeSystem>();
    49.         // Load the inputs
    50.         m_inputSetup = new InputSetup();
    51.         m_inputSetup.Enable();
    52.         // Set the update lambda magic
    53.         m_inputSetup.Player.Jump.performed += context => m_collectingFrameInput.Jump = 1;
    54.         m_inputSetup.Player.Movement.performed += context => m_collectingFrameInput.playerMovementAxis = context.ReadValue<Vector2>();
    55.         m_inputSetup.Player.Movement.canceled += context => m_collectingFrameInput.playerMovementAxis = Vector2.zero;
    56.         m_inputSetup.Player.Orientation.performed += context => m_collectingFrameInput.playerRotationAxis += context.ReadValue<Vector2>();
    57.         // Set as singleton so we can directly acces inputs
    58.         Instance = this;
    59.     }
    60.     protected override void OnDestroy()
    61.     {
    62.         base.OnDestroy();
    63.         m_inputSetup.Disable();
    64.     }
    65.     protected override void OnUpdate()
    66.     {
    67.         m_collectedFrameInput = m_collectingFrameInput;
    68.         if (used)
    69.         {
    70.             used = false;
    71.             m_collectingFrameInput.Reset();
    72.         }
    73.     }
    74. }
    75. [UpdateInGroup(typeof(ClientSimulationSystemGroup))]
    76. [UpdateAfter(typeof(GhostReceiveSystemGroup))]
    77. [UpdateBefore(typeof(PlayerCommandSendSystem))]
    78. public class InputAddingSystem : JobComponentSystem
    79. {
    80.     NetworkTimeSystem m_networkTimeSystem;
    81.     uint lastTick = 0;
    82.     protected override void OnCreate()
    83.     {
    84.         base.OnCreate();
    85.         m_networkTimeSystem = World.GetOrCreateSystem<NetworkTimeSystem>();
    86.     }
    87.     protected override void OnDestroy()
    88.     {
    89.         base.OnDestroy();
    90.     }
    91.     struct InputJob : IJobForEachWithEntity<CommandTargetComponent>
    92.     {
    93.         public BufferFromEntity<PlayerCommandData> inputFromEntity;
    94.         public uint targetTick;
    95.         public FrameInput inputFrame;
    96.         public void Execute(Entity entity, int index, [ReadOnly] ref CommandTargetComponent state)
    97.         {
    98.             if (state.targetEntity != Entity.Null)
    99.             {
    100.                 if (inputFromEntity.Exists(state.targetEntity))
    101.                 {
    102.                     // Get buffer array from entity and add new tick
    103.                     var input = inputFromEntity[state.targetEntity];
    104.                     input.AddCommandData(new PlayerCommandData
    105.                     {
    106.                         tick = targetTick,
    107.                         frameInput = inputFrame
    108.                     });
    109.                 }
    110.             }
    111.         }
    112.     }
    113.     protected override JobHandle OnUpdate(JobHandle inputDeps)
    114.     {
    115.         if (lastTick == m_networkTimeSystem.predictTargetTick)
    116.         {
    117.             Debug.Log("Same prediction tick");
    118.             return inputDeps;
    119.         }
    120.         lastTick = m_networkTimeSystem.predictTargetTick;
    121.         var inputJob = new InputJob()
    122.         {
    123.             inputFromEntity = GetBufferFromEntity<PlayerCommandData>(),
    124.             targetTick = lastTick,
    125.             inputFrame = InputCollectingSystem.GetCurrentInput()
    126.         };
    127.         InputCollectingSystem.ResetInput();
    128.         return inputJob.ScheduleSingle(this, inputDeps);
    129.     }
    130. }
     
  2. TheOtherMonarch

    TheOtherMonarch

    Joined:
    Jul 28, 2012
    Posts:
    866
    Wherever possible uncouple your code.