CharacterController movement with platform

Hello everyone.
I’m using asset library for character’s movement. It uses Character Controller component for the player movement. I want to add to my game some moving platforms, so that player will be able to jump to that platforms and move. I tried two approaches:

  1. To make player’s game object child of moving platform, but it doesn’t work, I guess it’s because Character Controller’s position is changing in Update method.
  2. To add platform’s velocity to the Character Controller’s calculated velocity, but now it moves faster than platform.

I know that there are some conditions related to the calculation logic, but my knowledge of mathematics is not enough to fix this.

Please help me to understand how to properly calculate and mix that two velocities, so the player will move along with the platform

This is the code, that reads user input and calculates movement velocity:

public void Move(Vector2 moveInput, float targetSpeed, Quaternion cameraRotation, bool rotateCharacter = true)
{
    // note: Vector2's == operator uses approximation so is not floating point error prone, and is cheaper than magnitude
    // if there is no input, set the target speed to 0
    if (moveInput == Vector2.zero) targetSpeed = 0.0f;

    // a reference to the players current horizontal velocity
    float currentHorizontalSpeed = new Vector3(_controller.velocity.x, 0.0f, _controller.velocity.z).magnitude;

    float speedOffset = 0.1f;
    float inputMagnitude = moveInput.magnitude; // _input.analogMovement ? _input.move.magnitude : 1f;

    if (inputMagnitude > 1)
        inputMagnitude = 1f;

    // accelerate or decelerate to target speed
    if (currentHorizontalSpeed < targetSpeed - speedOffset ||
        currentHorizontalSpeed > targetSpeed + speedOffset)
    {
        // creates curved result rather than a linear one giving a more organic speed change
        // note T in Lerp is clamped, so we don't need to clamp our speed
        _speed = Mathf.Lerp(currentHorizontalSpeed, targetSpeed * inputMagnitude,
            Time.deltaTime * SpeedChangeRate);

        // round speed to 3 decimal places
        _speed = Mathf.Round(_speed * 1000f) / 1000f;
    }
    else
    {
        _speed = targetSpeed * inputMagnitude;
    }

    _animationBlend = Mathf.Lerp(_animationBlend, targetSpeed * inputMagnitude,
        Time.deltaTime * SpeedChangeRate);

    // normalise input direction
    Vector3 inputDirection = new Vector3(moveInput.x, 0.0f, moveInput.y).normalized;

    // note: Vector2's != operator uses approximation so is not floating point error prone, and is cheaper than magnitude
    // if there is a move input rotate player when the player is moving
    if (moveInput != Vector2.zero)
    {
        _targetRotation = Mathf.Atan2(inputDirection.x, inputDirection.z) * Mathf.Rad2Deg +
                          (_useCameraOrientation ? cameraRotation.eulerAngles.y : 0);
        float rotation = Mathf.SmoothDampAngle(transform.eulerAngles.y, _targetRotation, ref _rotationVelocity,
            RotationSmoothTime);

        // rotate to face input direction relative to camera position
        if (rotateCharacter)
            transform.rotation = Quaternion.Euler(0.0f, rotation, 0.0f);
    }

    // update animator if using character
    if (_hasAnimator)
    {
        _animator.SetFloat(_animIDSpeed, _animationBlend);
        _animator.SetFloat(_animIDMotionSpeed, inputMagnitude);
    }

    Vector3 targetDirection = Quaternion.Euler(0.0f, _targetRotation, 0.0f) * Vector3.forward;
    _velocity = targetDirection.normalized * _speed + new Vector3(0.0f, _velocity.y, 0.0f);
    _timeoutToResetVars = 0.5f;
}

This is Update method:

void Update()
{
    GravityControl();
    GroundedCheck();

    if (_timeoutToResetVars <= 0)
    {
        _speed = 0;
        _animationBlend = 0;
        _animator.SetFloat(_animIDSpeed, 0);
        _timeoutToResetVars = 0;
    }
    else
        _timeoutToResetVars -= Time.deltaTime;

    if (_useRootMotion)
        return;

    if (!_controller.enabled) return;

    _controller.Move(_velocity * Time.deltaTime);
}

I think your second approach, to add the platform’s velocity to the player, would work with just a bit more logic. This is how I would do it:

  1. When you get on/off a platform, update a value on the player itself to be the platform speed or 0.

  2. When you’re going to move the player, run a check to see if that value is 0 or not. If it is 0, just move normally. If it is not 0, the player moves at that value instead of their normal value.

  3. (2.5) However, I think you’d want them to move a bit while on the platform, right? So you could create a new variable for the player with a smaller speed variable that is just for platforms and then take the platformSpeed + playerSpeedOnPlatform, for example if they’re moving right. But then just playerSpeedOnPlatform by itself when moving left, since they’d be moving against the platform’s speed.

  4. When the player leaves the platform, their speed returns to normal.

  5. If your player jumps, think about how you want them it to work. Should their movement velocity in the air return to normal or stay with the platform until they land?