private void FixedUpdate() { if (_wind == null) { return; } if (OnPreFixedUpdate != null) { OnPreFixedUpdate(this); } Vector3 centerPosition = transform.position; Vector3 windVelocity = _wind.GetWindVelocity(centerPosition); float airDensity = _wind.GetAirDensity(centerPosition); RelativeVelocity = _body.velocity - windVelocity; AirSpeed = RelativeVelocity.magnitude; Vector3 force = -RelativeVelocity.normalized * (0.5f * airDensity * _referenceArea * AirSpeed * AirSpeed * _cDrag); // Todo: optimize by not normalizing relative velocity _body.AddForceAtPosition(force, transform.position); if (OnPostFixedUpdate != null) { OnPostFixedUpdate(this); } }
private void FixedUpdate() { if (_body == null || _wind == null) { return; } if (OnPreFixedUpdate != null) { OnPreFixedUpdate(this); } Vector3 centerPosition = transform.position; Vector3 windVelocity = _wind.GetWindVelocity(centerPosition); float airDensity = _wind.GetAirDensity(centerPosition); RelativeVelocity = _body.velocity - windVelocity; AirSpeed = RelativeVelocity.magnitude; Vector3 worldLongitudinalAxis = transform.TransformDirection(_longitudinalAxis); Vector3 axis = Vector3.Cross(worldLongitudinalAxis, RelativeVelocity).normalized; AngleOfAttack = MathUtils.AngleAroundAxis(worldLongitudinalAxis, RelativeVelocity, axis); float liftCoefficient = _liftCoeffients.Evaluate(AngleOfAttack) * _liftCoefficientMultiplier; float dragCoefficient = _dragCoeffients.Evaluate(AngleOfAttack) * _dragCoefficientMultiplier; float dynamicSurfacePressure = 0.5f * airDensity * _referenceArea * AirSpeed * AirSpeed; // Todo: can optimize by not normalizing relative velocity LiftForce = Vector3.Cross(RelativeVelocity, axis).normalized *(dynamicSurfacePressure * liftCoefficient); DragForce = -RelativeVelocity.normalized * (dynamicSurfacePressure * dragCoefficient); _body.AddForceAtPosition(LiftForce + DragForce, transform.position); if (OnPostFixedUpdate != null) { OnPostFixedUpdate(this); } }
private void EvaluateForces() { Vector3 position = _transform.position; Vector3 windVelocity = _wind.GetWindVelocity(position); float airDensity = _wind.GetAirDensity(position); Vector3 velocity = (position - _lastPosition) / Time.deltaTime; _lastPosition = position; RelativeVelocity = velocity - windVelocity; AirSpeed = RelativeVelocity.magnitude; Vector3 relativeDirection = RelativeVelocity.normalized; float dynamicSurfacePressure = 0.5f * airDensity * _surfaceArea * AirSpeed * AirSpeed; // Bug: This method of determining angle of attack only works for the default axis configuration!! // Bug: if lift coeff goes negative, we get NaNs really quickly! Vector3 localRelativeVelocity = _transform.InverseTransformDirection(RelativeVelocity); Vector2 pitchPlaneVelocity = new Vector2(localRelativeVelocity.z, localRelativeVelocity.y); AngleOfAttack = AngleSigned(Vector2.right, pitchPlaneVelocity); if (float.IsNaN(AngleOfAttack)) { Debug.LogError("Angle of Attack is NaN " + RelativeVelocity); #if UNITY_EDITOR EditorApplication.isPlaying = false; #endif return; } Vector3 worldAxis = _transform.TransformDirection(_pitchAxis); Vector3 linearForce = Vector3.zero; if (_liftResponse.Enabled) { float liftCoefficient = _liftResponse.Coefficients.Evaluate(AngleOfAttack) * _liftResponse.Multiplier; float liftMagnitude = dynamicSurfacePressure * liftCoefficient * Efficiency; Vector3 liftDirection = Vector3.Cross(relativeDirection, worldAxis); LiftForce = liftDirection * liftMagnitude; linearForce += LiftForce; } if (_dragResponse.Enabled) { float dragCoefficient = _dragResponse.Coefficients.Evaluate(AngleOfAttack) * _dragResponse.Multiplier; float dragMagnitude = dynamicSurfacePressure * dragCoefficient * Efficiency; DragForce = relativeDirection * -dragMagnitude; linearForce += DragForce; } if (_momentResponse.Enabled) { float momentCoefficient = _momentResponse.Coefficients.Evaluate(AngleOfAttack) * _momentResponse.Multiplier; float momentMagnitude = dynamicSurfacePressure * momentCoefficient; MomentForce = worldAxis * momentMagnitude; } Vector3 pressurePoint = _transform.TransformPoint(_chordPressurePoint); // Todo: only needed if applying force from within this script if (_body) { _body.AddForceAtPosition(linearForce, pressurePoint); _body.AddTorqueAtPosition(MomentForce, _rollAxis, pressurePoint, ForceMode.Force); } }
private void EvaluateForces() { Transform wingTransform = transform; Vector3 wingPosition = wingTransform.position; Vector3 windVelocity = _wind.GetWindVelocity(wingPosition); float airDensity = _wind.GetAirDensity(wingPosition); Vector3 velocity = (wingPosition - _lastPosition) / Time.deltaTime; _lastPosition = wingPosition; RelativeVelocity = velocity - windVelocity; AirSpeed = RelativeVelocity.magnitude; const float groundEffectHeight = 32f; const float groundEffectPow = 2f; float groundEffectMultiplier = 1f; RaycastHit hit; if (Physics.Raycast(_body.transform.position, Vector3.down, out hit, groundEffectHeight, _layerMask)) { groundEffectMultiplier += Mathf.Pow(Mathf.InverseLerp(groundEffectHeight, 0f, hit.distance), groundEffectPow); } UpdateStats(wingTransform); // Calculate forces per section of the wing for (int i = 0; i < _numSections; i++) { var section = _sectionStates[i]; Vector3 sectionWorldPosition = wingTransform.TransformPoint(section.LocalPosition); Vector3 sectionWorldVelocity = (sectionWorldPosition - section.LastSectionWorldPosition) / Time.fixedDeltaTime; section.LastSectionWorldPosition = sectionWorldPosition; section.RelativeVelocity = sectionWorldVelocity - windVelocity; section.AirSpeed = section.RelativeVelocity.magnitude; float dynamicPressure = 0.5f * airDensity * section.AirSpeed * section.AirSpeed * groundEffectMultiplier; section.Lift = Vector3.zero; section.Drag = Vector3.zero; section.Moment = Vector3.zero; //Vector3 localRelativeVelocity = wingTransform.InverseTransformDirection(section.RelativeVelocity); // Calculate the effects around each axis for (int j = 0; j < _components.Length; j++) { AirfoilAxisResponse coefficient = _components[j]; AirfoilAxis airfoilAxis = _axes[(int)coefficient.AxisType]; Vector3 worldAxis = wingTransform.TransformDirection(airfoilAxis.Axis); Vector3 worldAxisBase = wingTransform.TransformDirection(airfoilAxis.AxisZero); // Todo: Collapse this all into 2D calculations around the coefficient axis Vector3 projectedVelocity = section.RelativeVelocity - Vector3.Project(section.RelativeVelocity, worldAxis); section.AngleOfAttack = MathUtils.AngleAroundAxis(worldAxisBase, projectedVelocity, worldAxis); Vector3 sectionRelativeDirection = projectedVelocity.normalized; float forceCoefficient = coefficient.Coefficients.Evaluate(section.AngleOfAttack + section.Offset) * coefficient.Multiplier; float forceMagnitude = dynamicPressure * section.SurfaceArea * forceCoefficient; var input = new SectionForceInput() { body = _body, force = forceMagnitude, efficiency = _efficiency, section = section, sectionWorldPosition = sectionWorldPosition, sectionRelativeDirection = sectionRelativeDirection, worldAxis = worldAxis, worldAxisZero = worldAxisBase }; //ForceApplicators[(int) coefficient.Type](input); switch (coefficient.Type) { case ForceType.Lift: ApplyLift(input); break; case ForceType.Drag: ApplyDrag(input); break; case ForceType.Moment: ApplyMoment(input); break; } } } }
private void EvaluateForces() { UpdateBodyMatrix(); var liftPressurePoint = _bodyMatrix.MultiplyPoint(_chordPressurePoint); var dragPressurePoint = _bodyMatrix.MultiplyPoint(_chordPressurePoint); var windVelocity = _wind.GetWindVelocity(liftPressurePoint); var airDensity = _wind.GetAirDensity(liftPressurePoint); var velocity = (liftPressurePoint - _lastPosition) / _fixedClock.DeltaTime; _lastPosition = liftPressurePoint; RelativeVelocity = velocity - windVelocity; var relativeDirection = RelativeVelocity.normalized; var localRelativeVelocity = _bodyMatrix.inverse.MultiplyVector(RelativeVelocity); var pitchPlaneVelocity = new Vector2(localRelativeVelocity.z, localRelativeVelocity.y); AngleOfAttack = AngleSigned(Vector2.right, pitchPlaneVelocity.normalized); AirSpeed = pitchPlaneVelocity.magnitude; var dynamicSurfacePressure = 0.5f * airDensity * _surfaceArea * AirSpeed * AirSpeed; var worldAxis = _bodyMatrix.MultiplyVector(Vector3.right); var linearForce = Vector3.zero; DeflectionInputs[0] = 1f - Mathf.Min(1f, DeflectionInputs[1] + DeflectionInputs[2] + DeflectionInputs[3]); var c = _definition.GetInterpolated(AngleOfAttack, DeflectionInputs); var liftMagnitude = dynamicSurfacePressure * c.Lift * Mathf.Pow(_sliderFactor, 3f) * _optimalPressureFactor * _optimalPressureFactor; float aspectFactor = 1f - 1f / Mathf.Pow(_totalAspectRatio, 0.5f); // Todo: Stupid simple way to make aspect ratio matter, improve liftMagnitude *= aspectFactor; var liftDirection = Vector3.Cross(relativeDirection, worldAxis); LiftForce = liftDirection * liftMagnitude; linearForce += LiftForce; var dragMagnitude = dynamicSurfacePressure * c.Drag * SliderFactor * _optimalPressureFactor; DragForce = relativeDirection * -dragMagnitude; linearForce += DragForce; var momentMagnitude = dynamicSurfacePressure * c.Moment; MomentForce = worldAxis * momentMagnitude; if (float.IsNaN(linearForce.magnitude) || float.IsNaN(linearForce.magnitude)) { Debug.LogError("Airfoil Force is NaN " + RelativeVelocity + ", forcing quit..."); #if UNITY_EDITOR EditorApplication.isPlaying = false; #else Application.Quit(); #endif return; } if (_body) { _body.AddForceAtPosition(linearForce, liftPressurePoint); _body.AddTorqueAtPosition(MomentForce, dragPressurePoint, ForceMode.Force); } }