public static void Simulate(PlayerSimulationConfig config, IRigidbody body, PlayerInput input) { Vector3 forward = body.State.Rotation * Vector3.forward; Vector3 up = body.State.Rotation * Vector3.up; float speedFactor = Mathf.Clamp01((body.State.Velocity.magnitude - 5f) / 10f); float forwardDot = Mathf.Clamp01(1f - forward.y); float rollAngle = MathUtils.AngleAroundAxis(up, Vector3.up, forward); float rollInducedBank = Mathf.Pow(Mathf.Clamp01(Mathf.Abs(rollAngle) * MathUtils.OneOver180 * forwardDot * speedFactor), 1.5f) * Mathf.Sign(rollAngle); float rollStabilizer = Mathf.Pow(Mathf.Clamp01(Mathf.Abs(rollAngle) * MathUtils.OneOver180 * forwardDot), 2f) * Mathf.Sign(rollAngle); rollStabilizer *= 1f - Mathf.Abs(input.Roll); body.AddRelativeTorque(new Vector3( input.Pitch * config.RotationSpeed.x + Mathf.Abs(rollInducedBank) * -0.66f * config.RotationSpeed.x, input.Yaw * config.RotationSpeed.y + rollInducedBank * 0.66f * config.RotationSpeed.y, input.Roll * -config.RotationSpeed.z + rollStabilizer * 0.66f * config.RotationSpeed.z), ForceMode.Force); float maxThrust = input.Boost ? 50f : 20f; body.AddRelativeForce(Vector3.forward * maxThrust * input.Thrust, ForceMode.Force); Vector3 offHeadingVelocity = body.State.Velocity - Vector3.Project(body.State.Velocity, forward); Vector3 dampening = offHeadingVelocity * -2.0f; Vector3 conserved = offHeadingVelocity.magnitude * forward * 0.33f; body.AddForce(dampening + conserved + Vector3.down, ForceMode.Force); }
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)); }
private float GetAxis(Vector3 controllerVector, Vector3 baseVector, Vector3 rotationAxis) { const float maxAngle = 45f; float angle = MathUtils.AngleAroundAxis(controllerVector, baseVector, rotationAxis); float input = Mathf.Clamp(angle / maxAngle, -1f, 1f); input = MathUtils.ScaleQuadratically(input, 2f); return(input); }
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() { 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; } } } }