0
\$\begingroup\$

This is a question that I think is best to explain visually, so I will do my best to be succinct here.

What I am trying to do:

Allow the player to run about a world that is spherical, not a flat object. The intended result is going to be much like navigating a planet in super mario galaxy.

My attempt at solving this problem:

Conceptually it is simple. I create a vector from the player to the origin of the planet/world, and use it as the gravity vector. This works great, I know the direction I need to fall towards.

The problem:

When trying to use unity's transform.forward vector, everything is fine until I move slightly off to the side and attempt to circle the planet. At about the south pole, the forward vector points to random places. I believe this is because of how unity calculates the forward vector, but I don't know how it is done so I can't say.

Should I attempt to calculate it myself? Or should I just store a matrix for my object and do everything manually?

Below are images that hopefully shed some light.

In this image, the forward vector (red ray) points correctly in front. This is at the starting position of the object. The blue ray is the vector toward the planet center transform.forward points correctly in front of the character

After circling to the bottom of the planet, I just need to move slightly to the side, and the forward vector begins to circle about the blue vector enter image description here

EDIT for code:

BaseObject class for objects that will react to planet gravity

public class BaseObject : MonoBehaviour {

    public GameObject planet;
    private float gravityConstant;

    public Vector3 TowardOrigin { get; set; }

    void Awake () {
        /// Store the gravity constant
        gravityConstant = Physics.gravity.magnitude;
    }

    void FixedUpdate()
    {
        /// Manually set the force of gravity to point towards the center of the planet
        TowardOrigin = transform.position - planet.transform.position;
        TowardOrigin.Normalize();
        Physics.gravity = -gravityConstant * TowardOrigin;
        transform.up = TowardOrigin;

        ChildFixedUpdate();
    }

    /// <summary>
    /// To be inherited and overriden by child objects
    /// </summary>
    public virtual void ChildFixedUpdate() { }
}

PlayerController class:

public class PlayerController : BaseObject {
    public float speed;
    Rigidbody body;

    void Start()
    {
        body = GetComponent<Rigidbody>();
    }

    public override void ChildFixedUpdate()
    {
        if (Input.GetAxis("Vertical") != 0 || Input.GetAxis("Horizontal") != 0)
        { 
            Vector3 forwardMomentum = (transform.forward) * (Input.GetAxis("Vertical") * speed);
            Vector3 sideMomentum = (transform.right) * (Input.GetAxis("Horizontal") * speed);

            body.AddForce(forwardMomentum);
            body.AddForce(sideMomentum);
        }
        else
        {
            body.velocity = Vector3.zero;
        }

    }
}    
\$\endgroup\$
3
  • \$\begingroup\$ The images are very helpful, but could you please also include a bit of code? Are you using a character controller whose visible entity has a different transform, perhaps? \$\endgroup\$
    – Jibb Smart
    Commented Apr 20, 2015 at 1:37
  • \$\begingroup\$ I might have to capture a video. It's not a problem with the capsule object failing to rotate with the vector, its just bizarre rotation of the vector itself. At some points it will do 180 degree twirls. Updating the question with some code \$\endgroup\$ Commented Apr 20, 2015 at 1:45
  • \$\begingroup\$ Thanks for the extra info. Writing up an answer that I believe will help :) \$\endgroup\$
    – Jibb Smart
    Commented Apr 20, 2015 at 1:59

1 Answer 1

2
\$\begingroup\$

Your problem is transform.up = TowardOrigin;. I didn't know you could actually set transform.up until now, since it's actually a summary of a more complex state of the object -- its rotation. Setting it tells Unity to orient your object along that axis, but doesn't tell Unity to keep transform.forward as close as possible to what it used to be.

You're probably better off using Transform.LookAt, which doesn't sound intuitive at first, but hear me out. It has an optional up parameter. So in BaseObject.FixedUpdate, in order to do this without having to worry about tricky maths myself, I would first figure out a point in front of the character using its current transform.forward. Then I'd use transform.LookAt to look towards that position, passing it TowardOrigin as the up vector.

So it'd be something like:

var frontPos = transform.position + transform.forward;
transform.LookAt(frontPos, TowardOrigin);
\$\endgroup\$
6
  • \$\begingroup\$ yea I see what you mean. I'm going to try and make some changes and see what happens \$\endgroup\$ Commented Apr 20, 2015 at 2:15
  • \$\begingroup\$ Ah, got it! Your code wasn't quite what I was looking for, but your idea was perfect. \$\endgroup\$ Commented Apr 20, 2015 at 2:42
  • \$\begingroup\$ Oh good! Glad I could be of help :) \$\endgroup\$
    – Jibb Smart
    Commented Apr 20, 2015 at 2:43
  • \$\begingroup\$ Instead of manually setting the up vector as the opposite of the gravity vector, I rotated the object relatively instead, and let unity handle the rest \$\endgroup\$ Commented Apr 20, 2015 at 2:43
  • 2
    \$\begingroup\$ So right below the line Physics.gravity = -gravityConstant*TowardOrigin; I added transform.rotation = Quaternion.FromToRotation(transform.up, TowardOrigin) * transform.rotation; \$\endgroup\$ Commented Apr 20, 2015 at 2:44

You must log in to answer this question.

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