Character Controller doesn't move with a moving parent

Using Unity 2019.1
There’s no rigidbody in none of the objects.
Character moves with CharacterController.Move method.

MovingPlatform:

Update() {  transform.Translate(transform.forward * speed * Time.deltaTime); }

CharacterController:

private void OnControllerColliderHit(ControllerColliderHit hit)
{
        if (charControl.isGrounded && hit.transform.tag == "Floor")
                transform.SetParent(hit.transform);
}

The character keeps sliding out the platform while standing fixed. Platform has the tag “Floor”. The object is being setted as child while looking at hierarchy.

Any solutions?

Figured out that if I change the platform from Update to FixedUpdate, changing the parent of CharacterController will works, and it will be carried with the platform.

FixedUpdate

 FixedUpdate() {transform.Translate (transform.forward * speed * Time.deltaTime); }

But I can not say if it’s a bug or a feature! So, it does not seem to be a ‘answer’, even if it works

You need to add the platform movement in the CharacterController.Move.

Vector3 externalMovement = externalMoveSpeed * Time.deltaTime;
controller.Move(movement + externalMovement);

The “externalMoveSpeed” is a public Vector3. When the character collides with the platform, the platform should change that value. It would look something like this:

using UnityEngine;

public class Platform: MonoBehaviour
{
    private CharacterMovement character;

    public Vector3 moveDirection;

    private void Update()
    {
        transform.Translate(moveDirection * Time.deltaTime);
    }

    private void OnCollisionEnter(Collision collision)
    {
        if (!character)
        {
            character = collision.gameObject.GetComponent<CharacterMovement>();
        }

        character.externalMoveSpeed = moveDirection;
    }

    private void OnCollisionExit(Collision collision)
    {
        character.externalMoveSpeed = Vector3.zero;
    }
}

But I suggest you put a BoxCollider on top of the platform, check “IsTrigger” and use OnTriggerEnter/OnTriggerExit instead (with that same logic) because when the character moves, the OnCollisionExit method might get called and it won’t work properly.

turns out there’s a much easier way to do this, instead of changing the transform in script using an animator and under the update mode change it to “animate physics”, setting the object’s parent under the same script of moving the character controller in a oncontrollercolliderhit method

void OnControllerColliderHit(ControllerColliderHit hit)
    {
        if (hit.collider.tag == "floor")
        {
             transform.parent = hit.transform;
        }
        else
        {
            transform.parent = null;
        }
    }

I just got a very simple solution for this problem that might help someone coming to this post. It also solves the problem related to moving the player acordingly when the platform/spaceship rotates, without needing to have it parented!

  1. Instead of parenting the object with the character controller to the platform or whatever, create an empty object and parent it instead. I will call it ghost.

  2. On your custom player controller script, create a reference to the ghost and set its position the same as the player itself on Start():

    private void Start()
    {
        ghost.transform.position = transform.position;
    }
    
  3. Create a Vector3 representing the amount of movement your character should be doing to be synchronized with the platform (IMPORTANT: Make sure to use LateUpdate to create all of the player movement logic now on).
    Sum up with the movement logic on Move(), reset the ghost position to player position so it is always up to date on where the player is exactly and then get all the movement up to the next frame.

    private void LateUpdate()
    {
        Vector3 translation = ghost.transform.position - transform.position;
    
        controller.Move(translation + movement);
    
        ghost.transform.position = transform.position;
    
        transform.rotation = ghost.transform.rotation;
    }
    
  4. Now its up to you how to customize that. You can also use OnControllerColliderHit() to change which object should the player have its movement synchronized to, like this:

    void OnControllerColliderHit(ControllerColliderHit hit)
    {
        if (hit.collider.tag == "Ground")
        {
            ghost.transform.parent = hit.transform;
            ghost.transform.position = transform.position;
        }
    }
    

Also a tip: Some unwanted behaviour might happen because of how the Character Controllers collisions works, turning the controller.detectCollisions to false will fix a lot of these problems. However, in order to get it perfect, a workaround is to remove all of the colliders from the object containing the rigidbody, create another independent object with only the colliders and simply copying the transform to the object with the rigidbody, so it will always be at the same pos/rot/scale. That is the most solid I could get. No jittering, no object going through eachother, fluid movement, etc.
Hope it helps!