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 UpdateStatistics() { /* Note: Calculating position derivatives in Update caused framerate dependent effects to happen * (which is what caused the OrbitCamera to lag behind at low fps) */ /* Update altitudes. */ _altitudeSeaLevel = _body.transform.position.y; RaycastHit hit; if (Physics.Raycast(_body.transform.position, Vector3.down, out hit, Mathf.Infinity, _layerMask)) { _altitudeGround = hit.distance; //Debug.Log("I hit this thang: " + hit.collider.name); } /* Find position derivatives. */ Vector3 velocity = _body.velocity; Vector3 oldVelocity = RigidbodyState.Lerp(_historyBuffer, Time.fixedTime - 0.25f).Velocity; _acceleration = (velocity - oldVelocity) / 0.25f; Vector3 localAngularVelocity = _body.transform.InverseTransformDirection(_body.angularVelocity); _localAngularAcceleration = localAngularVelocity - _localAngularVelocity; _localAngularVelocity = localAngularVelocity; /* Determine relative wind vector. */ Vector3 windVelocity = _wind.GetWindVelocity(_body.transform.position); _relativeVelocity = _body.velocity - windVelocity; _trueAirspeed = _relativeVelocity.magnitude; /* Determine angle of attack. */ Vector3 v1 = _body.transform.TransformDirection(_rollAxis); Vector3 v2 = _relativeVelocity.normalized; Vector3 n = _body.transform.TransformDirection(_pitchAxis); _angleOfAttack = MathUtils.AngleAroundAxis(v1, v2, n); /* Determine glide ratio. */ float horizontalDistance = Mathf.Sqrt(Mathf.Pow(_body.velocity.x, 2f) + Mathf.Pow(_body.velocity.z, 2f)); float verticalDistance = -_body.velocity.y; _glideRatio = horizontalDistance / verticalDistance; /* Determine Angle to ground for local axes. */ Vector3 rollAxisCenter = _body.transform.forward; _angleToGroundRoll = MathUtils.AngleAroundAxis(Vector3.down, rollAxisCenter, _body.transform.TransformDirection(_rollAxis)); Vector3 forward = _body.transform.TransformDirection(_rollAxis); Vector3 projectedForward = new Vector3(forward.x, 0f, forward.z); _angleToGroundPitch = MathUtils.AngleAroundAxis(projectedForward, forward, _body.transform.TransformDirection(_pitchAxis)); _historyBuffer.Enqueue(RigidbodyState.ToImmutable(_body, Time.fixedTime)); }
void Update() { Vector3 windVelocity = _wind.GetWindVelocity(_transform.position); Vector3 velocity = (_transform.position - _lastPosition) / _gameClock.DeltaTime; _lastPosition = _transform.position; Vector3 relativeVelocity = velocity - windVelocity; relativeVelocity.Normalize(); _airspeed = Mathf.Lerp(_airspeed, _statistics.TrueAirspeed, 20f * _gameClock.DeltaTime); _angleOfAttack = Mathf.Lerp(_angleOfAttack, _statistics.AngleOfAttack, 20f * _gameClock.DeltaTime); _airspeedParam.setValue(_airspeed); _angleOfAttackParam.setValue(_angleOfAttack); _groundProximityParam.setValue(_statistics.AltitudeGround); }
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); } }
void OnDrawGizmos() { if (!Application.isPlaying) { return; } float horizontalStepsize = _horizontalSize / _horizontalSteps; float verticalStepsize = _verticalSize / _verticalSteps; float horizontalHalfSize = _horizontalSize * 0.5f; float verticalHalfSize = _verticalSize * 0.5f; Vector3 bottomLeft = transform.position - new Vector3(horizontalHalfSize, verticalHalfSize, horizontalHalfSize); for (int x = 0; x < _horizontalSteps; x++) { float xPos = x * horizontalStepsize; for (int y = 0; y < _verticalSteps; y++) { float yPos = y * verticalStepsize; for (int z = 0; z < _horizontalSteps; z++) { float zPos = z * horizontalStepsize; Vector3 position = bottomLeft + new Vector3(xPos, yPos, zPos); Vector3 windVector = _wind.GetWindVelocity(position) * _drawScale; Gizmos.color = Color.green; Gizmos.DrawRay(position, windVector); Gizmos.color = Color.red; Gizmos.DrawSphere(position, 0.25f); } } } }
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); } }