- Home /
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.
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!
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));
}
}
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!
Never $$anonymous$$d that above, I have found the solution =] (Created an additional flipped sprite, and changed it to that ins$$anonymous$$d of changing the scale)
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
Your answer
Follow this Question
Related Questions
i have rotated an gameobject but the axis of the object did not change 2 Answers
transform.Rotate and Rigidbody.MovePosition 1 Answer
move 2d character affected by physics with velocity and/or add force 2 Answers
Problems with rotating an object using angular velocity. 2 Answers
Rotate object towards another object without using the local Y axis 0 Answers