Slide and Stepping issue

  • Hi all, and thanks for the invite from the

    tutorial.


    O.K. I have a few scripting issues, but for the moment I will focus on the player controller for a 3D based environment,

    It is a Rigidbody and uses a zero-friction physics to keep player from getting stuck on things (at least that's what it is suppose to do).

    It has been created using the current Visual Studio editor and most recent up to date version of Unity 2018.


    To make it easier to identify the problems I will bullet the issues:


    Currently:

    1. Player sometimes walks up over step edges, sometimes wont regardless of step height settings.
    2. Player slides off slopes and curved ground regardless of slope and slide settings.
    3. Player seems to not be able to determine if it is in locomotion mode or slide mode on some completely flat surfaces.

    Now for why coding has been done the way it has been in attempts to resolve several more previous issues:

    1. If the player stepped off a mesh or any collider it was staying stuck at that height.
    2. In some cases it would shoot right off into space or shoot straight through the ground.
    3. Player would get stuck either on edges or on other meshes and refused to move despite animations being called.
    4. Player script then was a given a raycast to help detect and find ground to stop a lot of these gravity issues.
    5. In some cases player was getting stuck on flat horizontal planes.

    I have notes in the script detailing what it is suppose to do and some are // out because of trying to find the best options (and still failing).

    Eventually I will be wanting to add to the script things like health and additional animations,

    though I am thinking it may be better to have others like combat, climbing and dodging handled by another script, such as a melee combat feature.

    In any case, do note there is a section for a separate camera to find the player which is suppose to clone in from scene to scene and focus just on the player.

    It also has developed recently an issue but is very minor by comparison.

    Any assistance to improve the script would be much appreciated as I literally have pulled hair out being driven mad trying to figure out why its not working.



    Now for the code itself:


    using System.Collections;

    using System.Collections.Generic;

    using UnityEngine;



    public class PlayerController : MonoBehaviour

    {

    public enum State { loco, jump, fall, slide };

    public State state = State.loco;


    //public PlayerData playerData;


    [Range(1, 10)] public float jumpForce = 5;

    [Range(1, 10)] public float speedMultiplier = 5;

    [Range(1, 10)] public float rotSpeed = 2;

    [Range(0, 10)] public float stepHeight = 0.4f;

    [Range(0, 1)] public float lookAhead = 0.5f;

    [Range(0, 20)] public float gravityWeight = 12f;

    [Range(1, 10)] public float slideSpeedMax = 5;


    private Animator anim;

    private Rigidbody rb;

    public bool isGrounded;

    private bool isRunning;

    private Vector3 newVelocity = Vector3.zero;

    private Vector3 newGravityVelocity = Vector3.zero;

    [SerializeField] [Range(0, 1)] private float _slideStopAngle = 0.5f;

    [SerializeField][Range(0, 1)] private float _slideStartAngle = 0.7f;


    public float SlideAngle { get { return state == State.slide ? _slideStopAngle : _slideStartAngle; } }


    private float speed;


    public Transform camTarget;


    private AudioSource audioSource;

    public AudioCollection footsteps;

    private float footstepTimer = 0;

    public AudioSource Jump;

    [SerializeField] [Range(0, 1)] float walkStepFrequency = 0.5f;

    [SerializeField] [Range(0, 1)] float runStepFrequency = 0.25f;


    private float groundedStateDuration = 0;


    private void Start()

    {

    gameObject.layer = Layers.Player;

    rb = GetComponent<Rigidbody>();

    anim = GetComponent<Animator>();

    audioSource = GetComponent<AudioSource>();

    }


    private void Update()

    {

    if (Game.UIFocus) { return; }


    bool currentGroundedState = GetGrounded();


    if(isGrounded != currentGroundedState)

    {

    groundedStateDuration += Time.deltaTime;

    if(groundedStateDuration > 0.1f)

    {

    isGrounded = currentGroundedState;

    groundedStateDuration = 0;

    }

    }

    //isGrounded = GetGrounded();


    Vector3 input = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));

    anim.SetFloat("InputX", input.x * speed);

    anim.SetFloat("InputY", input.z * speed);


    //Footsteps!!!!

    if(input.magnitude > 0)

    {

    footstepTimer += Time.deltaTime;


    if (isRunning && footstepTimer > runStepFrequency && input.z > 0 || footstepTimer > walkStepFrequency)

    {

    audioSource.PlayOneShot(footsteps.NextClip());

    footstepTimer = 0;

    }

    }


    //tranistion run states

    if (Input.GetButtonDown("Run"))

    {

    isRunning = !isRunning;

    }

    if (isRunning)

    {

    speed = 1.5f;

    }

    else

    {

    speed = 0.5f;

    }


    //jump

    if (Input.GetButtonDown("Jump") && isGrounded && state == State.loco)

    {

    anim.SetTrigger("isJumping");

    Jump.Play();

    rb.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);

    newGravityVelocity = Vector3.up * jumpForce;

    state = State.jump;

    }


    if (state == State.slide)

    {

    Slide();

    }

    else

    {

    MovePlayer();

    }


    //Apply motion

    if (state == State.loco)

    {

    float x = Input.GetAxisRaw("Vertical") < 0 ? -input.x : input.x;

    //Debug.Log(x);

    //rb.angularVelocity = Vector3.up * x * rotSpeed;

    transform.Rotate(transform.up, x * rotSpeed);

    }


    }


    private void FixedUpdate()

    {

    rb.velocity = newVelocity + newGravityVelocity;

    Debug.DrawRay(transform.position, rb.velocity, Color.magenta);

    }


    private void Slide()

    {

    Vector3 input = transform.TransformDirection(new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical")));

    Vector3 cross = Vector3.Cross(Vector3.up, ground.normal);

    Vector3 slideDir = Vector3.Cross(cross, ground.normal);


    newVelocity = (slideDir + input).normalized * rb.velocity.magnitude;

    newVelocity += slideDir * GravityTerm();


    if (newVelocity.magnitude > slideSpeedMax)

    newVelocity = newVelocity.normalized * slideSpeedMax;

    }


    private void MovePlayer()

    {

    Vector3 input = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));

    Vector3 proposedPos = transform.TransformVector(new Vector3(0, 0, input.z * speed * speedMultiplier));


    //If we're jumping

    if (newGravityVelocity.y > 0)

    {

    newVelocity = proposedPos;

    newGravityVelocity += Vector3.down * GravityTerm();

    return;

    }


    Ray testPosition = new Ray(transform.position + (Vector3.up * stepHeight) + (proposedPos.normalized * lookAhead), Vector3.down);


    //ground test

    RaycastHit point;

    if (Physics.Raycast(testPosition, out point, stepHeight * (2), Layers.m_default))

    {


    //We want to move to point, but the distance to point could range from differ up to ~0.4,

    //Create a vector from our position to point

    Vector3 vector = point.point - transform.position;


    //If we're not moving push into ground

    if (proposedPos.magnitude < 0.02f)

    {

    //Debug.Log("we are stopped!");

    vector = -point.normal;

    }

    else

    {


    //Scale it to be the same length as our proposed position

    vector = vector.normalized * proposedPos.magnitude;

    }

    //Set our new velocity

    newVelocity = vector;


    //Remove Gravity

    newGravityVelocity = Vector3.zero;

    }


    //If we didn't find a point to move to in direction of motion. But we still want some control

    else if(state == State.loco)

    {

    newVelocity = proposedPos;

    }


    if(!isGrounded || state == State.fall) {

    //If we can't find any ground to magnatize to apply gravity

    newGravityVelocity += Vector3.down * GravityTerm();


    //Lock at a terminal velocity

    newGravityVelocity.y = Mathf.Max(newGravityVelocity.y, -gravityWeight);

    }

    }

    private RaycastHit ground = new RaycastHit();



    private bool GetGrounded()

    {

    if (ground.normal == Vector3.zero)

    ground.normal = Vector3.up;


    //If moving up and jumping

    if (state == State.jump && rb.velocity.y > 0)

    {

    return false;

    }

    else

    {

    //if (Physics.Raycast(transform.position + Vector3.up * 0.5f, Vector3.down, out ground, 2.5f, Layers.m_default)) // <== note this part here seemed to work less effectively, but still here to show some solutions attempted.


    if (Physics.Raycast(transform.position + Vector3.up * 0.1f, -ground.normal, out ground, 1f, Layers.m_default))

    {

    //Debug.Log("Ground Dot: "+Vector3.Dot(ground.normal, Vector3.up));

    //if moving down and higher than 1m

    if (ground.distance > 1 && rb.velocity.y < 0)

    {

    state = State.fall;

    return false;

    }

    //if not on the ground

    else if (ground.distance > 0.2f && state != State.slide)

    {

    return false;

    }

    else if (Vector3.Dot(ground.normal, Vector3.up) < SlideAngle)

    {

    state = State.slide;

    return true;

    }

    //else on the ground

    else

    {

    state = State.loco;

    return true;

    }

    }

    //else higher than 2m

    else

    {

    state = State.fall;

    return false;

    }

    }

    }

    private float GravityTerm()

    {

    return gravityWeight * Time.deltaTime;

    }

    }


    Please show me by note what i need to fix and what the specific code string is and explain what it does and how it differs from what I have done here. Did I put something in the wrong place? thanks a bunch in advance for any volunteered help, and please be aware I am a newbie and I do have a learning disability. :)