Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 12 Next capture
2021 2022 2023
1 capture
12 Jun 22 - 12 Jun 22
sparklines
Close Help
  • 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 /
  • Help Room /
avatar image
1
Question by globalenemy · Oct 26, 2015 at 06:39 PM · quaternionrotatetowards

how to do Quaternion.RotateTowards on single LOCAL axis?

http://i.imgur.com/tzn4FGM.png

I've found several solutions for achieving this goal when working from global axises (I hope that's spelled right). But have a look at my picture and you'll see that this wont work for me :\

I want that red character rotate towards the blue one back there. But I don't want him to fall over or do other wiered stuff because of my FauxGravity to the sphere. (the FauxGravity script I use forces every linked body to rotate so it stands on the surface corectly)

EDIT: I visualized what i want to happen in a small clip: http://gfycat.com/SingleSparseBarracuda

untitled2.png (83.9 kB)
Comment
Add comment · Show 3
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 Socapex · Oct 26, 2015 at 07:17 PM 0
Share

I should downvote for using my avatar... lol just kidding :P

Have you looked into http://docs.unity3d.com/ScriptReference/Vector3.RotateTowards.html ? You can try their example, I don't remember if it will rotate every axis (probably). If it rotates everything, I would try using the example, but then extracting only the axis you want. $$anonymous$$aybe?

Hope this helps, at least put you on the right track :)

avatar image globalenemy Socapex · Oct 26, 2015 at 07:27 PM 0
Share

hmmm... SANDWICH!!

I had a look at that allready, but I'll have another one just in case.

avatar image Socapex globalenemy · Oct 26, 2015 at 07:40 PM 0
Share

I have a feeling you want to save the 2 other axes, and then set those back after the looktowards... It's an insteresting problem for sure, be sure to post your solution when you find it :)

But I love TACOS! I need them or I will EXPLODE ^_^

1 Reply

· Add your reply
  • Sort: 
avatar image
1
Best Answer

Answer by Statement · Oct 26, 2015 at 10:22 PM

EDIT: Fixed some issues and reduced the math a bit.

There may be other ways to solve it but my math is a bit rusty. However, one approach would be something like this. Given the two dudes A and B (red and blue. image 1), we can make a vector C (green, image 2) which is A - B.

We can project C onto the plane of B to get a point D (pink, image 4).

This D point is in world space. It would be nice if it was in local space to B where rotation can be performed along Y axis, so let's convert it (square, image 5). Now we can ignore Y and work with X and Z. From X, Z we can use simple trig like Mathf.Atan2 to get an angle. Now we have a single angle that defines Y rotation, so all we need to do is keep moving an angle toward the destination angle.

From this angle, we can make a quaternion that is a Y rotation.

Then we can apply the Y rotation to your existing rotation, to preserve your plants Up direction.

This all sounds crazy and stuff with math and ideas, so how do you do this in code?

 Vector3 LookatXZ()
 {
     Vector3 distance = lookat.position - transform.position;
     Vector3 direction = Vector3.ProjectOnPlane(distance, transform.up).normalized;
     return transform.InverseTransformVector(direction);
 }

LookatXZ will perform the steps to get the direction in local space as described above. We can ignore the Y component and focus on X,Z, which will be the direction toward the target on our plane.

 private static float AngleXZ(Vector3 direction)
 {
     return Mathf.Atan2(direction.x, direction.z) * Mathf.Rad2Deg;
 }

We can then take that direction and use Atan2 to get radians that describe our rotation. But quaterion methods expect degrees, not radians, so I convert the radians to degrees with Rad2Deg. What we get back now is an angle 0-360 degrees, which is the Y rotation that should be applied.

If we want to look toward a target gradually, we can use Mathf.MoveTowardsAngle which will take care of wrapping negative values for us (-180 is the same as 180 etc). Finally, we can create the quaternion based on the Y axis.

 void LateUpdate()
 {
     if (!lookat)
         return;
 
     float forwardAngle = AngleXZ(ForwardXZ());
     float lookatAngle = AngleXZ(LookatXZ());
     float speed = degreesPerSecond * Time.deltaTime;
     float angle = Mathf.MoveTowardsAngle(forwardAngle, lookatAngle, speed);
     transform.rotation *= Quaternion.AngleAxis(angle, Vector3.up);
 }

I've got an example project (based on your previous question, but I disabled the line renderer) so you can test it out. Code file is linked too if you just want to set it up on your end. Note that rotation is not interactive in scene view in the example project. You have to press play and then drag the "Drag Me" around in the scene view. Both of them should be trying to look at each other.

Dragging is a little buggy so I advise you to change from local transform handles to world transform handles (toggle with X key)

EDIT: Updated links to point to new version with fixes.

Example project
SphericalLookTowards.cs

Comment
Add comment · Show 14 · 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 Statement · Oct 26, 2015 at 10:33 PM 0
Share

If you later want to move your characters on the sphere, look at this existing answer if you don't have ideas.

avatar image globalenemy · Oct 26, 2015 at 10:50 PM 0
Share

jesus, your answers are bonkers! Are you getting payed for this? you really should be! :D

you will have a place in the credits of my project 100% (if you don't $$anonymous$$d that)

and thank you very much for explaining the stuff so well. $$anonymous$$y understanding of math is waaay worse than yours seems to be and I understood this all so well that I think to know that this will work just by reading.

avatar image Statement globalenemy · Oct 26, 2015 at 11:00 PM 1
Share

No, I don't get paid for this. I just like to elaborate on answers. I think we have enough of "here are the docs, RTF$$anonymous$$" kind of answers. Yes, I agree that feeding people solutions might turn them into zombie programmers but I am personally that kind of guy who appreciate some snippets, learn from it, improve on it and adapt. Also, it refreshes my $$anonymous$$d on math and problem solving. Though, don't expect to get these kinds of detailed answers every time and from every one. It's just my pastime. I'm rubbish at ideas so I try to do what other people dream up.

avatar image globalenemy Statement · Oct 26, 2015 at 11:18 PM 0
Share

yea, I think I can understand that. This project infact wasn't really my own idea. I came up with it after I saw a similar project at a friends place. Your answers certainly have been a great learning experience so far. I hope I can live up to this example one day.

Show more comments
avatar image Statement globalenemy · Oct 26, 2015 at 11:04 PM 0
Share

Eh, you can just do a "Thanks to the Unity Community" thing.

avatar image globalenemy · Oct 27, 2015 at 02:22 AM 0
Share

okay I'm done implementing and it is working. But,..

Have a look at this: gfycat.com/Specific$$anonymous$$ean$$anonymous$$itten

I tried fixing it by adding an if statement to stop the rotation. But the character doesn't stop at the right angle because of its idk, 'swing'?

 if (transform.rotation == transform.rotation * yRotation) {
     actionNr = -1;
 }
 transform.rotation *= yRotation;

I need to know when I can stop excecuting the whole thing, because it takes a good amount of performance and I want the character move towards the target after rotating (The moving part is allready figured out (I think).)

avatar image globalenemy globalenemy · Oct 27, 2015 at 01:50 PM 0
Share

just had a look at your example project. it doesn't happen there. It's because I have a rigidbody component on my characters. I need that to make my FauxGravity script work.

EDIT: I guess, I could get rid of that. I will try some things, and come back.

EDIT: I managed to get rid of the rigidbody but then my colliders (obviously) stopped working. I need those colliders to create those spherical lines when the characters meet each other. Found several questions adressing this but everybody just tell that I'll need atleast one character having a rigidbody to make the colliders work. But both characters will be moving later and changing the rigidbody behaviour to kinematic doesn't prevent the spinning and drag would mess with the rotation speed.

Next, I updated that if statement by setting the angularVelocity to zero but that only faced me to the fact that this if statement isn't working at all. the character just makes one bit of the whole turn and then stops, way before being at the target rotation. (I guess I didnt understand your code as well as I thought.

avatar image Statement globalenemy · Oct 27, 2015 at 02:28 PM 0
Share

The swinging motion happen because you stop updating the rotation from your other script I think. It shouldn't happen just because you have a rigidbody.

You can stop updating the rotation after currentAngle == angle.

It's a fragile solution that depends upon the other script setting the rotation every frame. Otherwise rotation will start accumulating and you get that pendulum effect.

You could maybe solve this with something like this: transform.rotation = fauxthingey.theRotationILastSet * yRotation; Your fauxthingey was setting the rotation, right? So it knew how to create the rotation. But you stop updating the transforms rotation I guess. Then use the rotation you used in the other script as a base rotation for your yRotation. See if that works.

avatar image globalenemy Statement · Oct 27, 2015 at 03:36 PM 0
Share

I'm not sure I understand. However my FauxGravity script keeps updating the rotation.

On the character:

 [RequireComponent(typeof(Rigidbody))] //rigidbody approach
 public class FauxGravityBody : $$anonymous$$onoBehaviour
 {
     private Rigidbody rb; //rigidbody approach
 
     public FauxGravityAttractor attractor;
     private Transform myTransform;
     
     void Start()
     {
         rb = GetComponent<Rigidbody>(); //rigidbody approach
 
         rb.useGravity = false; //rigidbody approach
         rb.constraints = RigidbodyConstraints.FreezeRotation; //rigidbody approach
 
         myTransform = transform;
     }
 
     void FixedUpdate()
     {
         if (attractor)
         {
             attractor.Attract(myTransform);
         }
     }
 
 }

On the sphere:

 public class FauxGravityAttractor : $$anonymous$$onoBehaviour
 {
     public float gravity = -12;
     //snapTransforms approach
     //public bool snapBodys = true;
     //public float radius = 1f;
     //
     //void Awake() {
     //    radius = transform.localScale.x / 2;
     //}
 
     public void Attract(Transform body)
     {
         Vector3 gravityUp = (body.position - transform.position).normalized;
         Vector3 localUp = body.up;
 
         //rigidody approach
         body.GetComponent<Rigidbody>().AddForce(gravityUp * gravity);
 
         //snapTransforms approach
         //if (snapBodys)
         //{
         //    Vector3 localTo = (body.position - transform.position).normalized * radius;
         //    body.position = localTo;
         //}
 
         Quaternion targetRotation = Quaternion.FromToRotation(localUp, gravityUp) * body.rotation;
         body.rotation = Quaternion.Slerp(body.rotation, targetRotation, 50f * Time.deltaTime);
     }
 
 }
avatar image Statement globalenemy · Oct 27, 2015 at 05:57 PM 1
Share

I found a math error. I've updated the links and edited the answer. I also added some Gizmos so you can visualize the vectors involved. You can remove all the gizmo functions if you don't need the visualization when you select the object. It doesn't affect the functionality of the rotations.

avatar image globalenemy Statement · Oct 28, 2015 at 01:56 AM 0
Share

This is working great! now I still need to find a way to break out of that loop. have been trying around with that allready but.. (the Debug message never appears)

 if (actionStep != 0)
 {
     rb.velocity = transform.forward * moveSpeed;
 }
 else
 {
     // SphericalLookTowards Unity-Answers-Statement-27-10-2015
     float forwardAngle = AngleXZ(ForwardXZ());
     float lookatAngle = AngleXZ(LookatXZ(tempCollider.transform));
     float speed = moveSpeed * Time.deltaTime;
     float angle = $$anonymous$$athf.$$anonymous$$oveTowardsAngle(forwardAngle, lookatAngle, speed);
     if (forwardAngle < lookatAngle)
     {
         transform.rotation *= Quaternion.AngleAxis(angle, Vector3.up);
         if (forwardAngle >= lookatAngle)
         {
             actionStep = 1;
         }
     }
     else
     {
         transform.rotation *= Quaternion.AngleAxis(angle, Vector3.up);
         if (forwardAngle <= lookatAngle)
         {
             Debug.Log("doing Action 1, Step 2..");
             actionStep = 1;
         }
     }
                         
 }

Debugging the whole thing confuses me. what am I doing wrong?

 Frame: 563 # forwardAngle = 0
 Frame: 563 # lookatAngle = -0.6545103
 
 Frame: 567 # forwardAngle = 0
 Frame: 567 # lookatAngle = -0.05968561
 
 Frame: 570 # forwardAngle = 0
 Frame: 570 # lookatAngle = 8.537736E-06
 
 Frame: 574 # forwardAngle = 0
 Frame: 574 # lookatAngle = -3.415095E-06
 
 Frame: 577 # forwardAngle = 0
 Frame: 577 # lookatAngle = -3.415095E-06....................

EDIT : Fixed! I simply had to add a script excecution order. If 'FauxGravityBody' is set to run after 'SphericalLookTowards' my if statement works, and the character starts moving.

Is it wise to to it that way? there are numbers I can change in the script excecution order and I dont really know what that means.

EDIT: Actually, it doesn't fix it. it just sometimes work and sometimes doesn't :\ woot!?

also tried enabling and disabling the FauxGravityBody rotation during the process of SphericalLookTowards, no luck. still sometimes happens, sometimes not.

Show more comments
avatar image globalenemy · Oct 28, 2015 at 04:31 PM 0
Share

Done! it's kinda dirty, but it works. And it keeps working. your last comment wasn't the true fix (it was working more often but still stopped working sometimes) but it brought me on the right way.

 float forwardAngle = AngleXZ(ForwardXZ());
 float lookatAngle = AngleXZ(LookatXZ(tempCollider.transform));
 float speed = rotateSpeed * Time.deltaTime;
 float angle = $$anonymous$$athf.$$anonymous$$oveTowardsAngle(forwardAngle, lookatAngle, speed);
 
 float lastAngleRound = (float)System.$$anonymous$$ath.Round(lastAngle, 5);
 float nextlastAngleRound = (float)System.$$anonymous$$ath.Round(nextlastAngle, 5);
 float lookatAngleRound = (float)System.$$anonymous$$ath.Round(lookatAngle, 5);
 
 if (nextlastAngleRound != lookatAngleRound) {
     if (lastAngleRound != lookatAngleRound) {
         transform.rotation *= Quaternion.AngleAxis(angle, Vector3.up);
         nextlastAngle = lastAngle;
         lastAngle = lookatAngle;
     }
     else {
         lastAngle = 361f;
         nextlastAngle = 361f;
         actionStep = 1;
     }
 }
 else {
     lastAngle = 361f;
     nextlastAngle = 361f;
     actionStep = 1;
 }

angle and lookAtAngle will sometimes never be the same number. And none of them will allways be 0 when at end rotation. sometimes they will also switch around from one value to another forever and ever.

Gotta think now how I can make this all clean (and maybe a bit faster too). But I think we're done here for now.

Thank you so much Statement!

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.

Follow this Question

Answers Answers and Comments

34 People are following this question.

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

Related Questions

transform.localRotation = Quaternion.RotateTowards() or Quaternion.Euler fails on vertical axis 1 Answer

transform rotations overwriting eachother 1 Answer

My Quaternion.RotateTowards is instant 1 Answer

Quaternion.RotateTowards stopping precision. 0 Answers

Quaternion.RotateTowards wierd issues 1 Answer


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