Bouncing with Sloped Movement - Correctly Calculating Direction of Downward Terrain

Good day,

I have encountered an issue with my script for movement down a slope using a character controller where the controller bounces as it moves downwards. Although I have found a few forums posts related to this issue and some alternative fixes such as increasing y velocity based on how steep the terrain angle is, I have been unable to find the fix I am looking for which is adjusting the velocity vector to match the normal of the terrain so that the player travels in the direction of the slope. If anyone has any insight on where I am going wrong, it would be much appreciated.

I am following this tutorial:

Here is my code:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/*

DESCRIPTION:

CLASSES:

DATABASES:

VARIABLES:

METHODS:

COROUTINES:

*/

namespace EasyControls
{

    public class MovementTemplate : MonoBehaviour
    {

        //
        [HideInInspector]
        public CharacterController controller;

        // Variable for movement speed
        private float speed = 5;

        // Variable for gravity constant
        private float gravityConstant = -9.81f;   

        //
        private float ySpeed;

        //
        private float defaultStepOffset;

        //
        private float slopeLimit;

        // Bool for grounded state
        private bool isGrounded;

        // Move forward ControlObject reference for linking value on ControlBuffer to movement speed
        public ControlObject moveForward;

        // Move backward ControlObject reference for linking value on ControlBuffer to movement speed
        public ControlObject moveBackward;

        // Move left ControlObject reference for linking value on ControlBuffer to movement speed
        public ControlObject moveLeft;

        // Move right ControlObject reference for linking value on ControlBuffer to movement speed
        public ControlObject moveRight;

        // Move jump ControlObject reference for linking value on ControlBuffer to movement speed
        public ControlObject moveJump;

        public void Start()
        {

            controller = this.GetComponent<CharacterController>();
            defaultStepOffset = controller.stepOffset;
            slopeLimit = controller.slopeLimit;

        }

        // System method
        public void Update()
        {

            PlayerMovement();

        }
        // Last Updated:

        public void PlayerMovement()
        {

            float _horizontalInput = (-ControlBuffer.assignedControls.controls[moveLeft.data.Id].Value + ControlBuffer.assignedControls.controls[moveRight.data.Id].Value);
            float _verticalInput = (ControlBuffer.assignedControls.controls[moveForward.data.Id].Value + -ControlBuffer.assignedControls.controls[moveBackward.data.Id].Value);
            Vector3 _movementDirection = new Vector3(_horizontalInput, 0, _verticalInput).normalized;
            float _magnitude = Mathf.Clamp01(_movementDirection.magnitude) * speed;
            ySpeed += gravityConstant * Time.deltaTime;
            if (controller.isGrounded)
            {

                controller.stepOffset = defaultStepOffset;
                ySpeed = -0.5f;

            }
            else
            {
                controller.stepOffset = 0;
            }
            Vector3 _moveVelocity = _movementDirection * _magnitude;
            _moveVelocity = AdjustVelocityToSlope(_moveVelocity);
            _moveVelocity.y += ySpeed;
            controller.Move(transform.TransformDirection(_moveVelocity * Time.deltaTime));

        }

        public void JumpPlayer()
        {

            Debug.Log("Jump");

        }

        private Vector3 AdjustVelocityToSlope(Vector3 _velocity)
        {
            var ray = new Ray(transform.position, Vector3.down);
            if (Physics.Raycast(ray, out RaycastHit hit, 1.5f))
            {
               
                var slopeRotation = Quaternion.FromToRotation(Vector3.up, hit.normal);
                var adjustedVelocity = slopeRotation * _velocity;
                var slope = Vector3.Cross(hit.normal, Vector3.Cross(hit.normal, Vector3.up));
                if (adjustedVelocity.y < 0)
                {
                    return adjustedVelocity;
                }
            }
            return _velocity;
        }

    }
    // Last Updated: DD/MM/YYYY - Author Name

}
// Last Updated: DD/MM/YYYY - Author Name

Quick Update - I found out that the issue is related to the line:

controller.Move(transform.TransformDirection(_moveVelocity * Time.deltaTime));

The bouncing issue does not occur when the code is set to:

controller.Move(velocity * Time.deltaTime);

However this breaks the system for relative movement

Issue resolved - I simply calculated the relative vectors by multiplying the horizontal and vertical input values by the players transform.right and transform.forward respectively and then added them together for the players direction.

If you would prefer something more full-featured here is a super-basic starter prototype FPS based on Character Controller (BasicFPCC):

That one has run, walk, jump, slide, crouch… it’s crazy-nutty!!