• Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
Question by Semuta · Oct 10, 2016 at 07:22 PM · rotation axisvectors

Rotate an object until its x axis lies in a plane perpendicular to a given vector...

I want a spaceship to rotate on its forward axis until its right axis is (relatively) perpendicular to a surface normal. I don't, however, want to force the spaceship's up axis to align specifically with the surface normal - e.g. I want the spaceship's up axis to be unrestricted so that it can look up and down to any desired degree. This amounts to keeping the spaceship's right axis within a plane perpendicular to the surface normal.

I have a script that accomplishes this behavior by performing a test rotation on a child empty, checking the before and after dot product of the empty's right vector to see if the test rotation moves the dot product closer to zero, and rotating the ship's forward axis in the appropriate direction. This, however, strikes me as quite messy - I keep wondering if there's a simpler way that dispenses with the additional game object and test rotations. Mathematically, I know that I just need to find the projection of the ship's right axis on a plane through the ship parallel to the surface and rotate the right axis towards that projection, but I'm not sure of how to do this in Unity.

Comment
Bunny83

People who like this

1 Show 2
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image doublemax · Oct 10, 2016 at 07:27 PM 0
Share

I tried to imagine what you're trying to do, but failed.

But maybe this helps: https://docs.unity3d.com/550/Documentation/ScriptReference/Vector3.Project.html

avatar image Semuta doublemax · Oct 10, 2016 at 09:14 PM 0
Share

Unfortunately, Vector3.Project is a scalar projection, meaning that I would first have to have a vector in the plane parallel to the target surface. My problem more or less amounts to finding that plane and a specific vector in it (the projection of the right axis in the direction of the surface normal).

What I'm trying to do is similar to the "auto leveling" in games like Descent. The method I've seen suggested here and other places is using the normal of a RaycastHit to define a target vector and then rotate the ship's up axis to that target vector. This works at leveling out the ship's right axis - but also prevents the ship remaining in a position where it looks up at a 45 degree angle (or 30, 15 etc.) relative to the surface plane. Keeping the up vector aligned with the surface normal forces both the forward and right axis parallel to the surface when I only want to keep the right axis parallel.

1 Reply

· Add your reply
  • Sort: 
avatar image
Best Answer

Answer by Bunny83 · Oct 11, 2016 at 01:21 AM

Well, first to answer your first question on how to project the object's local-x-axis onto the ground place defined by a ground normal. Unity now has the method Vector3.ProjectOnPlane which should do exactly what you asked for. It simply does this:

 public static Vector3 ProjectOnPlane(Vector3 vector, Vector3 planeNormal)
 {
     return vector - Vector3.Project(vector, planeNormal);
 }

So it actually projects your vector onto the normal and then simply subtracts that part from the original vector which will project it onto the place defined by the normal vector.

"Vector3 Project" on the other hand simply does this:

 public static Vector3 Project(Vector3 vector, Vector3 onNormal)
 {
     float num = Vector3.Dot(onNormal, onNormal);
     if (num < Mathf.Epsilon)
     {
         return Vector3.zero;
     }
     return onNormal * Vector3.Dot(vector, onNormal) / num;
 }

Since the normal vector is involved two times as a factor we don't have to normalize the normal vector but can simply divide by the square magnitude (num).

Finally another way is to simply use the Cross product between your normal vector and your forward vector. This gives you the "right" vector that you're after as well. Unity uses a left-handed-system so the left-hand-rule applies:

 Vector3 right = Vector3.Cross(normal, transform.forward).normalized;

Of course when you pull up over 90° right will flip to the other side as you officially are now upside down.

To get the relative rotation you might use Quaternion.FromToRotation. However i guess you want to apply a torque instead of rotating it manually, right?. So all you have to determine is:

  • how far you have to rotate (the remaining angle) so you can adjust the rotation speed

  • Which direction you have to rotate.

To get the remaining angle you can simply use Vector3.Angle with your object's right vector and the calculated target right vector.

To determine the direction you have to rotate, simply use the Dot product between the surface normal and your right vector. If the result is positive you have to rotate clockwise (as seen from behind looking along forward) and if it's negative you have to rotate counter-clockwise.

Note: you might disable the alignment if you almost go straight up or down (+-70°) to avoid constant realignment by 180° each time you cross the zenith.

Comment
Semuta

People who like this

1 Show 1 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Semuta · Oct 11, 2016 at 04:32 AM 0
Share

Thanks for the detailed answer. I'm curious if you could help me along just a little bit further - I'm actually more interested in a kinematic approach due to zero g collisions with pillars/wall edges/door frames being too disorienting and uncontrollable (even with tweaking angular drag and mass). Right now, I have a setup similar to what you described using planes to determine rotation direction and integrate that with mouse movements (I'll probably switch to using the dot product as you describe, but this is what I have on hand now):

     void MouseMove(float x_rot, float y_rot){
         //x and y values taken from mouse input;
         RaycastHit align; 
         var qRot = transform.rotation;
         qRot = Quaternion.AngleAxis (x_rot, transform.up) * qRot;
         qRot = Quaternion.AngleAxis (y_rot, transform.right) * qRot;
         if (Physics.Raycast (transform.position, transform.up * -1f, out align, 10f)) {
             //Check for surface beneath player, get right x axis projection on plane parallel to surface;
             Vector3 target = Vector3.ProjectOnPlane (transform.right, align.normal);
             //Check angle between right axis and projection; if greater than 1 degree determine direction of rotation
             if (Vector3.Angle (target, transform.right) > 1f){
                 //construct reference plane, check if right axis is above or below plane 
                 refPlane.Set3Points (transform.position, target, transform.right);
                 if (refPlane.GetSide (Vector3.Cross (target, transform.right)))
                     z_rot = -1f;
                 else
                     z_rot = 1f;
             } else
                 z_rot = 0f; 
         } else
             z_rot = 0f; 
         qRot = Quaternion.AngleAxis (z_rot, transform.forward) * qRot; 
         transform.rotation = qRot; 

Just for the sake of completeness, potentially simpler/more efficient code, and my own understanding, I'm curious how and if I could use a quaternion rotation to gradually rotate the right axis towards its projection. I'm curious if this could eliminate checking for what direction I want to rotate and allow me to side-step adding an offset/damping factor to keep the ship from jittering.

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Welcome to Unity Answers

If you’re new to Unity Answers, please check our User Guide to help you navigate through our website and refer to our FAQ for more information.

Before posting, make sure to check out our Knowledge Base for commonly asked Unity questions.

Check our Moderator Guidelines if you’re a new moderator and want to work together in an effort to improve Unity Answers and support our users.

Follow this Question

Answers Answers and Comments

6 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

Rotate one vector towards another but keeping it on the same plane 1 Answer

Trajectory issues, projectile not behaving as expected 1 Answer

What is the best way to implement vector graphics? 3 Answers

Changing the forward rotation for LookAt 2 Answers

How can I get the distance from the center of an object to the mouse position? 0 Answers


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges