private BiVector3 CalculateAerodynamicForces(Vector3 velocity, Vector3 angularVelocity, Vector3 wind, float airDensity, Vector3 centerOfMass) { BiVector3 forceAndTorque = new BiVector3(); foreach (var surface in aerodynamicSurfaces) { Vector3 relativePosition = surface.transform.position - centerOfMass; forceAndTorque += surface.CalculateForces(-velocity + wind - Vector3.Cross(angularVelocity, relativePosition), airDensity, relativePosition); } return(forceAndTorque); }
private void FixedUpdate() { BiVector3 forceAndTorqueThisFrame = CalculateAerodynamicForces(rb.velocity, rb.angularVelocity, Vector3.zero, 1.2f, rb.worldCenterOfMass); Vector3 velocityPrediction = PredictVelocity(forceAndTorqueThisFrame.p); Vector3 angularVelocityPrediction = PredictAngularVelocity(forceAndTorqueThisFrame.q); BiVector3 forceAndTorquePrediction = CalculateAerodynamicForces(velocityPrediction, angularVelocityPrediction, Vector3.zero, 1.2f, rb.worldCenterOfMass); currentForceAndTorque = (forceAndTorqueThisFrame + forceAndTorquePrediction) * 0.5f; rb.AddForce(currentForceAndTorque.p); rb.AddTorque(currentForceAndTorque.q); rb.AddForce(transform.forward * thrust * thrustPercent); }
public BiVector3 CalculateForces(Vector3 worldAirVelocity, float airDensity, Vector3 relativePosition) { BiVector3 forceAndTorque = new BiVector3(); if (!gameObject.activeInHierarchy || config == null) { return(forceAndTorque); } if (!initialized) { Initialize(); } Vector3 airVelocity = transform.InverseTransformDirection(worldAirVelocity); airVelocity = new Vector3(airVelocity.x, airVelocity.y); Vector3 dragDirection = transform.TransformDirection(airVelocity.normalized); Vector3 liftDirection = Vector3.Cross(dragDirection, transform.forward); float dynamicPressure = 0.5f * airDensity * airVelocity.sqrMagnitude; float angleOfAttack = Mathf.Atan2(airVelocity.y, -airVelocity.x); IsAtStall = !(angleOfAttack <stallAngleHigh && angleOfAttack> stallAngleLow); Vector3 aerodynamicCoefficients = CalculateCoefficients(angleOfAttack); CurrentLift = liftDirection * aerodynamicCoefficients.x * dynamicPressure * area; CurrentDrag = dragDirection * aerodynamicCoefficients.y * dynamicPressure * area; CurrentTorque = -transform.forward * aerodynamicCoefficients.z * dynamicPressure * area * config.chord; forceAndTorque.p += CurrentDrag + CurrentLift; forceAndTorque.q += Vector3.Cross(relativePosition, forceAndTorque.p); forceAndTorque.q += CurrentTorque; return(forceAndTorque); }
public BiVector3 CalculateForces(Vector3 worldAirVelocity, float airDensity, Vector3 relativePosition) { BiVector3 forceAndTorque = new BiVector3(); if (!gameObject.activeInHierarchy || config == null) { return(forceAndTorque); } // Accounting for aspect ratio effect on lift coefficient. float correctedLiftSlope = config.liftSlope * config.aspectRatio / (config.aspectRatio + 2 * (config.aspectRatio + 4) / (config.aspectRatio + 2)); // Calculating flap deflection influence on zero lift angle of attack // and angles at which stall happens. float theta = Mathf.Acos(2 * config.flapFraction - 1); float flapEffectivness = 1 - (theta - Mathf.Sin(theta)) / Mathf.PI; float deltaLift = correctedLiftSlope * flapEffectivness * FlapEffectivnessCorrection(flapAngle) * flapAngle; float zeroLiftAoaBase = config.zeroLiftAoA * Mathf.Deg2Rad; float zeroLiftAoA = zeroLiftAoaBase - deltaLift / correctedLiftSlope; float stallAngleHighBase = config.stallAngleHigh * Mathf.Deg2Rad; float stallAngleLowBase = config.stallAngleLow * Mathf.Deg2Rad; float clMaxHigh = correctedLiftSlope * (stallAngleHighBase - zeroLiftAoaBase) + deltaLift * LiftCoefficientMaxFraction(config.flapFraction); float clMaxLow = correctedLiftSlope * (stallAngleLowBase - zeroLiftAoaBase) + deltaLift * LiftCoefficientMaxFraction(config.flapFraction); float stallAngleHigh = zeroLiftAoA + clMaxHigh / correctedLiftSlope; float stallAngleLow = zeroLiftAoA + clMaxLow / correctedLiftSlope; // Calculating air velocity relative to the surface's coordinate system. // Z component of the velocity is discarded. Vector3 airVelocity = transform.InverseTransformDirection(worldAirVelocity); airVelocity = new Vector3(airVelocity.x, airVelocity.y); Vector3 dragDirection = transform.TransformDirection(airVelocity.normalized); Vector3 liftDirection = Vector3.Cross(dragDirection, transform.forward); float area = config.chord * config.span; float dynamicPressure = 0.5f * airDensity * airVelocity.sqrMagnitude; float angleOfAttack = Mathf.Atan2(airVelocity.y, -airVelocity.x); Vector3 aerodynamicCoefficients = CalculateCoefficients(angleOfAttack, correctedLiftSlope, zeroLiftAoA, stallAngleHigh, stallAngleLow); Vector3 lift = liftDirection * aerodynamicCoefficients.x * dynamicPressure * area; Vector3 drag = dragDirection * aerodynamicCoefficients.y * dynamicPressure * area; Vector3 torque = -transform.forward * aerodynamicCoefficients.z * dynamicPressure * area * config.chord; forceAndTorque.p += lift + drag; forceAndTorque.q += Vector3.Cross(relativePosition, forceAndTorque.p); forceAndTorque.q += torque; #if UNITY_EDITOR // For gizmos drawing. IsAtStall = !(angleOfAttack <stallAngleHigh && angleOfAttack> stallAngleLow); CurrentLift = lift; CurrentDrag = drag; CurrentTorque = torque; #endif return(forceAndTorque); }