Character jitters when using KinematicCharacterController but not with PhysicsBody

Hi,

I’m having a wierd issue with my character controller, if I use the character controller, my character jitters alot when FPS is higher than 60 even with a basic movement script (not using the CharacterAspect), but if I replace it by a PhysicsBody or RigidBody, it no longer jitters. This brings me to think that something is not set up properly for my character controller, but I can’t figure out what.

Here is my character authoring :

using System;
using Unity.CharacterController;
using Unity.Entities;
using Unity.Mathematics;
using UnityEngine;
using static CharacterAuthoring;

[Serializable]
public struct CharacterComponent : IComponentData
{
    public float RotationSharpness;
    public float GroundMaxSpeed;
    public float GroundedMovementSharpness;
    public float AirAcceleration;
    public float AirMaxSpeed;
    public float AirDrag;
    public float JumpSpeed;
    public float3 Gravity;
    public bool PreventAirAccelerationAgainstUngroundedHits;
    public BasicStepAndSlopeHandlingParameters StepAndSlopeHandling;
    public int MaxJumps;
    public int CurrentJumps;
    public float MaxCoyoteTime;
    public float CoyoteTime;
    public float AirJumpMultiplier;
}

public struct ExternalForce : IBufferElementData
{
    public float3 Force;
}

[DisallowMultipleComponent]
public class CharacterAuthoring : MonoBehaviour
{
    public AuthoringKinematicCharacterProperties CharacterProperties = AuthoringKinematicCharacterProperties.GetDefault();

    public float RotationSharpness = 25f;
    public float GroundMaxSpeed = 10f;
    public float GroundedMovementSharpness = 15f;
    public float AirAcceleration = 50f;
    public float AirMaxSpeed = 10f;
    public float AirDrag = 0f;
    public float JumpSpeed = 10f;
    public float3 Gravity = math.up() * -30f;
    public bool PreventAirAccelerationAgainstUngroundedHits = true;
    public BasicStepAndSlopeHandlingParameters StepAndSlopeHandling = BasicStepAndSlopeHandlingParameters.GetDefault();
    public int MaxJumps = 2;
    public float MaxCoyoteTime = 0.125f;
    public float AirJumpMultiplier = 0.5f;

    public class Baker : Baker<CharacterAuthoring>
    {
        public override void Bake(CharacterAuthoring authoring)
        {
            authoring.CharacterProperties.InterpolatePosition = false;
            authoring.CharacterProperties.InterpolateRotation = false;
            authoring.CharacterProperties.SnapToGround = false;
            authoring.CharacterProperties.EnhancedGroundPrecision = true;
            KinematicCharacterUtilities.BakeCharacter(this, authoring, authoring.CharacterProperties);

            Entity entity = GetEntity(TransformUsageFlags.Dynamic | TransformUsageFlags.WorldSpace);

            AddComponent(entity, new CharacterComponent
            {
                RotationSharpness = authoring.RotationSharpness,
                GroundMaxSpeed = authoring.GroundMaxSpeed,
                GroundedMovementSharpness = authoring.GroundedMovementSharpness,
                AirAcceleration = authoring.AirAcceleration,
                AirMaxSpeed = authoring.AirMaxSpeed,
                AirDrag = authoring.AirDrag,
                JumpSpeed = authoring.JumpSpeed,
                Gravity = authoring.Gravity,
                PreventAirAccelerationAgainstUngroundedHits = authoring.PreventAirAccelerationAgainstUngroundedHits,
                StepAndSlopeHandling = authoring.StepAndSlopeHandling,
                MaxJumps = authoring.MaxJumps,
                MaxCoyoteTime = authoring.MaxCoyoteTime,
                AirJumpMultiplier = authoring.AirJumpMultiplier
            });

            AddBuffer<ExternalForce>(entity);
        }
    }
}

Here is the movement script :

using Unity.Collections;
using Unity.Burst;
using Unity.Entities;
using Unity.NetCode;
using Unity.Transforms;
using Unity.Mathematics;

[UpdateInGroup(typeof(PredictedSimulationSystemGroup))]
public partial struct CharacterMovementSystem : ISystem
{
    [BurstCompile]
    public void OnCreate(ref SystemState state)
    {
        var builder = new EntityQueryBuilder(Allocator.Temp);
        builder.WithAll<PlayerInput>();
        state.RequireForUpdate(state.GetEntityQuery(builder));
    }

    [BurstCompile]
    public void OnUpdate(ref SystemState state)
    {
        var job = new PlayerMovementJob
        {
            dt = SystemAPI.Time.DeltaTime
        };
        state.Dependency = job.ScheduleParallel(state.Dependency);
    }
}

[BurstCompile]
public partial struct PlayerMovementJob : IJobEntity
{
    public float dt;

    public void Execute(in PlayerInput input, ref LocalTransform transform)
    {
        float3 movement = new float3(input.MoveInput.x, 0, input.MoveInput.y) * 10f * dt;
        transform.Position = transform.Translate(movement).Position;
    }
}

and this is how my character is set up :

In your CharacterAuthoring’s inspector, under the “General Properties” section, you can enable “Interpolate Position”. Depending on the nature of your jitter, this could solve it.

Don’t enable “Interpolate Rotation” though. This would only be necessary if you change the character’s default rotation handling code and move it to a fixed update instead

Jitter often happens when 2 things are moving similarly but not at the same rate, and often one of these 2 things is the camera. If your camera is moving on regular Update, and it smoothly follows a character moving on FixedUpdate, then the difference in update rates is what causes jitter. Enabling “Interpolate Position” will ensure the character’s visuals will at least update on regular update even if the actual simulation remains in fixed update

2 Likes

Thank you for the response!

I found the issue, it was my cinemachine, I was not updating it in the late update group and I was using LocalTransform instead of LocalToWorld.

I didn’t think of looking there sooner since it didn’t look like the camera itself was jittering only the entity.