Search Unity

Falling Down Slopes [SOLVED]

Discussion in 'Scripting' started by AnimusRex, Sep 28, 2019.

  1. AnimusRex

    AnimusRex

    Joined:
    Apr 26, 2019
    Posts:
    67
    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):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using System;
    5.  
    6. [RequireComponent(typeof(CharacterController))]
    7. //[RequireComponent(typeof(Animator))]
    8.  
    9. public class MovementController : MonoBehaviour
    10. {
    11.     private float InputX, InputZ, Speed;
    12.  
    13.     private Camera cam;
    14.  
    15.     private CharacterController characterController;
    16.  
    17.     private Animator characterAnimator;
    18.  
    19.     private Vector3 desiredMoveDirection;
    20.    
    21.     [SerializeField] float rotationSpeed = 0.3f;
    22.     [SerializeField] float allowRotation = 0.1f;
    23.     [SerializeField] float movementSpeed = 3f;
    24.     [SerializeField] float gravity;
    25.     [SerializeField] float gravityMultiplier;
    26.     [SerializeField] float slopeLimit = 45f;
    27.  
    28.     [Header("Results")]
    29.     public float groundSlopeAngle = 0f;            // Angle of the slope in degrees
    30.     public Vector3 groundSlopeDir = Vector3.zero;  // The calculated slope as a vector
    31.  
    32.     [Header("Settings")]
    33.     public bool showDebug = false;                  // Show debug gizmos and lines
    34.     public LayerMask castingMask;                  // Layer mask for casts. You'll want to ignore the player.
    35.     public float startDistanceFromBottom = 0.2f;   // Should probably be higher than skin width
    36.     public float sphereCastRadius = 0.25f;
    37.     public float sphereCastDistance = 0.75f;       // How far spherecast moves down from origin point
    38.  
    39.     public float raycastLength = 0.75f;
    40.  
    41.     public Vector3 rayOriginOffset1 = new Vector3(-0.2f, 0f, 0.16f);
    42.     public Vector3 rayOriginOffset2 = new Vector3(0.2f, 0f, -0.16f);
    43.  
    44.     // Start is called before the first frame update
    45.     void Awake()
    46.     {
    47.         characterController = GetComponent<CharacterController>();
    48.         cam = Camera.main;
    49.         characterAnimator = GetComponent<Animator>();
    50.     }
    51.  
    52.     // Update is called once per frame
    53.     void Update()
    54.     {
    55.         InputX = Input.GetAxis("Horizontal");
    56.         InputZ = Input.GetAxis("Vertical");
    57.  
    58.         InputDecider();
    59.         MovementManager();
    60.     }
    61.  
    62.     void FixedUpdate()
    63.     {
    64.         // Check ground, with an origin point defaulting to the bottom middle
    65.         // of the char controller's collider. Plus a little higher
    66.         if (characterController && characterController.isGrounded)
    67.         {
    68.             CheckGround(new Vector3(transform.position.x, transform.position.y - (characterController.height / 2) + startDistanceFromBottom, transform.position.z));
    69.         }
    70.     }
    71.  
    72.     void InputDecider()
    73.     {
    74.         Speed = new Vector2(InputX, InputZ).sqrMagnitude;
    75.        
    76.         if (Speed > allowRotation)
    77.         {
    78.             RotationManager();
    79.         } else
    80.         {
    81.             desiredMoveDirection = Vector3.zero;
    82.         }
    83.     }
    84.  
    85.     void RotationManager()
    86.     {
    87.         var camforward = cam.transform.forward;
    88.         var camright = cam.transform.right;
    89.  
    90.         camforward.y = 0;
    91.         camright.y = 0;
    92.  
    93.         camforward.Normalize();
    94.         camright.Normalize();
    95.  
    96.         desiredMoveDirection = camforward * InputZ + camright * InputX;
    97.         transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(desiredMoveDirection), rotationSpeed);
    98.     }
    99.  
    100.     void MovementManager()
    101.     {
    102.         gravity -= 9.8f * Time.deltaTime;
    103.         gravity = gravity * gravityMultiplier;
    104.  
    105.         Vector3 moveDirection = desiredMoveDirection * (movementSpeed * Time.deltaTime);
    106.         moveDirection = new Vector3 (moveDirection.x, gravity, moveDirection.z);
    107.         characterController.Move(moveDirection);
    108.  
    109.         characterAnimator.SetFloat("Speed", (Mathf.Abs(Input.GetAxis("Horizontal")) + Mathf.Abs(Input.GetAxis("Vertical"))));
    110.  
    111.         if (characterController.isGrounded)
    112.         { gravity = 0; }
    113.  
    114.        
    115.         if (groundSlopeAngle > slopeLimit)
    116.         {
    117.             characterController.Move(groundSlopeDir + Vector3.down * gravity * Time.deltaTime);
    118.         }
    119.     }
    120.  
    121.     /// Checks for ground underneath, to determine some info about it, including the slope angle.
    122.     public void CheckGround(Vector3 origin)
    123.     {
    124.         // Out hit point from our cast(s)
    125.         RaycastHit hit;
    126.  
    127.         {
    128.             Physics.SphereCast(origin, sphereCastRadius, Vector3.down, out hit, sphereCastDistance, castingMask);
    129.             // Angle of our slope (between these two vectors).
    130.             // A hit normal is at a 90 degree angle from the surface that is collided with (at the point of collision).
    131.             // e.g. On a flat surface, both vectors are facing straight up, so the angle is 0.
    132.             groundSlopeAngle = Vector3.Angle(hit.normal, Vector3.up);
    133.  
    134.             // Find the vector that represents our slope as well.
    135.             //  temp: basically, finds vector moving across hit surface
    136.             Vector3 temp = Vector3.Cross(hit.normal, Vector3.down);
    137.             //  Now use this vector and the hit normal, to find the other vector moving up and down the hit surface
    138.             groundSlopeDir = Vector3.Cross(temp, hit.normal);
    139.         }
    140.  
    141.         // Now that's all fine and dandy, but on edges, corners, etc, we get angle values that we don't want.
    142.         // To correct for this, let's do some raycasts. You could do more raycasts, and check for more
    143.         // edge cases here. There are lots of situations that could pop up, so test and see what gives you trouble.
    144.         RaycastHit slopeHit1;
    145.         RaycastHit slopeHit2;
    146.  
    147.         // FIRST RAYCAST
    148.         if (Physics.Raycast(origin + rayOriginOffset1, Vector3.down, out slopeHit1, raycastLength))
    149.         {
    150.             // Debug line to first hit point
    151.             if (showDebug) { Debug.DrawLine(origin + rayOriginOffset1, slopeHit1.point, Color.red); }
    152.             // Get angle of slope on hit normal
    153.             float angleOne = Vector3.Angle(slopeHit1.normal, Vector3.up);
    154.  
    155.             // 2ND RAYCAST
    156.             if (Physics.Raycast(origin + rayOriginOffset2, Vector3.down, out slopeHit2, raycastLength))
    157.             {
    158.                 // Debug line to second hit point
    159.                 if (showDebug) { Debug.DrawLine(origin + rayOriginOffset2, slopeHit2.point, Color.red); }
    160.                 // Get angle of slope of these two hit points.
    161.                 float angleTwo = Vector3.Angle(slopeHit2.normal, Vector3.up);
    162.                 // 3 collision points: Take the MEDIAN by sorting array and grabbing middle.
    163.                 float[] tempArray = new float[] { groundSlopeAngle, angleOne, angleTwo };
    164.                 Array.Sort(tempArray);
    165.                 groundSlopeAngle = tempArray[1];
    166.             }
    167.             else
    168.             {
    169.                 // 2 collision points (sphere and first raycast): AVERAGE the two
    170.                 float average = (groundSlopeAngle + angleOne) / 2;
    171.                 groundSlopeAngle = average;
    172.             }
    173.         }
    174.     }
    175.  
    176.     void OnDrawGizmosSelected()
    177.     {
    178.         if (showDebug)
    179.         {
    180.             // Visualize SphereCast with two spheres and a line
    181.             Vector3 startPoint = new Vector3(transform.position.x, transform.position.y - (characterController.height / 2) + startDistanceFromBottom, transform.position.z);
    182.             Vector3 endPoint = new Vector3(transform.position.x, transform.position.y - (characterController.height / 2) + startDistanceFromBottom - sphereCastDistance, transform.position.z);
    183.  
    184.             Gizmos.color = Color.white;
    185.             Gizmos.DrawWireSphere(startPoint, sphereCastRadius);
    186.  
    187.             Gizmos.color = Color.gray;
    188.             Gizmos.DrawWireSphere(endPoint, sphereCastRadius);
    189.  
    190.             Gizmos.DrawLine(startPoint, endPoint);
    191.         }
    192.     }
    193. }
    194.  
    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?
     
  2. AnimusRex

    AnimusRex

    Joined:
    Apr 26, 2019
    Posts:
    67
    Solved with

    Code (CSharp):
    1.         if (groundSlopeAngle > slopeLimit && characterController.isGrounded)
    2.         {
    3.             characterController.Move((groundSlopeDir + Vector3.down * gravity) * Time.deltaTime * 10);
    4.         }