Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Need help with torque, rotations, and spacecraft control

Discussion in 'Editor & General Support' started by Maker16, Jun 23, 2010.

  1. Maker16

    Maker16

    Joined:
    Mar 4, 2009
    Posts:
    779
    Currently, I have a script that uses AddTorque to simulate thruster control. What I want it to do is add torque areound whichever axis is receiving input. So, I'm doing this:

    Code (csharp):
    1.  
    2. AddTorque(thrust,0,0);   //x-axis torque
    3. AddTorque(0,thrust,0);   //y-axis torque
    4. AddTorque(0,0,thrust);   //z-axis torque
    5.  
    It works fine when I initially apply thrust to only one axis, but once it has rotated even a bit on one axis, adding toque on the other axes has it rotating in all sorts of wrong ways on wrong axes. What is the right way to script something basic like this?

    Note: Tried AddRelativeTorque, too. Same problem.
     
  2. aubergine

    aubergine

    Joined:
    Sep 12, 2009
    Posts:
    2,864
    For rotation on spaceship, dont use physics but use normal transform rotations. In the long run when you want to add auto pilot and some ai, you will have lots of math problems to solve otherwise.

    Use physics only on thrusting.
     
  3. Maker16

    Maker16

    Joined:
    Mar 4, 2009
    Posts:
    779
    Ok, but assuming I'm going to use AddTorque, how would I do it? Is it that the rigidbody is viewed in world space versus local space?

    To further clarify what I want to accomplish, I want the ship to rotate around each axis independent of the others' rotations. If I start the ship rolling around its z-axis, and then pitch the ship up on the x-axis, and then input to roll the opposite direction around its z-axis, it should do that. I thought AddRelativeTorque() was the solution, suspecting the problem was that AddTorque() was using the world axes, but the same problem is occurring. I found a post by Andeeee somewere that suggested something like:
    Code (csharp):
    1.  
    2. AddTorque(Vector3.up*amount)
    3.  
    How is that different from:
    Code (csharp):
    1.  
    2. AddRelativeTorque(Vector3.up*amount)
    3.  

    And another question I have...Transform.Rotate() uses euler angles. Problem is, euler angles result in gimbal lock in certain situations. What is the Quaternion equivalent to the Transform.Rotate() function?
     
  4. andeeeee

    andeeeee

    Joined:
    Jul 19, 2005
    Posts:
    8,768
    As you say, AddTorque works in world coordinates, so adding torque around Vector3.up will turn around the world Y axis. A freely-rotating object in space can actually be quite difficult to control in two axes, so you might need to help the player out by simplifying one axis or adding a bit of stabilising.

    The equivalent to transform.Rotate can be accomplished by using quaternion multiplication. Just specify the rotation corresponding to the change you want (ie, the parameters you would pass to transform.Rotate) and then multiply the existing rotation by this new quaternion:-
    Code (csharp):
    1. var change = Quaternion.Euler(0, 90, 0);
    2. transform.rotation *= change;
     
    ezero7 likes this.
  5. Maker16

    Maker16

    Joined:
    Mar 4, 2009
    Posts:
    779
    Yes, Andeee. I have already designed in auto-stabilization. Trust me, I understand. The first time I tried it manually, I was all over the place. Thanks. I will take another stab at it tonite and see if I can't get it to work.
     
  6. ForceX

    ForceX

    Joined:
    Jun 22, 2010
    Posts:
    1,102
    I’m working on something similar my self, and I was wondering if your using torque for your rotations to achieve a particular effect / style of rotation motion; or if you are just trying to figure out how to make the craft rotate around it’s axis? If you just want it to rotate you can use a (transform.rotate * input axis ) set to you local axis.
    This is what I’m using to get that classic X-wing / Freespace / X3 feel.
    If you are using joystick controls, what are you using for your throttle. I cannot get unity to recognize my throttle slider.
    When I get home I can post the code for my X,Y,Z, rotation controls.
     
  7. Maker16

    Maker16

    Joined:
    Mar 4, 2009
    Posts:
    779
    I am using torque to control the ship. I originally had it worked out to be done through the transform, but the math involved is a lot easier using AddTorque. I got it working, now that I have come to understand how AddTorque() works relative to world space.

    Code (csharp):
    1.  
    2. var tVec = Vector3(Input.GetAxis("Pitch"*pitchThrust,Input.GetAxis("Yaw")           *yawThrust,Input.GetAxis("Roll")*rollThrust);
    3.  
    4. rigidbody.AddTorque(transform.TransformDirection(tVec)*Time.deltaTime);
    5.  
    Not sure why so many are opposed to handling something like this using Physics versus direct transform manipulations. Handles just the same as far as I can tell.
     
  8. ForceX

    ForceX

    Joined:
    Jun 22, 2010
    Posts:
    1,102
    I'm going to plugin you code and give it a try. Heres mine just for the heck of it.

    ///

    var CurrentSpeed=0;
    var Speed=100;
    var Throttle=0.00;
    var Pitch=0.00;
    var Yaw=0.00;
    var Roll=0.00;

    private var PitchYaw=100;
    private var RollR= 100;

    function Update() {

    CurrentSpeed=(Throttle*Speed);

    Throttle =(Input.GetAxis("Throttle"));
    Pitch =(Input.GetAxis("Pitch"));
    Yaw =(Input.GetAxis("Yaw"));
    Roll =(Input.GetAxis("Roll"));

    //Throttle
    if(Throttle !=0){
    rigidbody.velocity= (transform.forward * Throttle * Speed);
    }
    // Pitch, Yaw, Roll Input
    if(Pitch != 0) {
    transform.Rotate (Pitch * Time.deltaTime * PitchYaw * -1, 0 , 0, Space.Self);
    }
    if(Yaw != 0) {
    transform.Rotate (0, Yaw * Time.deltaTime * PitchYaw * 1 , 0, Space.Self);
    }
    if(Roll != 0) {
    transform.Rotate(0, 0 , Roll * Time.deltaTime * RollR * -1, Space.Self);
    }
    }

    ///
     
  9. Maker16

    Maker16

    Joined:
    Mar 4, 2009
    Posts:
    779
    Don't you run into gimbal lock issues with that code? I had something which was essentially the same (maybe I'm missing something), but when I would rotate my ship 90 degrees along one axis, it would lock on me.

    By the way, when using torque, remember that your angular acceleration around any given axis is not equal to the magnitude of the respective component unless you specify it as such with the ForceMode argument of the AddTorque() function.
     
  10. ForceX

    ForceX

    Joined:
    Jun 22, 2010
    Posts:
    1,102
    I don't have any problems with the code getting locked in on one axis or the other. The only problem i have is Unity not recognizing my throttle slider. I hope someone knows how to fix that, or if it just doesn't work atm.
    http://forum.unity3d.com/viewtopic.php?t=55829


    The key to the code i use is the Space.Self. Whith out it, it does freak out. So the code needs to know to look at your local XYZ and not the world XYZ.
     
  11. Maker16

    Maker16

    Joined:
    Mar 4, 2009
    Posts:
    779
    Hey, Force. I had to go with the Transform.Rotate() approach, afterall. I liked AddTorque because it accounted for gradual build up of angular velocity accounting for mass of the ship, but to auto stabilize the ship, I needed a way to compare the local angular velocity with the target angular velocity. Can't use rigidbody.angularVelocity because it is oriented to world space, and rotations are happening in local space.

    So, I went with your method and just coded in the calculations to make the rotations acceleration values. Now, my ships don't rotate at the same angular velocity. They accelerate to the target angular velocity, meaning larger ships take longer to get to target rotation velocities and take longer to stop rotating.
     
  12. ForceX

    ForceX

    Joined:
    Jun 22, 2010
    Posts:
    1,102
    That's awesome! I'm glad it helped you out. My next step is to build up a rotation and acceleration system similar to yours. I'm just working out how I would like things to react.
     
  13. Maker16

    Maker16

    Joined:
    Mar 4, 2009
    Posts:
    779
    Hey, Force. I sent you a PM with my entire script as an attachment. Take a look at it. Use what you want from it. Whatever. I'm very pleased with it so far. I haven't implemented it with the game proper, but just a little toying with it has yielded a good balance between realistic control and a complexity level that is accessible to most players with a little practice.

    I don't have main engines implemented in this script. Propulsion is a separate one, but acceleration for those can be done using the same basic principals that I used for the attitude controls. When I'm done with it, I'll send it, too, if you like.
     
  14. ForceX

    ForceX

    Joined:
    Jun 22, 2010
    Posts:
    1,102
    Wow that's fantastic, I really appreciate you sharing. I've PM'd you back my test environment. It's a Matrix filed of cubes that help to provide visual feedback during testing of velocity and rotation. I found this to be more useful than just placing a few static objects as reference points. I hope it's something you can find useful.
     
  15. paulygons

    paulygons

    Joined:
    May 6, 2010
    Posts:
    162
    I wanted to post a package that trys to emulate Descent, but I've been fooling with the Project Settings > Input Manager, and the package I built doesn't seem to bring those changes along. Is there an easier way to share a project, like maybe zipping it up?
    http://www.stuffycorporatedesign.com/open/descentish.zip
    Anyway, I was pumped to see this conversation.

    The following seems to be a good start for what I had in mind for the test. I thought it might be related enough to be usefull, but I'm still an infant with Unity, so go ahead and laugh now, get it all out.

    Code (csharp):
    1.  
    2. var flameParticle : GameObject;
    3.  
    4.  
    5. function Awake (){
    6.     Screen.showCursor = false; //(hides the cursor)
    7.     flameParticle.renderer.enabled = false;//turn off gun to start with
    8.     Screen.lockCursor = true;
    9. }
    10.  
    11.  
    12. function Update(){
    13.     if (Input.GetButtonDown("Fire1")){
    14.         InvokeRepeating("GunFlash", 0,.05);
    15.     }
    16.     else{
    17.         CancelInvoke();
    18.     }
    19. }
    20.  
    21.  
    22. function FixedUpdate() {
    23. //recalculate movedirection directly from axes
    24. moveDirection = new Vector3(Input.GetAxis("Horizontal"), Input.GetAxis("Rise"), Input.GetAxis("Vertical"));
    25. moveDirection = transform.TransformDirection(moveDirection);
    26. rigidbody.AddForce (moveDirection * 20);
    27.  
    28. rollDir = transform.TransformDirection(Input.GetAxis("Mouse Y")*2, Input.GetAxis("Mouse X")*2, Input.GetAxis("Roll")*-.25);
    29.  
    30. rigidbody.AddTorque (rollDir);
    31. }
    32.  
    33.  
    34. function GunFlash(){
    35.     if(flameParticle.renderer.enabled){
    36.         flameParticle.renderer.enabled = false;
    37.     }
    38.     else{
    39.         flameParticle.renderer.enabled = true;
    40.     }
    41. }
    42.  
     
    KOemxe likes this.
  16. Maker16

    Maker16

    Joined:
    Mar 4, 2009
    Posts:
    779
    Pauly, that's basically the same premise for what I was doing when I was using AddTorque. My stumbling block there, however, was handling the autostbailization of my ship, whereby, when the user stopped giving input (meaning the ship should stop all rotations on any axis with zero input), counter torque would be applied to stop the ship (rather than the artificial method of just stopping them cold). I was having trouble figuring out how to determine the local angular velocity of the rigidbody to determine the amount of counter-torque needed, so I abandoned that path and went to direct transform manipulation.
     
  17. paulygons

    paulygons

    Joined:
    May 6, 2010
    Posts:
    162
    I think I understand now. I'm imagining a "navigational computer" that counters rotation with automated thrusts, instead of the "mysterious drag" that I used. In other words, you're looking for something a bit more sim-ish and exact? I've been assuming that I could explain away the mystery drag by adding thruster effects when the user wasn't adding input, and make the thruster graphics proportional to the amount of spin. Hope that made sense.
     
  18. ForceX

    ForceX

    Joined:
    Jun 22, 2010
    Posts:
    1,102
    Haha.. My reasoning was the use of inertial dampener or external thrusters to account for the offset. This I think also allows for an extra level of upgrade-ability.
     
  19. ForceX

    ForceX

    Joined:
    Jun 22, 2010
    Posts:
    1,102
    Ok I had an idea. We want rotation to ease out over time. Say 2 seconds. How about having a timer that counts down two seconds and multiplies by the rotation thrust velocity. So as the count down timer approaches 0 you slow down until you stop. This could be applied to thrust also to make it take time to accelerate after the throttle slider has been moved.

    Some thing like:
    CountdownTimerY = (Count Down Math Here)
    CountDownYaw = (CountdownTimerY*RotationThrust*LastRotationAngelFromAxis)
    if(Yaw != 0) {
    transform.Rotate (0, CountDownYaw* Time.deltaTime * PitchYaw * 1 , 0, Space.Self);

    I’m sure there’s more that’s required but it’s the principal I wanted to get out there. Let me know what you guys think. I’ll start working on this after I get home and see what happens.
     
  20. Maker16

    Maker16

    Joined:
    Mar 4, 2009
    Posts:
    779
    If anyone can tell me a good way to convert the rigidbody.angularVelocity values from world to local, I will give you the script for auto-counter thrust using AddTorque().

    ForceX, the script I gave you will work with AddTorque with a little modification. I just need to figure out that one piece of the puzzle I mentioned above. But, as it is, the script is simulating proper torque with a given thrust-to-mass ratio anyway. When it slows itself down, it is due to calculated counter torque that is converted into rotation acceleration values. Essentially, it is simulating counter thrust. It just does all the math that AddTorque does for you.
     
  21. ForceX

    ForceX

    Joined:
    Jun 22, 2010
    Posts:
    1,102
    I was playing around with rigidbody.AddRotation and I Unity was telling me that it cannot be monitored. So I'm not sure how to get unity to return ithe nformation of a rigidbody.. So I was thinking how about using pitchR= Mathf.SmoothDamp or Math.SmoothStep, using something like minPitchThrust , MaxPitxhThrust?
     
  22. Maker16

    Maker16

    Joined:
    Mar 4, 2009
    Posts:
    779
    With a little help from my father (an aerospace engineer :) ), I figured out what needs to be done to convert the rigidbody.angularVelocity from world to local and back again. I will re-work my script to use AddTorque tonight, and I will get it to you guys tomorrow.
     
  23. ForceX

    ForceX

    Joined:
    Jun 22, 2010
    Posts:
    1,102
    This is fantastic news! Great job.
     
  24. klumhru

    klumhru

    Joined:
    Jun 25, 2010
    Posts:
    32
    I saw someone posted about AI being difficult using torques to steer.

    This function:
    Code (csharp):
    1.     public static void TurnToTarget(Transform transform,  Vector3 movePoint, float maxTurnAccel)
    2.     {
    3.         Vector3 directionOfTarget = (movePoint - transform.position).normalized;
    4.         Vector3 directionInEulers = Quaternion.LookRotation(directionOfTarget, Vector3.up).eulerAngles;
    5.         Vector3 offsetInEulers = ClampHeading(directionInEulers) - ClampHeading(transform.eulerAngles);
    6.         Vector3 angularVelocity = transform.rigidbody.angularVelocity/Time.fixedDeltaTime;
    7.         offsetInEulers = ClampHeading(offsetInEulers);
    8.         if (offsetInEulers.sqrMagnitude < Mathf.Pow(maxTurnAccel, 2))
    9.         {
    10.             if (offsetInEulers.y < 0)
    11.             {
    12.                 if (angularVelocity.y < offsetInEulers.y)
    13.                 {
    14.                     offsetInEulers.y = -offsetInEulers.y;
    15.                 }
    16.             }
    17.             else
    18.             {
    19.                 if (angularVelocity.y > offsetInEulers.y)
    20.                 {
    21.                     offsetInEulers.y = -offsetInEulers.y;
    22.                 }
    23.             }
    24.             if (offsetInEulers.x > 0)
    25.             {
    26.                 if (angularVelocity.x < -offsetInEulers.x)
    27.                 {
    28.                     offsetInEulers.x = -offsetInEulers.x * 2;
    29.                 }
    30.             }
    31.             else
    32.             {
    33.                 if (angularVelocity.x > -offsetInEulers.x)
    34.                 {
    35.                     offsetInEulers.x = -offsetInEulers.x * 2;
    36.                 }
    37.             }
    38.             if (offsetInEulers.z > 0)
    39.             {
    40.                 if (angularVelocity.z < -offsetInEulers.z)
    41.                     offsetInEulers.z = -offsetInEulers.z * 2;
    42.             }
    43.             else
    44.             {
    45.                 if (angularVelocity.z > -offsetInEulers.z)
    46.                     offsetInEulers.z = -offsetInEulers.z * 2;
    47.             }
    48.         }
    49.         offsetInEulers = ClampVector(offsetInEulers / 10, -maxTurnAccel, maxTurnAccel);
    50.         transform.rigidbody.AddRelativeTorque(offsetInEulers);
    51.     }
    52.  
    did the trick for me. Hope someone finds it useful. It uses euler angles and clamps to get past the gimbal problem and seems to be working fine.

    Edit:
    Thought I might include the required functions as well:
    Code (csharp):
    1.     public static Vector3 ClampHeading(Vector3 eulers)
    2.     {
    3.         for (int i = 0; i < 3; i++ )
    4.             eulers[i] = ClampHeading(eulers[i]);
    5.         return eulers;
    6.     }
    7.  
    8.     public static float ClampHeading(float heading)
    9.     {
    10.         if (heading > 180f)
    11.             return heading - 360f;
    12.         if (heading < -180f)
    13.             return heading + 360f;
    14.         return heading;
    15.     }
    16.  
    17.     public static Vector3 ClampVector(Vector3 vec, float min, float max)
    18.     {
    19.         for (int i = 0; i < 3; i++)
    20.             vec[i] = Mathf.Clamp(vec[i], min, max);
    21.         return vec;
    22.     }
    And the reason for all the ifs is to reduce the angularVelocity when the angle is reaching the target angle. It should be possible to use this with Input values, e.g. gamepades, mouse, by fiddling with the values.

    Regards
    Klum
     
  25. ForceX

    ForceX

    Joined:
    Jun 22, 2010
    Posts:
    1,102
    Thank you for sharing this. This is a great resource.
     
  26. Maker16

    Maker16

    Joined:
    Mar 4, 2009
    Posts:
    779
    Ok. Here is the new and improved Reaction Control System for spacecraft, now featuring AddTorque!! In reference to Klumhru's post regarding AI control, I will, in the coming weeks, see if I can't develop some utility functions for AI to use with this script, using what I can fro his script. Those who say it is difficult to implement AI spacecraft control using AddTorque have never met my father! He knows the math, I know how to integrate it in Unity. So, thank you, Klumhru, for the scripting.

    Back to my new RCS system. Functions similarly to my first attempt using direct transform manipulation, but it is done completely with AddTorque (with one bit that directly sets the rigidbody.angularVelocity, but, when you read the comments, you will understand why it is necessary...which it really isn't, but I didn't want to code in the calculations to adjust the torque. Just easier my way. :p )

    This system uses a fixed thrust configuration in and of itself. However, it can be used in conjunction with other scripts that can handle variable thrust configurations (i.e. - if you want to track each thruster individually, maybe for something to account for damage to one or multiple thrusters, you could use another script to track the amount of thrust available for each thruster, then crunch some numbers to determine how much total thrust is avaiable on each axis and feed those values into this script).

    Anyway, I commented the heck out of it, so you should be able to follow what I am doing. Any questions, let me know. The best part is, it works in builds just like in the editor (as you no doubt have realized by now, my last attempt turned out to be buggy as hell).
     

    Attached Files:

  27. ForceX

    ForceX

    Joined:
    Jun 22, 2010
    Posts:
    1,102
    Testing this out..

    Whats the proper setup for the script. I have my Mesh with a rigidbody and box collider applied. It seams to be getting stuck at functions Awake()
     
  28. Maker16

    Maker16

    Joined:
    Mar 4, 2009
    Posts:
    779
    Oh. Look at the first line of code in Awake(). I have an child object called "Frame" on mine. You just need to re-write that line so it finds the mesh you are using. Might be simple enough to do this:

    Code (csharp):
    1.  
    2. var shipExtents = GetComponent(MeshFilter).mesh.bounds.extents;
    3.  
    This is assuming your script is on the same object that has the MeshFilter component. And pay attention to the comments I wrote in the Awake function. My mesh was rotated 90 degrees, so I had to mess with the math a little.
     
  29. ForceX

    ForceX

    Joined:
    Jun 22, 2010
    Posts:
    1,102
    Ok cool. I'll give that a go. In the meantime here's the script I've been working on. I liked your naming conventions on the functions so i updated mine to be similar.

    You can adjust the drag settings to get different flight charactics.

    The throttle math is just made up atm everything equates to maxVelocity. I'll get a real math solver implemented soon.
     
  30. ForceX

    ForceX

    Joined:
    Jun 22, 2010
    Posts:
    1,102
    Maker16 what do you think the solution is to the world space floating point problem of having to keep the player near the 0,0,0 orgin. Other wise things start to shake. I just found this out. http://forum.unity3d.com/viewtopic.php?t=56285

    To see what i mean. Move your ship with script attached to about 2000 on the Z and switch to game mode. Move the joystick and it should happen.
     
  31. Maker16

    Maker16

    Joined:
    Mar 4, 2009
    Posts:
    779
    I haven't encountered this myself. I have flown up to 500000 units out and never seen the shakes. I think scaling is the best solution, as suggested in the other thread. 500000 units in my world equates to the furthest extent of the largest solar system. Everything else is scaled down. An earth sized planet is 1 unit in diameter.
     
  32. ForceX

    ForceX

    Joined:
    Jun 22, 2010
    Posts:
    1,102
    Wow that small. I can't seam to het the camera to play nice once I drop the camera near clip below .0001
     
  33. Maker16

    Maker16

    Joined:
    Mar 4, 2009
    Posts:
    779
    Well, I haven't implemented any of my ships with my system scale yet, so I'm sure there will be issues. I may just use a script to manage the scaling of planets so that, the closer the ship is to a planet, the larger the scale of the planet is. Distance is the real key, in my project. If I scale up the planets and reduce the approach velocity as a ship gets closer, I should be able to simulate a believable scale without having to scale my ships down. Maybe that will be my next project. :)

    Now that the ship control scheme is good enough for now, I'm ready to move on to another task. I have a procedural system generator that is 80% done, so I think I wil finish that off. Once done, then I will tackle the scaling issue.
     
  34. M4R5

    M4R5

    Joined:
    Apr 11, 2013
    Posts:
    33
    I lerped the rigidbody's drag and angular drag to higher values as a sort of RCS auto-stabilization device. Still struggling with the physics enabled roll though, and I'm not willing to sacrifice the physics aspect of it so woe is me...