• 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
0
Question by Kyuu · Dec 23, 2014 at 02:43 PM · rotationphysicsquaternionrigidbody2daddforce

2D Rotate towards GameObject acting odd.

Hey, I am having a problem with a 2d (air)plane I am working on. It is a side scrolling game, and I want the plane to follow the player. To move I am using the rigidbody2D and to rotate I am using Quaternion LookAt. The problem is, when the aircraft exceeds the X position of the player, it goes in the opposite direction, so it's moving away from the player instead of towards it. When it gets behind the player again ( X<=player X), it follows the player normally.

 void FixedUpdate () 
 {
         Rotate ();
         Move ();
 }
     
 void Rotate()
 {    
     Quaternion newRotation = Quaternion.LookRotation (player.transform.position - transform.position);
     Quaternion newRot = Quaternion.Slerp(transform.rotation, newRotation, rotateSpeed * Time.deltaTime);
     transform.rotation = new Quaternion(0, 0, newRot.z, newRot.w);
 }
     
 void Move()
 {
     rigidbody2D.AddForce (transform.right * flySpeed*8 * Time.deltaTime, ForceMode2D.Impulse);
     
     if(rigidbody2D.velocity.magnitude > flySpeed)
     {
         rigidbody2D.velocity = rigidbody2D.velocity.normalized * flySpeed;
     }
 }

I want the aircraft to rotate with a rotatespeed, so it won't instantly face the player, and has to make a nice loop when it needs to. Can anyone help me with this? I've looked into multiple solutions, but haven't found anything.

Comment
Add comment · Show 1
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 Scribe · Dec 24, 2014 at 12:37 PM 0
Share

This line scares me: transform.rotation = new Quaternion(0, 0, newRot.z, newRot.w); perhaps you want to convert to an eulerian representation of the rotation before trying to manipulate component-wise, I'd say I'm reasonably competent at Quaternions but I wouldn't be able to work out the expected behaviour of the line.

I will have a go at this is a bit!

2 Replies

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

Answer by nu-assets · Dec 24, 2014 at 02:00 PM

You should perform your calculations in local space not in world space.

You can rewrite your Rotate() method like this:

     void Rotate()
     {
         Vector2 dir = transform.InverseTransformPoint(player.transform.position);
 
         float angle = Vector2.Angle(Vector2.right, dir);
 
         angle = dir.y < 0 ? -angle : angle;
 
         transform.Rotate(Vector3.forward, rotateSpeed * Time.deltaTime * angle);
     }

This way you have a very simple but solid implementation of a follow script.

To prevent the follower to increase its angular velocity with the size of the angle you could do the following:

     const float Threshold = 1e-3f;
     void Rotate()
     {
         Vector2 dir = transform.InverseTransformPoint(player.transform.position);
 
         float angle = Vector2.Angle(Vector2.right, dir);
 
         angle = dir.y < 0 ? -angle : angle;
 
         if (Mathf.Abs(angle) > Threshold)
         {
             transform.Rotate(Vector3.forward, rotateSpeed * Time.deltaTime * Mathf.Sign(angle));
         }
     }

Comment
Add comment · Show 3 · 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 Kyuu · Jan 05, 2015 at 08:39 AM 0
Share

Sorry for the late reply, holidays got me busy. This works like a charm! So thank you very much for this =] The only problem I'm facing right now is that I want to flip the sprite when it goes upside down (so it actually won't fly upside down). This is the method I'm using:

  void CheckForChanges()
         {
             Vector3 standard = new Vector3 (1, 1, 1);
             Vector3 mirrored = new Vector3 (1, -1, 1);
             if (Helper.InBetween(transform.rotation.eulerAngles.z, 90, 270))
             {
                 if(!flipped)
                 {
                     flipped = true;
                     transform.localScale = mirrored;
                     Debug.Log ("Flipped");
                 }
             } 
             else 
             {
                 if(flipped)
                 {
                     flipped = false;
                     transform.localScale = standard;
                     Debug.Log ("Normal");
                 }
             }
         }

I know this isn't the best method to do it in first place, but it works. When combined though, with the script given, it just flips each frame when going straight up, not making the loop.

I appreciate your answer non the less, so thank you very much!

avatar image Kyuu · Jan 05, 2015 at 11:16 AM 0
Share

Never mind that above, I have found the solution =] (Created an additional flipped sprite, and changed it to that instead of changing the scale)

avatar image nu-assets · Jan 05, 2015 at 06:23 PM 0
Share

Glad to hear that it's working now... :)

avatar image
1

Answer by Scribe · Dec 24, 2014 at 01:27 PM

Okay, so this is a little 'hacky' in that I create a new parent so that I can separate the rotation around z and the local rotation around x (to make the plane upright). There would be a way of doing this without the extra parent, but I should be wrapping presents and not spending hours on Quaternion rotations! :D

The following should be attached to the highest object in your plane object hierarchy:

 public float forwardSpeed = 5;
 public float turnSpeed = 90;
 public float twistSpeed = 180;
 public Transform target;
 Vector3 targetPos;
 Transform this_transform;
 Vector3 dir;
 Transform parentT;
 GameObject parentGO;
 
 void Start(){
     this_transform = transform;
     parentGO = new GameObject("rotationControl");
     parentT = parentGO.transform;
     parentT.position = this_transform.position;
     this_transform.parent = parentT;
 }
 
 void FixedUpdate(){
     dir = new Vector3(target.position.x - parentT.position.x, target.position.y - parentT.position.y, 0);
     
     //Look rotation (around Z axis)
     Quaternion q = Quaternion.FromToRotation(Vector3.right, dir);
     parentT.rotation = Quaternion.RotateTowards(parentT.rotation, q, turnSpeed*Time.deltaTime);
     
     //Twist rotation (around local X axis)
     Quaternion q2 = Quaternion.identity;
     if(Mathf.Sign(dir.x) < 0){
         q2 = Quaternion.Euler(180, 0, 0);
     }
     this_transform.localRotation = Quaternion.RotateTowards(this_transform.localRotation, q2, twistSpeed*Time.deltaTime);
     
     //Movement (you can use your forces here, this was mainly for testing)
     parentT.position += parentT.right*Time.deltaTime*forwardSpeed;
 }


This should turn the plane to face the target over time and upright the plane when it becomes upside-down, hopefully without causing any movement in the z axis (you may want to clamp the z value just to be safe)!

Merry Christmas/Happy Holidays!

Scribe

Comment
Add comment · 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

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

The best place to ask and answer questions about development with Unity.

To help users navigate the site we have posted a site navigation guide.

If you are a new user to Unity Answers, check out our FAQ for more information.

Make sure to check out our Knowledge Base for commonly asked Unity questions.

If you are a moderator, see our Moderator Guidelines page.

We are making improvements to UA, see the list of changes.



Follow this Question

Answers Answers and Comments

4 People are following this question.

avatar image avatar image avatar image avatar image

Related Questions

Rotation with rigidbody2d.AddForce() ? 1 Answer

transform.Rotate and Rigidbody.MovePosition 1 Answer

Rigidbody2D and AddForceAtPosition 1 Answer

2D Geometry dash-like ship physics 0 Answers

Problems with rotating an object using angular velocity. 2 Answers

  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges