Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Question How to use velocity smoothly to implement CharController sliding

Discussion in 'Scripting' started by karsten-heim, Jan 29, 2023.

  1. karsten-heim

    karsten-heim

    Joined:
    May 16, 2022
    Posts:
    63
    I have a script for my charactercontrolller so that it slides when the angle of the surface that the bottom of it is colliding with is greater than the slopelimit, which generally works fine with slopes from 0-65 degree surfaces, but does not move at all when it is on slopes steeper that 65 degrees, and instead continues to build negative y-velocity without sliding down the slope. I tried using Vector3.ProjectOnPlane, which to my understanding provides the direction that the charactercontroller needs to slide in based on the normal/angle of the slope, but it still won't slide. I think that the side of my charactercontroller may be getting stuck on the slopes, but I don't see how that prevents the controller itself from sliding. Here are the parts of my scripts pertaining to the sliding:


    Code (CSharp):
    1.  
    2. if(issliding)
    3.         {
    4.             Vector3 velocity2 = slopeslidevelocity;
    5.             charcont.Move(velocity2 * Time.deltaTime);
    6.         }
    7.  
    8. if(groundee)
    9.         {
    10.             Debug.Log("Detecting Angle");
    11.             float angle = Vector3.Angle(ray2.normal, Vector3.up);
    12.             angle = angle * 17;
    13.             angled2 = angle;
    14.             if (angle >= charcont.slopeLimit)
    15.             {
    16.                 slopeslidevelocity = Vector3.ProjectOnPlane(new Vector3(0, -velocity.y * multiplier, 0), ray2.normal);
    17.                 return;
    18.             }
    19.         }
    20.         slopeslidevelocity = Vector3.zero;
    and

    Code (CSharp):
    1. void OnCollisionStay(Collision col)
    2.     {
    3.         cont = col.contacts[0];
    4.         collisionpoint = col.contacts[0].point;
    5.         onground = true;
    6.      
    7.     }
    8.     void OnCollisionExit()
    9.     {
    10.         onground = false;
    11.     }
    12.     void Update()
    13.     {
    14.         movescripttt.groundee = onground;
    15.         if (onground)
    16.         {
    17.             posy = new Vector3(transform.position.x, transform.position.y + top, transform.position.z);
    18.             raycsted = Physics.Linecast(posy, collisionpoint, out RaycastHit hit);
    19.             Debug.DrawLine(posy, collisionpoint);
    20.             if (raycsted)
    21.             {
    22.                 ray1 = hit;
    23.                 movescripttt.ray2 = ray1;
    24.             }
    25.         }
    26.     }
    the second part of the script functions fine to my knowledge, (though feel free to see if the error lies within it), as it consistently outputs an accurate angle, but the issue is mostly what I described earlier where it gets stuck on steeper slopes, despite the angle being detected as above the slopelimit.

    if anybody has any ideas how to fix this it would be much appreciated :)
     
  2. karsten-heim

    karsten-heim

    Joined:
    May 16, 2022
    Posts:
    63
    ANOTHER UPDATE: ran into a few more issues with the script. The slope sliding works fine now, as the character can slide down slopes smoothly, but when moving into the slopes, (whether it be from the base of the slope or while on the slope), too much friction is created, or I have conflicting velocities maybe, making the character try to move in two directions at once, and the character slides down the slope erratically instead of smoothly. Ideally what I think I need to do is somehow prevent the character from ever moving towards the slope, or disable friction while on the slope, neither of which I know how to do with a charactercontroller. I'm sure that there is a way to stop my charactercontroller from moving into the slope (which is what I want ideally), but alas, I cannot find any resources on how to do it, and struggle with mathematics. Here is the full script if anybody happens to find a solution or a helpful resource: (ignore the amount of comments, excessive if statements, and public variables I'll clean those up later)
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class Movement : MonoBehaviour
    6. {
    7.     public Transform grounded;
    8.     public float grounddist = 0.4f;
    9.     public CharacterController charcont;
    10.     public float speed = 15f;
    11.     Vector3 velocity;
    12.     public float gravity = -10f;
    13.     public LayerMask groundmask;
    14.    // bool isgrounded;
    15.     public float fallset = -2f;
    16.     public float jumpheight = 3f;
    17.     public GameObject returnpoint;
    18.     public Transform cielingchecker;
    19.     public bool hitcieling;
    20.     public bool issliding;
    21.     public Vector3 slopeslidevelocity;
    22.     public bool groundee;
    23.     public float spherezize = 0.5f;
    24.     public float angled2;
    25.     public RaycastHit ray2;
    26.     public float multiplier;
    27.     public bool moveoffslope;
    28.     public float combinedvelocity;
    29.     //public bool hasmovedthisframe;
    30.     //public Camera playercam;
    31.     //public Vector3 raycastlocation;
    32.  
    33.  
    34.     void OnDrawGizmos()
    35.     {
    36.         Gizmos.color = Color.red;
    37.         Gizmos.DrawSphere(grounded.position, 0.45f);
    38.     }
    39.     void Update()
    40.     {
    41.         if(Input.GetKeyDown(KeyCode.X))
    42.         {
    43.             Debug.Log(velocity - slopeslidevelocity);
    44.         }
    45.      //   isgrounded = Physics.CheckSphere(grounded.position, grounddist, groundmask);
    46.         hitcieling = Physics.CheckSphere(cielingchecker.position, grounddist, groundmask);
    47.         velocity.y += gravity * Time.deltaTime;
    48.         Vector3 forward = transform.TransformDirection(Vector3.down) * 50;
    49.         Debug.DrawRay(transform.position + Vector3.up, forward, Color.red);
    50.         slopeslideveloc();
    51.  
    52.         if (groundee == true && velocity.y < 0)
    53.         {
    54.             if (slopeslidevelocity != Vector3.zero)
    55.             {
    56.                 issliding = true;
    57.             }
    58.             if(issliding != true)
    59.             {
    60.                 velocity.y = fallset;
    61.             }
    62.  
    63.         }
    64.         float x = Input.GetAxis("Horizontal");
    65.         float z = Input.GetAxis("Vertical");
    66.         Vector3 move = transform.forward * x + (transform.right * -1) * z;
    67.         move = Vector3.ClampMagnitude(move, 1f);
    68.         Vector3 move2 = (transform.forward * x + (transform.right * -1) * z)/6;
    69.         move2 = Vector3.ClampMagnitude(move2, 1f);
    70.         if (groundee && !issliding)
    71.         {
    72.             charcont.Move(move * speed * Time.deltaTime);
    73.             //hasmovedthisframe = true;
    74.         }
    75.        
    76.         if (!groundee && !issliding)
    77.         {
    78.             charcont.Move(move * speed * Time.deltaTime);
    79.             //hasmovedthisframe = true;
    80.         }
    81.        /* if(issliding)
    82.         {
    83.             Vector3 velocity2 = slopeslidevelocity;
    84.             charcont.Move(velocity2 * Time.deltaTime);
    85.         } */
    86.  
    87.         if (!hitcieling)
    88.         {
    89.  
    90.             if(Input.GetButtonDown("Jump")&& groundee && issliding == false)
    91.             {
    92.                 // used to calculate jump height.
    93.                 velocity.y = Mathf.Sqrt(jumpheight * -2f * gravity);
    94.             }
    95.         }
    96.         if(hitcieling)
    97.         {
    98.             velocity.y = fallset;
    99.         }
    100.  
    101.  
    102.         if(slopeslidevelocity == Vector3.zero)
    103.         {
    104.             issliding = false;
    105.         }
    106.         if(issliding)
    107.         {
    108.             charcont.Move((velocity + slopeslidevelocity + move2) * Time.deltaTime);
    109.             //hasmovedthisframe = true;
    110.         }
    111.         if(!issliding)
    112.         {
    113.             charcont.Move(velocity * Time.deltaTime);
    114.             //hasmovedthisframe = true;
    115.         }
    116.        // hasmovedthisframe = false;
    117.     }
    118.     private void slopeslideveloc()
    119.     {
    120.         //Vector3 spherecastpos = transform.position
    121.         //bool grounchcheckee = Physics.CheckSphere(grounded.position, grounddist, groundmask);
    122.  
    123.         //groundee = Physics.SphereCast(transform.position, spherezize, Vector3.down, out RaycastHit hit);
    124.         if(groundee)
    125.         {
    126.             //Debug.Log("e");
    127.             float angle = Vector3.Angle(ray2.normal, Vector3.up);
    128.             angle = angle * 17;
    129.             angled2 = angle;
    130.             if (angle >= charcont.slopeLimit)
    131.             {
    132.                 slopeslidevelocity = Vector3.ProjectOnPlane(new Vector3(0, -velocity.y * multiplier, 0), ray2.normal);
    133.              
    134.                 return;
    135.             }
    136.         }
    137.         slopeslidevelocity = Vector3.zero;
    138.     }
    139.     void OnTriggerStay(Collider collided)
    140.     {
    141.         if (collided.gameObject.tag == "worldbox")
    142.         {
    143.             transform.position = returnpoint.transform.position;
    144.  
    145.         }
    146.     }
    147. }
    any help appreciated, Cheers, -K:D
     
  3. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,522
    Not sure where it is going wrong but I just made a convex and concave (think curved pipe) super-steep curving slope for this character controller and it behaved "as I would expect," both trying to go up it AND for trying to go down it without sliding down.

    The code (not mine):

    https://forum.unity.com/threads/a-basic-first-person-character-controller-for-prototyping.1169491/

    If does jitter if you hit the UP limit, but it might be easy to fix that by halving the move distance and bisecting the distance you move to try and take you RIGHT to the hairy edge of the slope change.
     
  4. karsten-heim

    karsten-heim

    Joined:
    May 16, 2022
    Posts:
    63
    You might have to dumb this down for me, as I do not fully understand what you mean by UP limit (slope limit?) and what bisecting distance is, but is what you are trying to say instead of moving past the base of the slope, just halve the move distance every time you get near it so you get closer to it but never reach it?