3
\$\begingroup\$

I created a script that handles movement for my player (moving around, going down and up slopes). However, since I'm using a RigidBody not a CharacterController when I go up slope and stop on the middle of it the gravity pulls me backward, which is fine, but the problem is when I move forward, the gravity force pulls me backward when I'm trying to go forward. This problem wouldn't have appeared if my character wasn't a sphere but my character is a sphere so it always roll down when gravity is applied to it.

What I want is to somehow stop the gravity for a second or reset it to zero when I move my character, and when I'm going up a slope so it won't hold me back. I tried:

if(grounded) rb.velocity = Vector3.zero;

but I don't think it's the right approach.

\$\endgroup\$
1
  • \$\begingroup\$ Have you tried if (grounded) rb.useGravity = false; ? \$\endgroup\$
    – jhocking
    Aug 3, 2017 at 18:14

4 Answers 4

4
\$\begingroup\$

I've had great success by the following method. The idea is that, under the specific circumstance that the Rigidbody is in contact with a surface appropriately angled in relation to gravity(which is to say, 'a slope that isn't too steep'), it will be pulled toward the surface normal instead of in the direction of gravity. As a bonus, this code will work regardless of what direction gravity happens to currently be.

Rigidbody ourRigidBody;
bool isGrounded; // Handy to have for jumping, and we'll use it here too
Vector3 currentGravity; // Just holds some data for us...
ContactPoint[] cPoints; // ContactPoints are generated by Collision, and they hold lots of fun data.
public float maxGroundedAngle = 45f; //The steepest you want the character to be able to stand firmly on. Steeper than this and they'll slide.
Vector3 groundNormal; //The angle that will be perpendicular to the point of contact that our Rigidbody will be grounded on.

void Start()
{
    ourRigidBody = GetComponent<Rigidbody>();
    ourRigidBody.useGravity = false; //we'll make our own!
    /*Freezing rotation is not necessary, but highly recommended
      if we're making a character rather than just some object
      that happens to be on a slope.*/
}

void OnCollisionStay(Collision ourCollision)
{
    isGrounded = CheckGrounded(ourCollision);
}

void OnCollisionExit(Collision ourCollision)
{
    /*It's okay to not have to check whether or not
     the Collision we're exiting is one we're grounded on,
     because it'll be reaffirmed next time OnCollisionStay runs.*/
    isGrounded = false;
    groundNormal = new Vector3(); //Probably not necessary, but a good habit, in my opinion
}

bool CheckGrounded(Collision newCol)
{
    cPoints = new ContactPoint[newCol.contactCount];
    newCol.GetContacts(cPoints);
    foreach(ContactPoint cP in cPoints)
    {
        /*If the difference in angle between the direction of gravity
          (usually, downward) and the current surface contacted is
          less than our chosen maximum angle, we've found an
          acceptable place to be grounded.*/
        if(maxGroundedAngle > Vector3.Angle(cP.normal, -Physics.gravity.normalized))
        {
            groundNormal = cP.normal;
            return true;
        }
    }
}

void ObeyGravity()
{
    if(isGrounded == false)
    {
        //normal gravity, active when not grounded.
        currentGravity = Physics.gravity;
    }
    else if(isGrounded == true)
    {
        /*Not normal gravity. Instead of going down, we go in the
        direction perpendicular to the angle of where we're standing. 
        This means whatever surface we're grounded on will be 
        effectively the same as standing on a perfectly horizontal 
        surface. Ergo, no sliding will occur. */
        currentGravity = -groundNormal * Physics.gravity.magnitude;
    }
    ourRigidBody.AddForce(currentGravity, ForceMode.Force);
}

void FixedUpdate()
{
    ObeyGravity();
}

```
\$\endgroup\$
1
  • 1
    \$\begingroup\$ This is a great answer! One change that I think is important is to make the ForceMode = ForceMode.Acceleration. That will make the custom gravity force apply equally regardless of the object's mass. \$\endgroup\$
    – Ben Throop
    Sep 5, 2020 at 20:18
0
\$\begingroup\$

Not saying that turning off gravity is the right approach, but if you want to cancel gravity, you only need to apply a force equal to gravity. The sum of the forces is then zero, the object doesn't move. Thus, anti-gravity is enabled.

public class AntiGravity : MonoBehaviour
{
    private Rigidbody rb;
    public Rigidbody getRigidbody
    {
        get { return rb ?? (rb = GetComponent<Rigidbody>()); }
    }

    void FixedUpdate()
    {
        Vector3 v = Physics.gravity * getRigidbody.mass;
        getRigidbody.AddForce(-v);
    }
}

You'd apply the anti-gravity force in the same FixedUpdate frame that you apply your movement force.

\$\endgroup\$
1
  • \$\begingroup\$ I trieded your approache to nulify the gravity,it works as well but for some reason when i'm in the middle of the slope and repeat the process of moving up and letting gravity pull me down it stops working and the power of gravity pull me down to the point where i cant move up an inch, not sure if it's a bug or it's due to something else. \$\endgroup\$ Aug 3, 2017 at 20:16
0
\$\begingroup\$

I would not change the global gravity. This will cause numerous complications in the future. Instead of changing the gravity, have you considered increasing the friction on the player or the slope? Maybe attaching a physic material to the player and then modifying the PhysicMaterial.staticFriction in the editor or at runtime via code might help? Static friction seems to work on objects that standing still which would stop the sliding on your character.

\$\endgroup\$
3
  • \$\begingroup\$ Since the goal is to go up the slope, I'm not sure how increasing friction would help. There are too many forces going against the player heading up the slope, adding more to that won't help. \$\endgroup\$
    – House
    Aug 3, 2017 at 18:14
  • \$\begingroup\$ I know, thats why I suggested to modify the static friction. For example dynamic friction can be set to Zero so it won't affect the player while moving up or down the slope, but then Static friction can set to a decent value so when the player stops moving they won't slide down the slope unless a force is applied. \$\endgroup\$ Aug 3, 2017 at 18:21
  • \$\begingroup\$ Won't work for a sphere, and OP said they were stopping on the slope in the middle. \$\endgroup\$
    – House
    Aug 3, 2017 at 18:23
0
\$\begingroup\$

After doing some debugging i found that using if(grounded) rb.velocity = Vector3.zero wasnt the issue, the issue was in mycheckgrounded function i had to add 0.05f to the height of the ray because when i was on the slope the grounded bool was false, wish was odd but actually it made sense my object wasnt rotated at the same angle as the slope so that must have created a small gap between the slope and my player.

\$\endgroup\$
0

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .