Search code examples
unity-game-enginephysics

Unity CharacterController move player with platform


I’m using asset library for character’s movement. It uses Character Controller component for the player movement. Now 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 The first one is 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. The second approach is 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 function:

private 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);
        }

Solution

  • You can try physics materials for platforms or just deactivate character controller while on platform. Another way is to try to change transform position and rotation manually from c#, in the same way as hierarchy does.