How to have other (any) Objects smoothly push a Character Controller?

Making a character controller apply forces to a rigidbody via OnCollisionEnter or OnTriggerEnter is not very difficult. There’s even a script for it in the docs and I’ve made my own.

However, how do I smoothly “push” a character controller with other objects, such as a static collider, or kinematic rigidbodies, or even non-kinematic rigidbodies (I’m trying not to use these but it doesn’t make a difference at this point. Ideally I am going to be using lots of kinematic rigidbodies)?

When I mean smoothly push, I mean not as in apply “a fake force” a character controller upon entering Collision or Trigger (in case of kinematic rigidbodies or static colliders), but sort of “carrying” the character controller along the perimeters of the other object? That is, gently nudging the character controller with it no matter the speeds of the other object.

The way I tried, using OnTriggerStay produces jerky results, most likely because OnTriggerStay occurs every FixedUpdate(). Although in my script I did use Update() to simulate a constant push, I also used FixedUpdate(). I’ve included my scripts below in full. CharacterMovement goes on the character (as well as a trigger). MovingPlatform goes on a simple cube or platform (this also needs a trigger).

I tried using Animated Physics on something more complex than a platform and it produced close results to what I wanted, with a non-kinematic rigidbody on the character, but I could sadly not control the amount of force animated physics has on the character. It throws the character too hard, when animations are fast, no matter what the settings are instead of sort of carrying it alongside it like when animations are slow .

public class CharacterMovement : MonoBehaviour
{
    public float speed = 10f;
    public CharacterController controller;
    public bool useController = false;

    public void Start()
    {
        if (controller == null) controller = GetComponent<CharacterController>();
    }

    public Vector3 pushVector;
    //public float pushSpeed;

    void OnTriggerStay(Collider collider) 
    {
        Debug.Log("Collided with " + collider.name);
        //For easy testing purposes
		if (collider.name != "MovingPlatform") return;

        //Already tried using using this in both Update() and FixedUpdate() as well as a calculated velocity from fixed update instead of update in the platform, as well as inputting different values for velocity instead of calculated ones and speeds, and turning off and on fake friction before adding the this fake force to the character controller
        pushVector = collider.GetComponent<MovingPlatform2>().transformVelocity * collider.GetComponent<MovingPlatform2>().oldDelta * collider.GetComponent<MovingPlatform2>().speed; //pushSpeed;

		//pushVector += collider.GetComponent<MovingPlatform2>().transformVelocity * 0.1f;
		//pushVector += collider.rigidbody.velocity;

    }

    public void OnCollisionEnter(Collision collision)
    {
        Debug.Log(" I have collided with " + collision.gameObject.name);
    }

    public void OnControllerColliderHit(ControllerColliderHit hit)
    {
        if (hit.gameObject.name == "Floor") return;
        Debug.Log("OnHit: "+ hit.gameObject.name);
    }

    // Update is called once per frame
    void Update()
    {

        float h = Input.GetAxis("Horizontal");
        float v = Input.GetAxis("Vertical");

        // Add to Update or FixedUpdate, just before using moveDirection: //Note: doesn't work well in either
        Vector3 moveDirection = Vector3.zero;
		moveDirection = pushVector; //infinite momentum, just for testing
        //moveDirection += pushVector; //Use with friction
        //pushVector *= 0.8f; //Friction

		
		
		//Doesn't matter just for testing
		//Move via transform
        if (controller == null || !useController)
        {
            moveDirection.x += h;
            moveDirection.z += v;

            transform.Translate(moveDirection * speed * Time.deltaTime);
        }
		//Move via Controller //Note: if not moving collisions and triggers not detected (only below because of the fake gravity), 
        else 
        {
            moveDirection.x += h;
            moveDirection.z += v;
            moveDirection.y += Physics.gravity.y;
            controller.Move(moveDirection * speed * Time.deltaTime);
        }

    }
}




public class MovingPlatform : MonoBehaviour
{
    public float speed = 12f;
    public float timeToTravel = 1f;
    public float delayTime = 0.7f;
	
	private Vector3 oldPosition;
    private Vector3 newPosition;
    public Vector3 transformVelocity = Vector3.zero;
    public float oldDelta;
	
	// Use this for initialization
    IEnumerator Start()
    {
        oldPosition = transform.position;
        fixedOldPosition = transform.position;

        while (true)
        {
            yield return new WaitForSeconds(delayTime);
            float t = 0f;

            while (t < timeToTravel)
            {
                transform.position += new Vector3(0f, 0f, speed * Time.deltaTime);
                t += Time.deltaTime;
                yield return null;
            }

            yield return new WaitForSeconds(delayTime);

            while (t > 0f)
            {
                transform.position -= new Vector3(0f, 0f, speed * Time.deltaTime);
                t -= Time.deltaTime;
                yield return null;
            }
        }
    }
	
	// Update is called once per frame
    void Update()
    {
        newPosition = transform.position;
        Vector3 media = (newPosition - oldPosition);

        transformVelocity = media / Time.deltaTime;
        oldDelta = Time.deltaTime;
        oldPosition = newPosition;
        newPosition = transform.position;
    }

    private Vector3 fixedOldPosition;
    private Vector3 fixedNewPosition;
    public Vector3 fixedTransformVelocity = Vector3.zero;
    public float fixedOldDelta;

    void FixedUpdate()
    {
        fixedNewPosition = transform.position;
        Vector3 media = (fixedNewPosition - fixedOldPosition);

        fixedTransformVelocity = media / Time.deltaTime;
        fixedOldDelta = Time.deltaTime;
        fixedOldPosition = fixedNewPosition;
        fixedNewPosition = transform.position;
    }
}

I found the solution! When you collide with the object that wants to push your controller, make the the player a child of that object