Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Floating a vehicle above the ground with multiple springs

Discussion in 'Scripting' started by Larry, Jan 10, 2010.

  1. Larry

    Larry

    Joined:
    Nov 3, 2009
    Posts:
    9
    I've been experimenting in Unity lately with hovering vehicles using the physics simulation. At first, I had a vehicle that would simply maintain a target position using a configurable joint and the y drive with high spring and damping forces. This gave me a very smooth floating effect which I liked. But I'm now trying to take it one step further by trying to get each corner to stay a desired distance above the ground, so I can use complex terrain rather than a simple plane.

    I realise this topic has come up a few times before. I was able to find a few but I couldn't really make sense of the maths, and they were tailored for different, specific purposes.

    So far, I have a child object in each corner of a massive rigidbody. I use a raycast to detect the difference between the current distance of the point from the ground, and the desired distance, and I'm trying to script my own force because I don't really know what I'm doing with the spring joints.

    Basically, I don't really know how to use spring joints to do this, and whatever forces I'm calculating myself are wrong (and I don't know how to correctly apply damping) and produce very unstable movements.

    My current script applied to every thruster child object of the main rigidbody:
    Code (csharp):
    1. var desiredHeight = 1.0;
    2. var spring = 100.0;
    3. var damp = 10.0;
    4.  
    5. var attachedRigidbody : Rigidbody;
    6.  
    7. function FixedUpdate() {
    8.     var hit : RaycastHit;
    9.    
    10.     var up = Vector3.up;
    11.    
    12.     if (Physics.Raycast(transform.position, -up, hit, 10.0)) {     
    13.         var discrepency = desiredHeight - hit.distance;
    14.         var addLift = discrepency * spring * up;
    15.        
    16.         attachedRigidbody.AddForceAtPosition(-addLift, transform.position);
    17.     }
    18. }
    (I've noticed in the past that when I use Physics.Raycast the hit.point seems to be flipped about the XZ plane. Is this true? Perhaps it was an error on my part at the time, but it's the reason I'm using -addLift)

    I want to script either my own forces to make each corner of the craft spring about the desiredHeight + the height of the terrain, or somehow use the spring joints to achieve this.

    As I've said, I've tried many different approaches in that script and nothing has produced even stable results.

    Thanks.
     
  2. grobm

    grobm

    Joined:
    Aug 15, 2005
    Posts:
    217
    Wrote some boat code for a project that die before it got published so maybe this will help:

    Code (csharp):
    1. //Boat physics
    2. //======================================================================
    3.  
    4. function FixedUpdate () {
    5.    
    6.     //if there is no water surface we are colliding with, no boat physics  
    7.     if(waterSurface==null)
    8.         return;
    9.  
    10.     //query input axes if necessarry
    11.     motor = 0.0;
    12.     steer = 0.0;
    13.     if(queryUserInput)
    14.     {
    15.         motor = Input.GetAxis("Vertical");
    16.         steer = Input.GetAxis("Horizontal");
    17.     }
    18.  
    19.     //get water level and percent under water
    20.     waterLevel=waterSurface.collider.bounds.max.y;
    21.     distanceFromWaterLevel = transform.position.y-waterLevel;
    22.     percentUnderWater = Mathf.Clamp01((-distanceFromWaterLevel + 0.5*size.y)/size.y);
    23.  
    24.  
    25.     //Buoyancy (the force which keeps the boat floating above water)
    26.     //--------------------------------------------------------------
    27.    
    28.     //the point the buoyancy force is applied onto is calculated based
    29.     //on the boat's picth and roll, so it will always tilt upwards:
    30.     buoyancyPos=transform.TransformPoint(-Vector3(transform.right.y*size.x*0.5,0,transform.forward.y*size.z*0.5));
    31.    
    32.     //then it is shifted arcording to the current waves
    33.     buoyancyPos.x+=waterSurface.waveXMotion1*Mathf.Sin(waterSurface.waveFreq1*Time.time)
    34.                 +waterSurface.waveXMotion2*Mathf.Sin(waterSurface.waveFreq2*Time.time)
    35.                 +waterSurface.waveXMotion3*Mathf.Sin(waterSurface.waveFreq3*Time.time);
    36.     buoyancyPos.z+=waterSurface.waveYMotion1*Mathf.Sin(waterSurface.waveFreq1*Time.time)
    37.                 +waterSurface.waveYMotion2*Mathf.Sin(waterSurface.waveFreq2*Time.time)
    38.                 +waterSurface.waveYMotion3*Mathf.Sin(waterSurface.waveFreq3*Time.time);
    39.    
    40.     //apply the force
    41.     rigidbody.AddForceAtPosition(- volume * percentUnderWater * Physics.gravity , buoyancyPos);
    42.    
    43.     //Engine
    44.     //--------------------------------------------------------------
    45.    
    46.     //calculate propeller position
    47.     propellerPos = Vector3(0,-size.y*0.5,-size.z*0.5);
    48.     propellerPosGlobal=transform.TransformPoint(propellerPos);
    49.    
    50.     //apply force only if propeller is under water
    51.     if(propellerPosGlobal.y<waterLevel)
    52.     {
    53.         //direction propeller force is pointing to.
    54.         //mostly forward, rotated a bit according to steering angle
    55.         steeringAngle = steer * propellerTurningAngle * Mathf.Deg2Rad;
    56.         propellerDir = transform.forward*Mathf.Cos(steeringAngle) - transform.right*Mathf.Sin(steeringAngle);
    57.        
    58.         //apply propeller force
    59.         rigidbody.AddForceAtPosition(propellerDir * engineForce * motor , propellerPosGlobal);
    60.        
    61.         //create particles for propeller
    62.         if(engineSpume!=null)
    63.         {
    64.             engineSpume.position = propellerPosGlobal;
    65.             engineSpume.position.y = waterLevel-0.5;
    66.             engineSpume.particleEmitter.worldVelocity = rigidbody.velocity*0.5-propellerDir*10*motor+Vector3.up*3*Mathf.Clamp01(motor);
    67.             engineSpume.particleEmitter.minEmission = Mathf.Abs(motor)*3;
    68.             engineSpume.particleEmitter.maxEmission = Mathf.Abs(motor)*3;
    69.             engineSpume.particleEmitter.Emit();            
    70.         }
    71.     }
    72.    
    73.     //Drag
    74.     //--------------------------------------------------------------
    75.    
    76.     //calculate drag force
    77.     dragDirection = transform.InverseTransformDirection(rigidbody.velocity);
    78.     dragForces = -Vector3.Scale(dragDirection,drag);
    79.    
    80.     //depth of the boat under water (used to find attack point for drag force)
    81.     depth = Mathf.Abs(transform.forward.y)*size.z*0.5+Mathf.Abs(transform.up.y)*size.y*0.5;
    82.    
    83.     //apply force
    84.     dragAttackPosition = Vector3(transform.position.x,waterLevel-depth,transform.position.z);
    85.     rigidbody.AddForceAtPosition(transform.TransformDirection(dragForces)*rigidbody.velocity.magnitude*(1+percentUnderWater*(waterSurface.waterDragFactor-1)),dragAttackPosition);
    86.    
    87.     //linear drag (linear to velocity, for low speed movement)
    88.     rigidbody.AddForce(transform.TransformDirection(dragForces)*500);
    89.    
    90.     //rudder torque for steering (square to velocity)
    91.     forwardVelo = Vector3.Dot(rigidbody.velocity,transform.forward);
    92.     rigidbody.AddTorque(transform.up*forwardVelo*forwardVelo*rudder*steer);
    93.    
    94.     //Sound
    95.     //--------------------------------------------------------------
    96.    
    97.     audio.volume=0.3+Mathf.Abs(motor);
    98.  
    99.     //slowly adjust pitch to power input
    100.     rpmPitch=Mathf.Lerp(rpmPitch,Mathf.Abs(motor),Time.deltaTime*0.4);
    101.     audio.pitch=0.3+0.7*rpmPitch;
    102.  
    103.     //reset water surface, so we have to stay in contact for boat physics.
    104.     waterSurface = null;
    105. }
    106.  
    107. function OnTriggerStay(coll)
    108. {
    109.     if(coll.GetComponent(FloatableWater)!=null)
    110.         waterSurface=coll.GetComponent(FloatableWater);
    111. }
    112.  
     
  3. Larry

    Larry

    Joined:
    Nov 3, 2009
    Posts:
    9
    OK, this appears to work:
    Code (csharp):
    1. var desiredHeight = 1.0;
    2. var spring = 100.0;
    3. var damp = 10.0;
    4.  
    5. var attachedRigidbody : Rigidbody;
    6.  
    7. function FixedUpdate() {
    8.     var hit : RaycastHit;
    9.    
    10.     var up = Vector3.up;
    11.    
    12.     if (Physics.Raycast(transform.position, -up, hit, 100.0)) {
    13.         // Force required to negate gravity
    14.         var neutralForce = attachedRigidbody.mass * Physics.gravity * 0.2;
    15.        
    16.         var yDifference = (hit.point.y + desiredHeight) - transform.position.y;
    17.         var addForce = yDifference * spring;
    18.         addForce -= attachedRigidbody.GetPointVelocity(transform.position).y * damp;
    19.        
    20.         attachedRigidbody.AddForceAtPosition(neutralForce + addForce*up, transform.position);
    21.     }
    22. }
    Thanks for the boat script as well, it's quite interesting, I hadn't thought about using drag forces or using a buoyancy based simulation.

    I still welcome any further tips.

    Thanks.