I'm trying to have my character slide down slopes that are above the slope limit so he can't get stuck on the side out mountains, this is what I have right now; Code (CSharp): using System.Collections; using System.Collections.Generic; using UnityEngine; using System; [RequireComponent(typeof(CharacterController))] //[RequireComponent(typeof(Animator))] public class MovementController : MonoBehaviour { private float InputX, InputZ, Speed; private Camera cam; private CharacterController characterController; private Animator characterAnimator; private Vector3 desiredMoveDirection; [SerializeField] float rotationSpeed = 0.3f; [SerializeField] float allowRotation = 0.1f; [SerializeField] float movementSpeed = 3f; [SerializeField] float gravity; [SerializeField] float gravityMultiplier; [SerializeField] float slopeLimit = 45f; [Header("Results")] public float groundSlopeAngle = 0f; // Angle of the slope in degrees public Vector3 groundSlopeDir = Vector3.zero; // The calculated slope as a vector [Header("Settings")] public bool showDebug = false; // Show debug gizmos and lines public LayerMask castingMask; // Layer mask for casts. You'll want to ignore the player. public float startDistanceFromBottom = 0.2f; // Should probably be higher than skin width public float sphereCastRadius = 0.25f; public float sphereCastDistance = 0.75f; // How far spherecast moves down from origin point public float raycastLength = 0.75f; public Vector3 rayOriginOffset1 = new Vector3(-0.2f, 0f, 0.16f); public Vector3 rayOriginOffset2 = new Vector3(0.2f, 0f, -0.16f); // Start is called before the first frame update void Awake() { characterController = GetComponent<CharacterController>(); cam = Camera.main; characterAnimator = GetComponent<Animator>(); } // Update is called once per frame void Update() { InputX = Input.GetAxis("Horizontal"); InputZ = Input.GetAxis("Vertical"); InputDecider(); MovementManager(); } void FixedUpdate() { // Check ground, with an origin point defaulting to the bottom middle // of the char controller's collider. Plus a little higher if (characterController && characterController.isGrounded) { CheckGround(new Vector3(transform.position.x, transform.position.y - (characterController.height / 2) + startDistanceFromBottom, transform.position.z)); } } void InputDecider() { Speed = new Vector2(InputX, InputZ).sqrMagnitude; if (Speed > allowRotation) { RotationManager(); } else { desiredMoveDirection = Vector3.zero; } } void RotationManager() { var camforward = cam.transform.forward; var camright = cam.transform.right; camforward.y = 0; camright.y = 0; camforward.Normalize(); camright.Normalize(); desiredMoveDirection = camforward * InputZ + camright * InputX; transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(desiredMoveDirection), rotationSpeed); } void MovementManager() { gravity -= 9.8f * Time.deltaTime; gravity = gravity * gravityMultiplier; Vector3 moveDirection = desiredMoveDirection * (movementSpeed * Time.deltaTime); moveDirection = new Vector3 (moveDirection.x, gravity, moveDirection.z); characterController.Move(moveDirection); characterAnimator.SetFloat("Speed", (Mathf.Abs(Input.GetAxis("Horizontal")) + Mathf.Abs(Input.GetAxis("Vertical")))); if (characterController.isGrounded) { gravity = 0; } if (groundSlopeAngle > slopeLimit) { characterController.Move(groundSlopeDir + Vector3.down * gravity * Time.deltaTime); } } /// Checks for ground underneath, to determine some info about it, including the slope angle. public void CheckGround(Vector3 origin) { // Out hit point from our cast(s) RaycastHit hit; { Physics.SphereCast(origin, sphereCastRadius, Vector3.down, out hit, sphereCastDistance, castingMask); // Angle of our slope (between these two vectors). // A hit normal is at a 90 degree angle from the surface that is collided with (at the point of collision). // e.g. On a flat surface, both vectors are facing straight up, so the angle is 0. groundSlopeAngle = Vector3.Angle(hit.normal, Vector3.up); // Find the vector that represents our slope as well. // temp: basically, finds vector moving across hit surface Vector3 temp = Vector3.Cross(hit.normal, Vector3.down); // Now use this vector and the hit normal, to find the other vector moving up and down the hit surface groundSlopeDir = Vector3.Cross(temp, hit.normal); } // Now that's all fine and dandy, but on edges, corners, etc, we get angle values that we don't want. // To correct for this, let's do some raycasts. You could do more raycasts, and check for more // edge cases here. There are lots of situations that could pop up, so test and see what gives you trouble. RaycastHit slopeHit1; RaycastHit slopeHit2; // FIRST RAYCAST if (Physics.Raycast(origin + rayOriginOffset1, Vector3.down, out slopeHit1, raycastLength)) { // Debug line to first hit point if (showDebug) { Debug.DrawLine(origin + rayOriginOffset1, slopeHit1.point, Color.red); } // Get angle of slope on hit normal float angleOne = Vector3.Angle(slopeHit1.normal, Vector3.up); // 2ND RAYCAST if (Physics.Raycast(origin + rayOriginOffset2, Vector3.down, out slopeHit2, raycastLength)) { // Debug line to second hit point if (showDebug) { Debug.DrawLine(origin + rayOriginOffset2, slopeHit2.point, Color.red); } // Get angle of slope of these two hit points. float angleTwo = Vector3.Angle(slopeHit2.normal, Vector3.up); // 3 collision points: Take the MEDIAN by sorting array and grabbing middle. float[] tempArray = new float[] { groundSlopeAngle, angleOne, angleTwo }; Array.Sort(tempArray); groundSlopeAngle = tempArray[1]; } else { // 2 collision points (sphere and first raycast): AVERAGE the two float average = (groundSlopeAngle + angleOne) / 2; groundSlopeAngle = average; } } } void OnDrawGizmosSelected() { if (showDebug) { // Visualize SphereCast with two spheres and a line Vector3 startPoint = new Vector3(transform.position.x, transform.position.y - (characterController.height / 2) + startDistanceFromBottom, transform.position.z); Vector3 endPoint = new Vector3(transform.position.x, transform.position.y - (characterController.height / 2) + startDistanceFromBottom - sphereCastDistance, transform.position.z); Gizmos.color = Color.white; Gizmos.DrawWireSphere(startPoint, sphereCastRadius); Gizmos.color = Color.gray; Gizmos.DrawWireSphere(endPoint, sphereCastRadius); Gizmos.DrawLine(startPoint, endPoint); } } } There's a couple issues, namely if I hit a slope that's steeper than 45 degree angle, my slope limit, I move at that direction until I hit the ground so if I hit a 46 degree mountain side I move at that 46 degree angle through the air until the ground not going down the actual slope if that makes sense It's also pretty jumpy if I'm running into a slope on the ground at that angle, it just instantly kicks you back a couple of feet.. Thoughts? Help?
Solved with Code (CSharp): if (groundSlopeAngle > slopeLimit && characterController.isGrounded) { characterController.Move((groundSlopeDir + Vector3.down * gravity) * Time.deltaTime * 10); }