- Home /
3D rotation in 3 components
I think this ought to be relatively simple but I just cant get my head around it. I want to control the rotation of my spaceship object by using rigidbody.AddRelativeTorque to accelerate/decelerate the rotation in an attempt to reach a desired facing. The idea being to give different ships different manouverability by specifying 3 maximum torque levels relative to the local axes (i.e. pitch, roll and yaw). I don't want to set the rotation directly so that I can have the ship knocked about by collisions using the physics system.
I'm having trouble figuring out the amount of torque to apply at any given frame to keep the ship moving towards the desired rotation. My current thinking is along the lines of;
Vector3 IdealForward;
Vector3 IdealForwardOnYawPlane = (IdealForward - Vector3.Project(IdealForward, transform.up)).normalized;
float AngleA = Mathf.Acos(Vector3.Dot(IdealForward,IdealForwardOnYawPlane);
Vector3 IdealForwardOnPitchPlane = (IdealForward - Vector3.Project(IdealForward, transform.right)).normalized;
float AngleB = Mathf.Acos(Vector3.Dot(IdealForward,IdealForwardOnPitchPlane);
...should give me AngleA and AngleB as the yaw and pitch required to reach the IdealForward. however I dont have the current angularvelocity in terms of yaw pitch and roll so I cant figure out when to start decelerating so I don't overshoot. I'm not even convinced I'm approaching the problem from the right way.
Surely this cant be an unusual problem? It seems like it should be pretty fundamental to any 3D game. Can anyone point me at a sample code or a project that handles the same sort of 3D manouvering? Or have I unwittingly hit some classic unsolvable problem that anyone with more experience than me would have never started?
Any help is appreciated. I'm still at a very early stage in getting my project up and running and my brain is too overloaded to cope.
Answer by Styxxx · Jul 20, 2014 at 02:16 PM
Just came across my own old question and figured I'd post the solution I eventually came up with. The trick is to realise that the final answer is the sum of three other torque Vectors
Start with the torque required to stop the current rotation instantly. That's component 1. Calculate how long it would take your ship to come to rest if we used a MaxTorque value which would be a variable that dictates the ships manouverability. Calculate what the ships facing and up vectors would be when the ship stopped. Then calculate the torque required to get from that Facing and Up to the targetfacing and targetup. Those are your components 2 and 3 Add them all together Limit the overall magnitude to the ships MaxTorque and apply it to the rigidbody.
I'm not sure I'd say it works perfectly, but it does the job. Hope its of use to someone.
public Vector3 TargetForward;
public Vector3 TargetUp;
public float MaxTorque = 1;
void FixedUpdate()
{
Vector3 BrakingTorque = -1 * rigidbody.angularVelocity;
float SecondsToComeToRestAtMaxBraking = rigidbody.angularVelocity.magnitude / MaxTorque;
Vector3 RotationWhileBraking = (rigidbody.angularVelocity / 2) * SecondsToComeToRestAtMaxBraking * Mathf.Rad2Deg;
Vector3 FacingWhenAtRest = Quaternion.Euler(RotationWhileBraking) * transform.forward;
Vector3 UpWhenAtRest = Quaternion.Euler(RotationWhileBraking) * transform.up;
Vector3 OtherTorqueVector = Vector3.Cross(FacingWhenAtRest, TargetForward);
float AngleBetweenRestAndTarget = Vector3.Angle(FacingWhenAtRest, TargetForward);
Vector3 TorqueToTarget = OtherTorqueVector * AngleBetweenRestAndTarget * Mathf.Deg2Rad;
Vector3 OtherTorqueVector2 = Vector3.Cross(UpWhenAtRest, TargetUp);
float AngleBetweenRestAndTarget2 = Vector3.Angle(UpWhenAtRest, TargetUp);
Vector3 TorqueToTarget2 = OtherTorqueVector2 * AngleBetweenRestAndTarget2 * Mathf.Deg2Rad;
//TODO: limit combined torque by axis rather than as overall value
Vector3 CombinedTorque = BrakingTorque + TorqueToTarget + TorqueToTarget2;
if (CombinedTorque.magnitude > MaxTorque) CombinedTorque = CombinedTorque.normalized * MaxTorque;
rigidbody.AddTorque(CombinedTorque * Mathf.Rad2Deg * Time.deltaTime, ForceMode.Acceleration);
}
great script!!! Thanks! btw, did you figure out how to compensate the torque so that object does not rotate over the Target direction?
I think that should be covered by the BrakingTorque component as long as you keep re-calculating. What I mean is that if you were to re-do this calculation every 0.1 seconds say, you would be constantly updating the torque to apply and as you got closer to the target vectors your TorqueToTarget component would get smaller and smaller and the BrakingTorque would take over and stop the rotation.
That's what I intended anyway. tbh I shelved this project a while back and I don't actually know if it works in practice.
Oh, nice, I did not expect you are still reading Answers :) I want to congratulate you - it works quite nicely, considering the fact that I accidentally found it after a month of reading the Answers :) Btw, send it to UnityWiki so it is not lost here!
I found some sort of LookAt script that uses AddTorque on UnityWiki, but it does not have Up vector correction as yours.
Now, some comments and questions, if you don't $$anonymous$$d: Your script works quite nicely, but sometimes, while moving around the object with your script, it looses orientation and starts to intensively rotate around Z (it seems like it is Z) axis for some time until, i guess, some 'value' happens in calculations and it catches proper rotation again (don't know how to explain in better words). Besides, if Up vectors of 'this' and 'target' differ, then during strafe movements in front of 'this' object with your script attached, rotation around Z accumulates and after some while 'this' flips up side down - maybe this Z accumulation causes that strange 'lost in rotation'? Any ideas why this strange rotation happen and how to fight it, from the perspective of your today's knowledge?
The length of cross product heavily affects the rotation speed. Normalization helps, but then all the algorithm gets broken. Next, when two objects look opposite direction (like have angle 180 degree) cross gives zero value vector which means our object will never turn at target as long as angle is 180. It could be dealt with by forcing random rotation along local Up vector, but then - how to make cross vector be more than 0 in such case?
The downside of such generic algo is that its hard to make it behave (accelerate/decelerate) like I can do with manual control of the same object.
What I was asking is if you have found the way to make object not overshoot the target orientation at all?
Also, the closer it rotates to target vector the slower the rotation is and it can take 10s of $$anonymous$$utes to reach target vector which is not really nice. Because all this affects situations when you need to shoot moving target.
It would be so much nice to talk to you about these topics as it seems I'm trying to work on the same problems as you did in the past.
Answer by Em3rgency · Jun 23, 2013 at 06:41 PM
Either I completely misunderstand what you're doing, or you're over-thinking it. Isn't this basically what you want? http://unity3d.com/learn/tutorials/modules/beginner/physics/addtorque (Skip to 1:08 for the visual demonstration)
No, that's adding torque based on user input that deter$$anonymous$$es the amount of torque. I need to figure out how much torque to add at any given moment to get from the current rotation to a desired rotation. Preferably in the form of relative torque around the objects local axes so I can limit them individually. It's part physics, part AI. The actual code for adding torque to a rigid body is the only bit I have that I know works!
Riiight. Well, I can only tell you that you will always need to start decelerating when you reach the halfway point, presu$$anonymous$$g acceleration and deceleration speeds are the same.
So the way I see it you can either find out where that midway is, or when it is.
Like you said, finding the when would require angular velocity, a heap ton of other information and some university level calculus.
Finding the where, on the other hand, would only require you to store the start rotation, current rotation, desired rotation and the use of high-school trigonometry. Once you plot out the directions torque needs to be applied in, you keep doing it, until you reach the midway between where you started and where you want to be. Then just reverse torque.
Heh. Unfortunately that also presupposes a lot of things remaining constant when they probably wont. Imagine that the objective is to pursue another ship by constantly turning towards it. The target facing would change frame by frame meaning that there is no "halfway point". Beginning to think I should come up with a plan B
That would make it more complex, true. You would need to re-evaluate the target point on updates and present angular velocity, which as you already said, you don't know...
This is not some age old problem, just a complicated as hell physics formula.
Your answer
Follow this Question
Related Questions
The name 'Joystick' does not denote a valid type ('not found') 2 Answers
Why does children position offset on parent collision? 1 Answer
How to generate a random trajectory when the ball is kicked ? 1 Answer
How can I make a flexible object? (rod or cylinder type) 0 Answers
[3D] Character stuck between cubes. 4 Answers