Rigidbody Sticking to Walls

Hey everyone, I’m having an issue where my character will stick into the side of a wall instead of sliding down, or falling to the ground like they should. I am using a rigidbody and capsule collider combination. I am using a C#Messenger System to listen for inputs from my controls class. the movements function is called during fixedupdate.

I am using bitshift operators to store my inputs because i’m recording the characters inputs to be played back.

Here is a sample of my movement code and my OnCollisionStay

//PLAYER MOVEMENT
    private void movement()
    {
        //Used for storing the characters movement Commands
        Vector3 targetVelocity = Vector3.zero;
        
        //Check for a New Input
        if (activeCharacter)
        {
            //If The oldCommand does not equal the Current command then create/store an input change
            if (oldCommand != command)
            {
                //record the current elapsed time
                float elapsedTime = worldScript.elapsedTime;
                
                //create the Input Record and Assign the oldCommand to Current command
                recordingScript.createRecord(name, elapsedTime, command, transform.position.x);
                oldCommand = command;
            }

        }

        //MOVING FORWARD AND BACKWARD
        if ((command  forward) == forward)
        {
            faceLeft = false;
            targetVelocity.x = 1;
            if (!playerJumping)
            {
                animScript.walkingRight();
            }
        }
        if ((command  backward) == backward)
        {
            faceLeft = true;
            targetVelocity.x = -1;
            if (!playerJumping)
            {
                animScript.walkingLeft();
            }
        }

        //JUMPING
        if (((command  jumping) == jumping))
        {
            //Make Sure I am on the Ground
            if (!falling)
            {
                Debug.Log("STARTING JUMP");
                canJump = false;
                playerJumping = true;
                animScript.jumping();
            }
        }

        //Multiple my movements by MoveSpeed
        targetVelocity *= moveSpeed;

        //Modify the Characters Velocity and Determine the Change in Velocity
        Vector3 velocity = rigidbody.velocity;
        Vector3 velocityChange = (targetVelocity - velocity);

        //Clamp the Velocity change to the MaxVelocity variable
        velocityChange.x = Mathf.Clamp(velocityChange.x, -maxVel, maxVel);
        velocityChange.y = 0;

        //Add the Change in Velocity to the Current Character
        rigidbody.AddForce(velocityChange, ForceMode.VelocityChange); //////////////////ORIGINAL
        
        //NEEDS TO BE CHECKED FOR JUMP SPAMMING
        //Player is Jumping
        if (playerJumping)
        {
            Debug.Log("Jumping");
            //Add The JumpForce to the Characters Y position
            rigidbody.AddForce(new Vector3(0, jumpForce, 0), ForceMode.VelocityChange); //////////////////ORIGINAL

            //Successively diminish the strength of the Jump
            jumpForce -= jumpSpeed;

            //Once the jumpForce is less than 0 the character is no longer ascending
            if (jumpForce < 0)
            {
                falling = true;
            }

            //Limit the fall speed of the character
            if (jumpForce < fallCap)
            {
                jumpForce = fallCap;
            }
             
        }

        //Manually Apply Gravity to Character
        rigidbody.AddForce(new Vector3(0, -gravity, 0));

        //Always Assume Falling
        falling = true;
    }

    //Check for CollisionStay - Check For Still on Ground
    public void OnCollisionStay()
    {
        Debug.Log("OnCollisionStay");
        //Issue with WallJumps Currently//
        //Once the player is not Jumping - Reset JumpForce
        if (!playerJumping)
        {
            Debug.Log("RESETTING JUMP");
            jumpForce = resetJumpForce;
            superJumpForce = superResetJumpForce;
            canJump = true;
        }

        
        falling = false;
        playerJumping = false;
 
    }

So there are two problems i’m facing right now,

  1. My character sticks into the sides of walls when applying force into that wall. So i cling to walls…
  2. OnCollisionStay doesn’t check to see if im on the ground, just checks for continuous collision with an object. So I can super jump…

I solved these problems by using a CharacterController but in my game my character is required to interact with other objects that can push my character, whether he’s moving or not…

So i’m stuck between figuring out a solution to the rigidbody problem, or finding a solution to the character controller solution… :face_with_spiral_eyes: Please Help

for a rigidbody controller script that checks if it is grounded:
http://forum.unity3d.com/threads/3024-RigidBodyFPSWalker-sliding-around

you might have to add a small offset if it is not working as is
http://forum.unity3d.com/threads/53490-FPS-Rigidbody-Controller

awesome thanks, i’ll run through these and let u know how it goes

edit: the rigidbody FPS walker is has the same problem that im facing right now, i tried that one a little earlier. I’ll try out the second one though

I know this is a dead post but for anyone interested here is working source code for a rigid body movement controller. It works for slopes, walls, and is velocity based. The next step I would like is to allow air control.

#region Using Directives
using UnityEngine;
#endregion

[RequireComponent(typeof(CapsuleCollider))]
[RequireComponent(typeof(Rigidbody))]
public class PlayerMovementController : MonoBehaviour
{
    #region Public Variables
    public float MoveSpeed = 10f;
    public float MaxVelocityChange = 10f;
    public float JumpHeight = 2.0f;
    public float SlopeLimit = 45f;

    public bool CanJump = true;

    public Vector3 GravityConstant = Physics.gravity;
    #endregion

    #region Member Variables
    private Rigidbody MyRigidBody;

    private bool IsGrounded = false;
    private bool IsOnSlope = false;
    #endregion

    #region Unity Functions
    private void Awake()
    {
        MyRigidBody = GetComponent<Rigidbody>();
    }

    private void FixedUpdate()
    {
        float horizontal = Input.GetAxis("Horizontal");
        float vertical = Input.GetAxis("Vertical");

        Vector3 move = new Vector3(horizontal, 0, vertical);
        /** Ensure we are moving according to our direction. */
        move = transform.TransformDirection(move);
        move *= MoveSpeed;

        /** Get our rigid bodies velocity and determine the change from our new move. */
        Vector3 velocity = MyRigidBody.velocity;
        Vector3 velocityChange = (move - velocity);

        /** Ensure we do not go over or under our constraint. */
        velocityChange.x = Mathf.Clamp(velocityChange.x, -MaxVelocityChange, MaxVelocityChange);
        velocityChange.z = Mathf.Clamp(velocityChange.z, -MaxVelocityChange, MaxVelocityChange);
        velocityChange.y = 0;

        if (IsGrounded && !IsOnSlope)
        {
            /** Placed inside ground check to ensure we do not stick to walls. */
            MyRigidBody.AddForce(velocityChange, ForceMode.VelocityChange);

            if (CanJump && Input.GetButton("Jump"))
            {
                MyRigidBody.velocity = new Vector3(velocity.x, CalculateJumpVelocity(JumpHeight, GravityConstant.y), velocity.z);
            }
        }

        /** Apply gravity to our rigid body. */
        MyRigidBody.AddForce(new Vector3(0, GravityConstant.y * MyRigidBody.mass, 0));

        /** Reset our grounded check so it is re-run. */
        IsGrounded = false;
    }

    private void OnCollisionStay(Collision collision)
    {
        /** Get our initial contact point, conveniently this is the bottom one on a capsule collider. */
        ContactPoint contact = collision.contacts[0];

        /** Determine the slope of the surface we are on. */
        Vector3 slope = Vector3.Cross(contact.normal, -transform.right);
        /** Determine the angle difference from our forward vector to the direction of the slope. */
        float angle = Vector3.Angle(slope, transform.forward);

        /** If the angle is greater than our slope limit than we are on a slope. */
        IsOnSlope = (angle > SlopeLimit) ? true : false;

        CheckGrounded(collision);
    }
    #endregion

    #region Member Functions
    /// <summary>
    /// Determines if we are grounded based on our collision, and its relative height to the bottom portion of the collider.
    /// </summary>
    /// <param name="collision">Where we contacted the other surface.</param>
    private void CheckGrounded(Collision collision)
    {
        /** The minimum height for our collision based on the bottom portion of our capsule. */
        float minHeight = GetComponent<CapsuleCollider>().bounds.min.y + GetComponent<CapsuleCollider>().radius;
        foreach(ContactPoint c in collision.contacts)
        {
            /** If our contact points height is less than our minimum height than we are grounded,
            * otherwise it is a surface higher than the ground*/
            if(c.point.y < minHeight)
            {
                IsGrounded = true;
            }
        }
    }

    /// <summary>
    /// Determines the required velocity to reach the required height.
    /// </summary>
    /// <param name="height">The intended height to reach for the jump. </param>
    /// <param name="gravity">The gravity constant for the jump. </param>
    /// <returns>The required velocity to reach given height.</returns>
    private float CalculateJumpVelocity(float height, float gravity)
    {
        /**
        * Formula: sqrt(2 * gravity * height)
        * We are taking the absolute value because gravity is negative and our calculation will be NaN.
        */
        return Mathf.Sqrt(Mathf.Abs(2 * gravity * height));
    }
    #endregion
}

Edit: Known bug when you are grounded but push into a surface >= 90 degrees where you can not jump.

1 Like