Getting the spike from the Simulation System Groups

I am working on the third-person battle royal game using ECS and Netcode for entities.

While checking the IOS build using profiler I found the spikes from the Simulation System Groups. I am getting these spikes constantly.

Can anyone please help me understand why I am getting this? Can I do anything to reduce or disable the physics calculations for the other player in the network?





More info…



Hmm, it seems like the job scheduling is the main culprit here. Can you post a screenshot of the job threads during these spikes? They should be available further down in the profiler window.

I managed to solve the in PhysicsUpdateGroup. While checking more into this issue I found that ThirdPersonCharacterPhysicsUpdateSystem has a Job that is running with ScheduleParallel.

But still, I getting the spikes but the reason is different. I am getting the spike for three different systems one after another.

  1. From PhysicsSystem
  2. WeaponFiringMechnisum
  3. ThirdPersonPlayerAnimationSystem
using Rukhanka;
using System.Runtime.CompilerServices;
using Unity.Burst;
using Unity.Entities;
using Unity.Mathematics;
using Unity.NetCode;

public struct InputStateData
{
    public float Speed;
    public bool Jump;
    public bool Grounded;
    public bool Crouch;
    public float InputX;
    public float InputY;
    public bool IsIdle;
    public bool FreeFall;
    public float MotionSpeed;
    public bool IsShooting;
    public bool Sprint;
    public bool IsDancing;
    public float DanceIndex;
    public bool IsAttecking;
    public bool IsVictim;
    public float FinisherIndex;
    public bool IsDead;
    public int CombinedStates;
}

#if RUKHANKA_WITH_NETCODE
[UpdateInGroup(typeof(RukhankaPredictedAnimationSystemGroup))]
#endif
[UpdateBefore(typeof(RukhankaAnimationSystemGroup))]
[RequireMatchingQueriesForUpdate]
[BurstCompile]
public partial struct ThirdPersonPlayerAnimationSystem : ISystem
{
    private FastAnimatorParameter Speed;
    private FastAnimatorParameter Jump;
    private FastAnimatorParameter Grounded;
    private FastAnimatorParameter Crouch;
    private FastAnimatorParameter InputX;
    private FastAnimatorParameter InputY;
    private FastAnimatorParameter IsIdle;
    private FastAnimatorParameter IsAiming;
    private FastAnimatorParameter MotionSpeed;
    private FastAnimatorParameter IsShooting;
    private FastAnimatorParameter Sprint;
    private FastAnimatorParameter IsDancing;
    private FastAnimatorParameter DanceIndex;
    private FastAnimatorParameter IsAttecking;
    private FastAnimatorParameter IsVictim;
    private FastAnimatorParameter FinisherIndex;
    private FastAnimatorParameter IsDead;
    private FastAnimatorParameter CombinedStates;

    private float m_SprintSpeedMultiplier;
    private float m_SmoothTime;

    [BurstCompile]
    public void OnCreate(ref SystemState state)
    {
        Speed = new FastAnimatorParameter("Speed");
        Jump = new FastAnimatorParameter("Jump");
        Grounded = new FastAnimatorParameter("Grounded");
        Crouch = new FastAnimatorParameter("Crouch");
        InputX = new FastAnimatorParameter("InputX");
        InputY = new FastAnimatorParameter("InputY");
        IsIdle = new FastAnimatorParameter("IsIdle");
        IsAiming = new FastAnimatorParameter("IsAiming");
        MotionSpeed = new FastAnimatorParameter("MotionSpeed");
        IsShooting = new FastAnimatorParameter("IsShooting");
        Sprint = new FastAnimatorParameter("Sprint");
        IsDancing = new FastAnimatorParameter("IsDancing");
        DanceIndex = new FastAnimatorParameter("DanceIndex");
        IsAttecking = new FastAnimatorParameter("IsAttecking");
        IsVictim = new FastAnimatorParameter("IsVictim");
        FinisherIndex = new FastAnimatorParameter("FinisherIndex");
        IsDead = new FastAnimatorParameter("IsDead");
        CombinedStates = new FastAnimatorParameter("CombinedStates");

        m_SprintSpeedMultiplier = 2f;
        m_SmoothTime = 0.2f;

        state.RequireForUpdate(SystemAPI.QueryBuilder().WithAll<ThirdPersonCharacterComponent, GhostOwnerIsLocal>().Build());
    }

    [BurstCompile]
    public void OnUpdate(ref SystemState state)
    {
        var deltaTime = SystemAPI.Time.DeltaTime;

        foreach (var (animatorParameterComponent, characterAspect, characterComponent, playerFinisherState, health, entity) in
            SystemAPI.Query<DynamicBuffer<AnimatorControllerParameterComponent>, ThirdPersonCharacterAspect, ThirdPersonCharacterComponent, RefRW<PlayerFinisherState>, Health>()
            .WithAll<ThirdPersonCharacterComponent, GhostOwnerIsLocal>()
            .WithEntityAccess())
        {
            /*if (playerFinisherState.ValueRO.IsPlayerVictim)
                PlayerInputHandler.Instance.crouch = false;*/

            if (characterAspect.CharacterControl.ValueRO.Sprint)
                m_SprintSpeedMultiplier = characterComponent.SprintSpeedMultiplier;
            else if (characterAspect.CharacterControl.ValueRO.Crouch)
                m_SprintSpeedMultiplier = characterComponent.CrouchSpeedMultiplier;
            else
                m_SprintSpeedMultiplier = characterComponent.SprintSpeedMultiplier;

            float speedValue;

            bool isIdle = characterAspect.CharacterControl.ValueRO.MoveVector.x == 0 && characterAspect.CharacterControl.ValueRO.MoveVector.y == 0;
            if (!characterAspect.CharacterControl.ValueRO.Sprint && !isIdle)
            {
                float rawSpeed = math.length(characterAspect.CharacterControl.ValueRO.MoveVector * m_SprintSpeedMultiplier);
                float maxSpeed = m_SprintSpeedMultiplier;
                speedValue = math.remap(0f, maxSpeed, 1.7f, 1f, rawSpeed);
            }
            else
            {
                speedValue = math.length(characterAspect.CharacterControl.ValueRO.MoveVector * m_SprintSpeedMultiplier);
            }

            UpdateAnimatorParameter(animatorParameterComponent, 0, speedValue);
            UpdateAnimatorParameter(animatorParameterComponent, 1, characterAspect.CharacterControl.ValueRO.Jump);
            UpdateAnimatorParameter(animatorParameterComponent, 2, characterAspect.CharacterControl.ValueRO.IsGrounded);
            UpdateAnimatorParameter(animatorParameterComponent, 3, characterAspect.CharacterControl.ValueRO.Crouch);
            UpdateAnimatorParameter(animatorParameterComponent, 4, math.lerp(characterAspect.CharacterControl.ValueRO.MoveAxisValue.x, characterAspect.CharacterControl.ValueRO.MoveAxisValue.x, m_SmoothTime));
            UpdateAnimatorParameter(animatorParameterComponent, 5, math.lerp(characterAspect.CharacterControl.ValueRO.MoveAxisValue.y, characterAspect.CharacterControl.ValueRO.MoveAxisValue.y, m_SmoothTime));
            UpdateAnimatorParameter(animatorParameterComponent, 6, speedValue == 0);
            UpdateAnimatorParameter(animatorParameterComponent, 7, characterAspect.CharacterControl.ValueRO.IsAiming);
            UpdateAnimatorParameter(animatorParameterComponent, 8, 1f);
            UpdateAnimatorParameter(animatorParameterComponent, 9, characterAspect.CharacterControl.ValueRO.IsShooting);
            UpdateAnimatorParameter(animatorParameterComponent, 10, characterAspect.CharacterControl.ValueRO.Sprint);
            UpdateAnimatorParameter(animatorParameterComponent, 11, characterAspect.CharacterControl.ValueRO.IsDancing);
            UpdateAnimatorParameter(animatorParameterComponent, 12, characterAspect.CharacterControl.ValueRO.DanceIndex);
            UpdateAnimatorParameter(animatorParameterComponent, 13, playerFinisherState.ValueRW.IsPlayerAttecker);
            UpdateAnimatorParameter(animatorParameterComponent, 14, playerFinisherState.ValueRW.IsPlayerVictim);
            UpdateAnimatorParameter(animatorParameterComponent, 15, playerFinisherState.ValueRW.AnimationIndex);

            if (!playerFinisherState.ValueRW.IsPlayerVictim)
            {
                UpdateAnimatorParameter(animatorParameterComponent, 16, health.IsDead());
            }

            UpdateAnimatorParameter(animatorParameterComponent, 17, characterAspect.CharacterControl.ValueRO.IsReloading ? 5 : 0);
        }
    }

    private void UpdateAnimatorParameter(DynamicBuffer<AnimatorControllerParameterComponent> buffer, int index, float value)
    {
        var param = buffer[index];
        param.FloatValue = value;
        buffer[index] = param;
    }

    private void UpdateAnimatorParameter(DynamicBuffer<AnimatorControllerParameterComponent> buffer, int index, bool value)
    {
        var param = buffer[index];
        param.BoolValue = value;
        buffer[index] = param;
    }

    private void UpdateAnimatorParameter(DynamicBuffer<AnimatorControllerParameterComponent> buffer, int index, int value)
    {
        var param = buffer[index];
        param.IntValue = value;
        buffer[index] = param;
    }
}

[WorldSystemFilter(WorldSystemFilterFlags.ClientSimulation | WorldSystemFilterFlags.ThinClientSimulation | WorldSystemFilterFlags.ServerSimulation)]
[UpdateInGroup(typeof(WeaponPredictionUpdateGroup), OrderFirst = true)]
[BurstCompile]
public partial struct WeaponFiringMecanismSystem : ISystem
{
    [BurstCompile]
    public void OnCreate(ref SystemState state)
    { 
        state.RequireForUpdate(SystemAPI.QueryBuilder().WithAll<StandardWeaponFiringMecanism, WeaponControl>().Build());
    }

    [BurstCompile]
    public void OnUpdate(ref SystemState state)
    {
        StandardWeaponFiringMecanismJob standardMecanismJob = new StandardWeaponFiringMecanismJob
        {
            DeltaTime = SystemAPI.Time.DeltaTime,
        };
        state.Dependency = standardMecanismJob.Schedule(state.Dependency);
    }

    [BurstCompile]
    [WithAll(typeof(Simulate))]
    public partial struct StandardWeaponFiringMecanismJob : IJobEntity
    {
        public float DeltaTime;

        void Execute(Entity entity, ref StandardWeaponFiringMecanism mecanism, ref WeaponControl weaponControl, ref WeaponShotVisuals weaponShotVisuals, in GhostOwner ghostOwner)
        {
            mecanism.ShotsToFire = 0;
            mecanism.ShotTimer += DeltaTime;

            // Detect starting to fire
            if (weaponControl.ShootPressed)
            {
                mecanism.IsFiring = true;
            }

            // Handle firing
            if (mecanism.FiringRate > 0f)
            {
                float delayBetweenShots = 1f / mecanism.FiringRate;
                
                // Clamp shot timer in order to shoot at most the maximum amount of shots that can be shot in one frame based on the firing rate.
                // This also prevents needlessly dirtying the timer ghostfield (saves bandwidth).
                mecanism.ShotTimer = math.clamp(mecanism.ShotTimer, 0f, math.max(delayBetweenShots + 0.01f ,DeltaTime)); 
                
                // This loop is done to allow firing rates that would trigger more than one shot per tick
                while (mecanism.IsFiring && mecanism.ShotTimer > delayBetweenShots)
                {
                    mecanism.ShotsToFire++;
                    
                    // Consume shoot time
                    mecanism.ShotTimer -= delayBetweenShots;

                    // Stop firing after initial shot for non-auto fire
                    if (!mecanism.Automatic)
                    {
                        mecanism.IsFiring = false;
                    }
                }
            }

            // Detect stopping fire
            if (!mecanism.Automatic || weaponControl.ShootReleased)
            {
                mecanism.IsFiring = false;
            }
            
            weaponShotVisuals.TotalShotsCount += mecanism.ShotsToFire;
        }
    }
}







Is this using the Character Controller package for ECS or is this your own character controller? If it’s the first, then it seems like a bug and you can report it using our bug reporter in the editor. Otherwise, you might be able to have more luck investigating if you use deep profiling and disabling burst (if it isn’t already disabled).

Yes, I am using this Character Controller package.

I understand that related to the PhysicsUpdateGroup might be the bug but WeaponFiringMechnisum
and ThirdPersonPlayerAnimationSystem is something that I have implemented.

Can you please look into it why I am getting the spike at a certain duration as seen in the images attached above?

Looking at the profiling screenshots, it seems the real cost of these spikes are in all kinds of other jobs that some systems are waiting to complete: Jobs.CreateMotions, NativeStream.ConstructJob, Broadphase.DynamicVsDynamicFindOverlappingPairsJob, etc…

It’s difficult to tell what the issue is from here, so it might be helpful to submit a bug report with a repro project for further investigation

+1, I’d also recommend profiling with jobs disabled (i.e. Untick Preferences -> Jobs -> Use Job Threads) to see “wall time” for all jobs, for easy “what specific system/job is performing the most work” comparisons (at the expense of losing the nuance that some jobs scale better across multiple cores).