Custom gravity based on normal vector and rigidbody.addForce

Hey all

I'm creating a game, where a ball is moving inside a tube. The ball should have the possible of moving along the inside of the tube all the way around, like it is stuck to the wall. alt text

I have made a sample in 2D, which you can see a sketch of here. This shows a ball which is in the top half of the circle. Under normal gravity (one way gravity) it would fall down, but in my case, the gravity force should force the ball to stay close to the wall. The gravity axis is the negative normal vector in this case.

So, my problem is, that to ball won't stop bounce as soon as it hits the wall. The gravity axis is being set correctly..

I have based my gravity system a little on the FauxGravity.

Here is my code for the ball:

using UnityEngine;
using System.Collections;

[RequireComponent(typeof(Rigidbody))]
public class GravityForce : MonoBehaviour
{
    // are we touching the surface? 
    private int grounded = 0;

    // Set to true for mono-directional gravity 
    private bool useLocalUpVector = false; 

    // Force applied along gravity up-vector (negative = down) 
    private float fauxGravity = 0.5f; 

    private float speed = 2f;

    void Start()
    {
        rigidbody.WakeUp();
        rigidbody.useGravity = false;
    }

    void FixedUpdate() 
    {
        Vector3 gravityUp;
        Vector3 tangent;
        Vector3 moveDirection = new Vector3(0f, 0f, 0f);
        // Figure out the body's up vector 
        if (useLocalUpVector)
        {
            gravityUp = transform.up;
        }
        else
        {
            RaycastHit hit;
            bool hitData = Physics.Raycast(transform.position, -transform.up, out hit);
            gravityUp = new Vector3(0f, 1f, 0f);
            if (hitData)
            {
                if (hit.distance < 0.1) {
                    grounded++;
                    Debug.DrawRay(hit.point, hit.normal, Color.cyan);
                    gravityUp = -hit.normal.normalized;
                    tangent = Vector3.Cross(hit.collider.transform.right, hit.normal).normalized;
                    Debug.DrawRay(hit.point, tangent, Color.magenta);

                    Debug.Log("gravityUp: " + gravityUp);
                }
            }

            gravityUp.Normalize();
        }

        // Accelerate the body along its up vector
        Vector3 v = gravityUp;
        rigidbody.AddForce(new Vector3(v.x, v.y, 0) * fauxGravity * rigidbody.mass);
        rigidbody.drag = (grounded > 0) ? 1.0f : 0.1f; 
    }
}

Unity package sample (zip-file)

I hope you can help :-S

I've tried your package and i've found some problems:

  1. You overall scale is too small. Physics needs some intersection to detect collisions properly. Your sphere collider have a radius of 0.36mm (0.00036 units) that's why the collision response doesn't work properly.
  2. Your raycast approach is a bit confusing. You raycast always in the same direction so you can never make a full circle.
  3. You inverted the gravity two times once where you get the normal vector and then you subtract it from your force.
  4. Your movement speed is also affected by your `fauxGravity` variable.
  5. If you use `AddForce` you mustn't multiply with the mass. `acceleration = Force/mass` that is calculated by unity, you just give the force value.

If you don't have other objects affected by gravity, I would recommend to use the default gravity and change that one accordingly. Just set `Physics.gravity` to your gravity. Instead of a Raycast just use the collision info you get from the collision events. OnCollisionStay will work best in that case.

float myGravity = 9.81f;
void OnCollisionStay(Collision col)
{
    Vector3 n = Vector3.zero;
    foreach (ContactPoint C in col.contacts)
        n += C.normal;
    n.Normalize();

    Physics.gravity = -n*myGravity;
}

If you want to work only in 2D you should reset the third axis frequently. Physics response can some add little forces in any direction.

I've tweaked it a bit and it works for me. I can roll inside or outside ;) (outside can cause some problems, if you move very fast you just start to fly)

I calculated the tangent with crossproduct between z-axis (Vector3.forward) and gravity vector.

ps. even in FixedUpdate you should use Time.deltaTime. The values will match realworld values (velocity in m/s, acceleration in m/s and not sure about the mass, if it's in kg the force would be in N (kg*m/s))

Try setting Acceleration ForceMode (which gravity is).

var gravityAcc = new Vector3(gravityUp.x, gravityUp.y, 0) * fauxGravity;
rigidbody.AddForce(gravityAcc, ForceMode.Acceleration);