Unity Community |

Hi guys,
I've come up with the following code for a physics-based flight model. It's a very simple script:
Code:
#pragma strict var rollSpeed : float = 100.0; //rolling rotation speed var pitchSpeed : float = 100.0; //vertical rotation speed var yawSpeed : float = 100.0; //horizontal rotation speed var accelerate : float = 20000; //main engine thrust var velocity; var rotSpeed = 15; var planform; var thrust : float = 0; var maxThrust : float = 600000; var liftFactor : float = 0.1; var crashThreshold = 10.0; var crashSound : AudioSource; private var oldVelocity : float; // Use this for initialization { oldVelocity = rigidbody.velocity.sqrMagnitude; crashThreshold *= crashThreshold; // So it works with sqrMagnitude } function CrashCheck() { // TODO: Proper Crash Code } oldVelocity = rigidbody.velocity.sqrMagnitude; } function KeepInBounds() { //Wrap the penguin around the playfield so that you cannot fly under the landscape //Limit the height } { UpdateFunction(); CrashCheck(); KeepInBounds(); } function UpdateFunction() { var roll : float = 0; var yaw : float = 0; var lift : float = 0; if (thrust >= 0) //TODO: Add force at two points on a wing for stability KeepInBounds(); }
However, it's a bit tricky to fly. For example, if you use keyboard controls for the four axes, you have to be really careful with pressing the keys, otherwise you can easily enter a spin. Moreover, if you try using a gamepad with the right stick controlling, say, pitch and roll, with the right stick controlling the throttle and yaw, you can also get into a spin extremely easily. Does anyone have any suggestions for making it more stable? After all, you can't have something suddenly go from moving predictably on one axis, only for it to go totally haywire the next moment.
MachCUBED
Last edited by MachCUBED; 04-01-2012 at 06:11 PM.
Well if its physic-based i think you can try to change up the physic to your liking by going to
Edit>Project Settings>Physic
Then adjust till you like it. I'm not entirely sure about this though.
Play Pixel Pirates [its only a small test]
-----------------------------------------------------------------------------
Check out my Ludum Dare 23 Entry Here
My Blog
My Portfolio
The biggest problem with your code is that I believe AddTorque requires a vector (or components thereof) in world space, not local space. So you ought to use AddRelativeTorque instead here.
Also, you might need to think about the shape of the object you're flying and set an appropriate inertia tensor. This could for example make it more responsive to roll torque than yaw or pitch torque.
Your model is also ignoring air resistance, and this is quite important for aircraft as most aircraft are designed to be self-stabilising - so if left to their own devices they will yaw to align with the direction of travel, and roll to level.
You don't need a complicated aerodynamic model, but I think you do need to put in an extra force proportional to the non-forwards velocity of the plane. Then consider fading out the yaw and pitch inputs when the plane is not flying fairly close to the current facing direction in the corresponding axis - this prevents excessive inputs from causing more and more spin in an unrealistic way.
Overall though, I expect your main problem is applying your torques in world space when you should be doing it in local space.
Thanks for the tips George Foot, I can't believe I didn't spot the AddRelativeTorque fluke. I fixed it, so now it works significantly better. It's still a bit tricky, so I'll also implement the inertia tensor fixes and other suggestions that you made, then post the results here.
Last edited by MachCUBED; 04-01-2012 at 06:10 PM.
There is probably a way to calculate it from world torque, but there is no variable that stores the localtorque.. would be really useful as to stop your plane from spinning, you either have to set the angulardrag or add negative force.
The angular drag was initially 0.5, with a main drag of 0.5 as well. Changing the angular drag to 2 while leaving the main drag the same fixed my spin problem, while setting the angular drag to 5 made things too sluggish.
The problem with that method is it stops all rotation... if you only want it to stop rotating around one axis, you need to apply negative force...
After dealing with some other aspects of my game, I decided to try refining my code to add negative force as additional rotational drag for the roll axis (and only the roll axis). This is what I came up with:
Code:
#pragma strict var rollSpeed : float = 100.0; //rolling rotation speed var pitchSpeed : float = 100.0; //vertical rotation speed var yawSpeed : float = 100.0; //horizontal rotation speed var accelerate : float = 20000; //main engine thrust var velocity; var rotSpeed = 15; var leftForwardWing : GameObject; var rightForwardWing : GameObject; var leftTailPlane : GameObject; var rightTailPlane : GameObject; var thrust : float = 0; var maxThrust : float = 600000; var rollDragFactor : float = 1; var liftFactor : float = 0.1; var crashThreshold = 10.0; var crashSound : AudioSource; private var oldRoll : float = 0; private var oldVelocity : float; // Use this for initialization { oldVelocity = rigidbody.velocity.sqrMagnitude; crashThreshold *= crashThreshold; // So it works with sqrMagnitude //posX = transform.position.x; //posY = transform.position.y; //posZ = transform.position.z; //rigidbody.AddForce(0,accelerate.y,0); } function CrashCheck() { // TODO: Proper Crash Code } oldVelocity = rigidbody.velocity.sqrMagnitude; } function KeepInBounds() { //Wrap the penguin around the playfield so that you cannot fly under the landscape //Limit the height } { UpdateFunction(); CrashCheck(); KeepInBounds(); } function UpdateFunction() { //Get inputs var roll : float = 0; var yaw : float = 0; var lift : float = 0; oldRoll = roll; //Rotate the penguin on its axes else // Move forwards if (thrust >= 0) // Generate lift KeepInBounds(); }
Last edited by MachCUBED; 04-06-2012 at 06:15 PM. Reason: Code updated
You can get the angular velocity from the physics, convert it into local space, isolate components and apply any asymmetric angular drag functions you like. It all behaves mostly like the linear equivalents, and you won't go far wrong by just doing the obvious thing.
I recently threw together a Javascript that may help you with this flight problem...
var speed = 50.0;
var rotateSpeed = 10.0;
function Update () {
var x = Input.GetAxis("Horizontal") * Time.deltaTime * rotateSpeed;
var y = Input.GetAxis("Roll") * Time.deltaTime * rotateSpeed;
var z = Input.GetAxis("Vertical") * Time.deltaTime * rotateSpeed;
var accel = Input.GetAxis("Jump") * Time.deltaTime * speed;
rigidbody.AddRelativeForce(0, 0, accel);
rigidbody.AddRelativeTorque(z, x, -y);
}
Just attach this and a rigidbody (yes gravity for atmospheric flight, none for space) to your plane or spaceship game object and set the drag and angular drag (on rigidbody) to something between 1 and 10, then set the speed (on script inspector) to the drag * 1000, and the rotateSpeed (on script inspector) to the angular drag * 200 for a simple flight style. You can mess with the speed, drag, rotateSpeed, and angular drag to affect how your little flyer handles. You'll also need to create a new input (edit>project settings>input) called "Roll" and set the buttons to get its input for the script. Let me know if you need any specifications for the settings on the "Roll" input.
Controls: w/s for down/up angle, a/d for left/right turn, q/e for left/right rotate (do a barrel roll when you test it out!), space bar for acceleration.
You could also change "rigidbody.AddRelativeForce" and "rigidbody.AddRelativeTorque" to "transform.Translate" and "transform.Rotate," respectively, in order to make the controls more responsive, but it's also kinda dull to fly that way...
C&C appreciated!
-Orbitus2
Asa matter of fat, your solution is pretty similar to mine:
Code:
var rollSpeed : float = 100.0; //rolling rotation speed var pitchSpeed : float = 100.0; //vertical rotation speed var yawSpeed : float = 100.0; //horizontal rotation speed ... function UpdateOrientation() { var roll : float = 0; var yaw : float = 0; //Get inputs if (isControllable) { { if (thrust > 0) thrust = maxThrust; } else if (thrust < 0) thrust = 0; //Rotate the penguin on its axes else } else { } oldRoll = roll; // Move forwards if (thrust >= 0) // Fire exhaust if (thrust <= 0) { } else { if (thrust > 0) } }
You can adjust the different rotation speeds to create different turning effects. Thanks for the contribution.
MachCUBED
No problem, and you were the one who got me thinking, so thanks to you as well!
-Orbitus2
Here's a new version for iOS devices. Unfortunately, it's really touchy on roll inputs, while being sluggish on pitch inputs. This data is from a rigidbody with a mass of 0.5kg, angular drag of 0.05, and drag of 0.0025.
Code:
#pragma strict var rollSpeed : float = 0.0005; //rolling rotation speed var pitchSpeed : float = 125.0; //vertical rotation speed var yawSpeed : float = 1000.0; //horizontal rotation speed var accelerate : float = 600; //main engine thrust var velocity; var thrust : float = 1; var groundHit : RaycastHit; var maxThrust : float = 5; var rollDragFactor : float = 5; var liftFactor : float = 0.3; var crashThreshold = 10.0; var crashSound : AudioClip; var stallWarning : AudioClip; var poopChute : ParticleSystem; var maxHeight : int = 1000; var maxLife : int = 100; var life : int = maxLife; var throttlePad : Joystick; private var oldRoll : float = 0; private var oldVelocity : float; private var lift : float = 0; private var sensitivity : float = 1.0; private var controlType : String; protected var isControllable = true; private var gyroBool : boolean; // Use this for initialization { oldVelocity = rigidbody.velocity.sqrMagnitude; crashThreshold *= crashThreshold; // So it works with sqrMagnitude NotificationCenter.DefaultCenter().AddObserver(this, "PlayerDeactivated"); NotificationCenter.DefaultCenter().AddObserver(this, "PlayerControllable"); //Setup controls gyroBool = SystemInfo.supportsGyroscope; if (gyroBool) { { } } else { // Add accelerometer controls and/or keyboard/gamepad controls } // Setup lifespan and thrust from PlayerPrefs var defaultMaxThrust : int = maxThrust; var defaultMaxLife : int = maxLife; life = maxLife; } function DistressHonk() { oldVelocity = rigidbody.velocity.sqrMagnitude; life -= oldVelocity / 100; if (life <= 0) { life = 0; PlayerDeactivated(); } oldVelocity = rigidbody.velocity.sqrMagnitude; } function KeepInBounds() { //Wrap the penguin around the playfield so that you cannot fly under the landscape //Limit the height } { UpdateOrientation(); CheckWarnings(); KeepInBounds(); PlayVoiceAudio(); } function UpdateOrientation() { var roll : float = 0; var yaw : float = 0; //Get inputs if (isControllable) { // we assume that the device is held parallel to the ground // and the Home button is in the right hand // remap the device acceleration axis to game coordinates: // 1) XY plane of the device is mapped onto XZ plane // 2) rotated 90 degrees around Y axis if (gyroBool) { } else { } // clamp acceleration vector to the unit sphere // Make it move at a consistent rate per time step... /* // Code for Desktop version, if one is made in the future. roll = Input.GetAxis("Roll") * (Time.deltaTime * rollSpeed) * sensitivity; pitch = Input.GetAxis("Pitch") * (Time.deltaTime * pitchSpeed) * sensitivity; yaw = Input.GetAxis("Yaw") * (Time.deltaTime * yawSpeed) * sensitivity; thrust += Input.GetAxis("Throttle") * (Time.deltaTime * accelerate); */ { if (thrust > 0) thrust = maxThrust; } else if (thrust < 0) thrust = 0; //Rotate the pigeon on its axes } else { } oldRoll = roll; // Move forwards if (thrust >= 0) } function PlayVoiceAudio() { /* if (rigidbody.velocity.magnitude * 3.6 == 100 || rigidbody.velocity.magnitude * 3.6 == 200 || rigidbody.velocity.magnitude * 3.6 == 500 || rigidbody.velocity.magnitude * 3.6 == 1000) { //Honk if (audioSource.isPlaying) return; // don't play a new sound while the last hasn't finished audioSource.clip = voiceSounds[Random.Range(0,voiceSounds.length)]; audioSource.Play(); } */ } function CheckWarnings() { // Stall warning - for when there is no lift being generated //Debug.Log("Lift force:" + lift); if (lift <= 0) { } else { } } { DistressHonk(); } function PlayerDeactivated () { isControllable = false; } function PlayerControllable () { isControllable = true; }
Does anyone have any feedback or suggestions?
MachCUBED
Last edited by MachCUBED; 07-30-2012 at 11:16 AM.