Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 13 Next capture
2021 2022 2023
1 capture
13 Jun 22 - 13 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 /
avatar image
4
Question by Zachary.C.Stewart · Apr 06, 2012 at 04:14 AM · rotationphysicsrigidbody

Rotate using physics

I've seen a question like this posted several times, but never answered properly.

I want to rotate an object to a specific rotation using the physics engine. All of the answers I've seen said to just slerp the angle, but I don't want to do that because if the object collides with another and acquires rotational velocity, it should be thrown off by that rotation. Slerping the angle would prevent any of those collision physics from affecting anything.

Its a spaceship game, and I want the objects to have to apply acceleration and deceleration forces, and have to fight rotation gained from collisions to reach specified angles, so using slerp is unacceptable. If all you have to say is "don't use physics, use slerp," please don't bother

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

6 Replies

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

Answer by aldonaletto · Apr 06, 2012 at 01:55 PM

Physics is not intended to allow precise positioning. To rotate a rigidbody with torque, you should write a PID controller, but feedback about the current rigidbody orientation is very unreliable (eulerAngles may return weird values, and changes from 0-360 and 360-0 when near to the origin). Take a look at this question, where I proposed an alternative physics control.

EDITED: The general idea behind a PID controller is explained in this pseudo code:

 var pGain: float; // adjust the individual gains
 var iGain: float; // iGain better to be 0 in this case
 var dGain: float;
 private var lastPError: float = 0;
 
 function PidLoop(){
   var pError = desiredAngle - currentAngle; // calculate the proportional error...
   var iError += pError * Time.deltaTime; // the integral error...
   var dError = (pError - lastPError)/ Time.deltaTime; // and the differential error
   lastPError = pError;
   // the torque is the sum of all errors weighted by their gains:
   torque = pGain*pError+iGain*iError+dGain*dError;
   // apply the torque to the object
   ApplyTorque(torque);
 }

The function PidLoop must be called at a constant rate, like in Unity's FixedUpdate. currentAngle is the current object angle about the axis of interest (Y, for instance), and desiredAngle is the angle you want to reach. pError is the proportional error: it's just the difference between what you want and the present position. iError is the integral error, a numeric integration of the proportional error, and dError is the differential error, a numeric differentiation of pError. The 3 errors are summed with individual weights, and the resulting value is applied as torque about the same axis currentAngle is measured.
If only pError were used, the final position would never be reached because the force applied is proportional to the error - thus zero error means zero force. To cancel this error while still mantaining some control force, the iError is used: any small pError accumulates in iError over time, creating a force that will eventualy cancel any opposite forces. If there are no opposite forces (as usually happens in rotational PID), the corresponding iGain must be set to zero, or the system will become very unstable. The differential error, dError, is very important in rotational PID, because it becomes negative when the desired angle is getting closer, acting as a brake and slowing down the rotation.
In Unity, the big problem is how to get a reliable feedback signal - the controller gets crazy when around 0, or if the other axes are non-zero. Maybe in your 2D engine this error is easier to work around - at least there are no other axes to worry about. Try to adapt an algorithm like this to your case.

Comment
Add comment · Show 5 · 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 Kleptomaniac · Apr 06, 2012 at 02:19 PM 1
Share

You, @Duck and @Eric5h5 ... geniuses :D

avatar image Zachary.C.Stewart · Apr 06, 2012 at 07:44 PM 0
Share

Alright, well to be honest, I was hoping for something that would also work outside of unity... I have a 2d game engine where you would add rotational forces the same way as in unity, and was hoping for a solution that would be applicable for projects in there as well as in my 3d game.

avatar image iceydee · Dec 02, 2012 at 09:23 PM 0
Share

Wow, this is brilliant. Thanks!

avatar image TonicMind · Dec 19, 2015 at 08:15 PM 0
Share

Oh my God. PLEASE put your code in code tags its not hard! You're making this 10x harder than it should be for someone looking for help. I seriously thought this was the one answer I needed. Now I have to sort this crap out.

Seriously if you can't just use code tags then how am I able to trust that your code is actually reliable? I don't believe using the physics engine produces "weird" values for eulerAngles when used. Perhaps you just don't have a frickin' clue?

God I feel like this is a troll answer. >:(

EDIT: Also Idk how a physics engine can have "weird" or "unreliable" output. Physics, at least the conventional kind (as opposed to the quantum kind) is NOT unreliable or unpredictable. Its quite predictable that's what physics (and science) is all about. $$anonymous$$ystery solved. This guy doesn't have a frickin' clue.

avatar image wpetillo · Dec 04, 2021 at 12:39 AM 0
Share

I would be very interested in seeing this pseudo code converted into tested/working code. I have tried implementing it here https://github.com/Will9371/Playcraft/tree/master/Assets/Playcraft/Quality%20of%20Life/Physics/PID by adapting the PID controller from your other post (I've also converted it to C#, extended to 3 axis rotation, combined it with PID movement, and have it following a target transform), but it moves erratically. The other post's code works OK, but doesn't provide physics-based control since it ultimately applies rotation directly.

avatar image
2

Answer by Chybis · Oct 27, 2013 at 10:18 PM

Ok, made it work without PID controller, but both @aldonaletto posts were very helpful. PID is most useful when you don't know how system reacts to input. But as we actually know how system reacts and it is pretty linear, it is possible to fit control system to Unity's physics engine.

Idea behind solution is following. We know how system reacts to acceleration and deceleration, so in each moment we can tell when objects stops spinning with current angular velocity. So in each update we determine angle remaining, current angular velocity and using this data, we decide whether to accelerate or decelerate. Do it separately for both axes and you have pretty precise controller. There are few things to note. First, Unity returns angles in degrees/s, but angular velocity in radians/s. Second, you need to consider current angular velocity when deciding to rotate right or left, for example. It may be faster to continue rotating instead of trying to stop and reverse rotation. The code uses speed bonus/penalty, first considering how would it be when idle, then adding time cost for reaching idle state, or substracting time cost needed to accelerate to current speed (both are same). And third, systems gets unstable if rotating too fast. If system can't stop rotation in 1/2 of spin, it will try to slow down, overshoots neutral zone and starts accelerating again. So you need to implement slowdown code (ommited as it is rather simple). Mine decelerates if speed is too high. Too high speed is computed into _maxAngularVelocity variable. Better to slowdown both axes, or at least don't accelerate the other. Also you way want to counter rotation around forward axis.

So far it works well even with angular damping of 0.

 public float angularForce = 1.0f; // this is how strong are your rotation thrusters
 void Start () {
     _acceleration = anguarForce / rigidbody.mass;
     // compute max angular velocity so it can be decelerated in 1/2 rotation (PI angle)
     _maxAngularVelocity = Mathf.Sqrt(_acceleration * Mathf.PI);
 }
 
 void FixedUpdate() {
     Vector3 targetDirection = currentTarget - transform.position;
     // get direction to target in local coordinates
     Vector3 localDirection = transform.InverseTransformDirection(targetDirection);        
     Vector3 angularVelocity = transform.InverseTransformDirection(rigidbody.angularVelocity);
 
     float upAxisSpeed = angularVelocity.y;
     float rightAxisSpeed = angularVelocity.x;
 
     // SLOWDOWN CODE HERE
 
     Vector2 upAxisVector = new Vector2(localDirection.x, localDirection.z).normalized; // vector in X-Z plane
     float upAxisAngle = Vector2.Angle(Vector2.up, upAxisVector) * Mathf.Deg2Rad;    // in degrees, convert to RADS!
     float upAxisStaticSign = Mathf.Sign(upAxisVector.x);
 
     // Same for Right axis = X
 
     forceUp = angularForce * GetAxisForce(upAxisAngle, upAxisSpeed, upAxisStaticSign);
     forceRight = angularForce * GetAxisForce(rightAxisAngle, rightAxisSpeed, rightAxisStaticSign);
 
     // apply forces
     rigidbody.AddRelativeTorque(forceUp * Vector3.up);
     rigidbody.AddRelativeTorque(forceRight * Vector3.right);
 }
 
 // this return 0, -1 or 1, meaning none or max force in either direction
 float GetAxisForce(float axisAngle, float axisSpeed, float directionSign, string axisLabel) {
     // this is direction of current rotation, helps to choose direction to reach target
     float dynamicCorrectionSign = Mathf.Sign(axisSpeed * directionSign);
     // this is how long will reach to stop from current velocity OR to accelerate from idle to current velocity
     float timeToStop = Mathf.Abs(axisSpeed / _acceleration);
     // how long would take to reach target in shorter direction if angular velocity is 0
     float staticToReach = Mathf.Sqrt(4 * axisAngle / _acceleration);
     float staticToReach2 = Mathf.Sqrt(4 * (2*Mathf.PI - axisAngle) / _acceleration); // same for longer direction
     // adjust these durations by current speed (this is basically integral part of velocity)
     float shorterTimeToReach = staticToReach - dynamicCorrectionSign * timeToStop;
     float longerTimeToReach = staticToReach2 + dynamicCorrectionSign * timeToStop;
         
     // choose direction, considering current velocity
     float force;
     float timeToReach;
     if (shorterTimeToReach <= longerTimeToReach) {
         force = directionSign;
         timeToReach = shorterTimeToReach;
     } else {
         force = -directionSign;
         timeToReach = longerTimeToReach;
     }
 
     // distance is small enough, consider target rotation reached (this pat could use more love)
     if (Mathf.Abs(timeToReach) < 0.1f) {
         // idle
         force = 0;
     } else if (timeToStop >= timeToReach) {
         // moving too fast, slow down
         force = -force;
     }
 
     return force;
 }
Comment
Add comment · Show 2 · 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 brenosilver · Jan 07, 2015 at 10:23 AM 0
Share

Could you please post the complete code for this? Your incomplete code makes me have more questions than answers.

I have a working controller that is intended for mouse axis lock. However, I am trying to achieve a double click in space to turn, just like here

What I have so far:

 float roll = directionTurn.x - rigidbody.transform.up.x;
 float pitch = directionTurn.y - rigidbody.transform.right.y;
 
 rigidbody.AddRelativeTorque(Vector3.up * roll * rollConstant * Time.deltaTime);
 rigidbody.AddRelativeTorque(Vector3.left * pitch * pitchConstant * Time.deltaTime);

And then I try to stabilize it without success =(

avatar image Alex_K98 · Feb 15, 2015 at 10:36 AM 0
Share

I think I made the Chybis code working, but I don't think it does what it is supposed to. Or maybe I did something wrong?

working-chybis-script.zip (1.3 kB)
avatar image
0

Answer by garner · Apr 06, 2012 at 04:20 AM

You have to assign a rigidbody component to the object and add torque, check this out:

http://unity3d.com/support/documentation/ScriptReference/Rigidbody.AddTorque.html

Comment
Add comment · 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 Zachary.C.Stewart · Apr 06, 2012 at 12:02 PM 0
Share

I know that. I'm looking for the way to add torque then remove it timed to stop at the right point. So it's more a matter of when to time the stoping point.

avatar image
0

Answer by toniepeli · Dec 05, 2013 at 03:16 AM

I'm also doing some space ship alike game with friction in the air -> rotation should also stop. I didn't get that ^ code working. What is currentTarget?

I made this kind of code to slow down the rotation of ship's axises:

 float slowDownPower = 10.0f;
 Vector3 localAngularVelocity = transform.InverseTransformDirection(gameObject.rigidbody.angularVelocity);
 gameObject.rigidbody.AddRelativeTorque(-localAngularVelocity * slowDownPower * Time.deltaTime);

It seems to work so far.

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 Alex_K98 · Feb 14, 2015 at 09:50 PM 0
Share

toniepeli, I also came to something similar, but am looking to a solution that takes into account 'virtual' engine that has 'maximum throttle' at which the vessel tries to come to a complete stop. For example, maxThrottle = 10. We call AddRelativeTorque(10,5,3) for some time (2 seconds), then vessel drifts some time and then we need to completely stop it using AddTorque or AddRelativeTorque in accordance with the maxThrottle. Any ideas or links to answers on this matter?

avatar image toniepeli · Feb 15, 2015 at 11:07 AM 0
Share

Hey!

I haven't been working with Unity for awhile. Currently making 2D space flight game:) I didn't get the point completely. Is the purpose to rotate the space ship in steps?

avatar image Alex_K98 · Feb 15, 2015 at 11:29 AM 0
Share

The idea is to use Torque to decelerate rotating (all axis) object (ship) with given limited force (throttle). Let's say your ship received huge hit which caused vast rotation of it. We need to have the emergency slowdown/stabilization mode (to a complete stop). The engine can produce only (for example) 10 points of torque per time interval and we want to stop at maximum possible rate, so to say. Any suggestions/links to a solution?

avatar image
0

Answer by Map-Builder · Jan 11, 2017 at 12:10 AM

WOW, tried PID and I always got weird results, so I wrote my code :

to put in a file called PhysicsHelper.cs

 using UnityEngine;
 using System.Collections;
 
 public static class PhysicsHelper
 {
     
     public static void TorqueLookAtPoint(Rigidbody rigidbody, Vector3 point, float force, float damper = 0f)
     {
         Vector3 direction = point - rigidbody.position;
         TorqueLookToward(rigidbody, direction, force, damper);
     }
     public static void TorqueLookToward(Rigidbody rigidbody, Vector3 direction, float force, float damper = 0f)
     {
 
         Vector3 p = rigidbody.position;
         Vector3 forward = rigidbody.transform.forward; // axis we are rotating
 
         Vector3 cross = Vector3.Cross(forward, direction);
 
         float angleDiff = Vector3.Angle(forward, direction);
         angleDiff = Mathf.Sqrt(angleDiff);
         
         rigidbody.AddTorque(cross * angleDiff * force, ForceMode.Acceleration);
         rigidbody.AddTorque(-rigidbody.angularVelocity * damper, ForceMode.Acceleration);
         //rigidbody.AddTorque(cross * angleDiff * force);
 
         //Debug.Log(direction);
         //Debug.DrawLine(p, p + direction.normalized, Color.yellow, .05f);
         //Debug.DrawLine(p, p + rigidbody.angularVelocity, Color.yellow);
         //Debug.DrawLine(p, p + new Vector3(rigidbody.angularVelocity.x, 0, 0), Color.red);
         //Debug.DrawLine(p, p + new Vector3(0,rigidbody.angularVelocity.y, 0), Color.green);
         //Debug.DrawLine(p, p + new Vector3(0,0,rigidbody.angularVelocity.z), Color.blue);
     }
 
 }

Example of use :

         Rigidbody rigid = transform.GetComponent<Rigidbody>();
         Vector3 direction = Vector3.one;
         float force = 100f / Time.fixedDeltaTime;
         float damper = 50f;
         PhysicsHelper.TorqueLookToward(rigid, direction, force, damper);

Finally just adjust the force and set the dampening to not have your object's direction pass the direction and have a spring effect.

It may fit for most of your cases, with a minimum problems

Beware, if you'd like to change the deltatime per example, the force changes and needed dampening too (but it's just proportional to speed). Anyway, best solution I've found

>>> GIF <<<

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 Ultroman · Apr 16, 2018 at 07:59 PM 0
Share

I have found a bug. In some situations, in my case a ragdoll, this script can make the rigidbody explode (accelerate it improperly). Fortunately, simply changing Force$$anonymous$$ode.Acceleration to Force$$anonymous$$ode.Force for both AddTorque calls, remedies this problem. FANTASTIC script you have there, though! Completely saved my sanity!

avatar image Map-Builder · Apr 19, 2018 at 07:57 AM 0
Share

$$anonymous$$y pleasure and thanks to notice it. I'm not a prog but a designer graduating in mechanical engineering and using this method is more flexible and less foggy. It usually leave less bugs and bring versatility.

avatar image Ultroman · Aug 09, 2018 at 05:02 PM 0
Share

That's a nasty squareroot call, though. A very computational operation. Anyone using this for moving many units, might want to find a way around that call.

  • 1
  • 2
  • ›

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

17 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

Related Questions

How may I observe expected physical interactions while using Rigidbody.MoveRotation()? 1 Answer

Rotating a Rigidbody with Physics 1 Answer

How to keep rotation from switching between - and + after a 360? 0 Answers

How do i keep a rigidbody upright? Looking for best way to freeze rotation 1 Answer

Freezing Rotation and Joints (Swing / Rope) Physics 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