private void RunFlightModelLinear(float deltaTime) { // Gravity can speed the plane up in a dive, or slow it in a climb. Vector3 gravityForce = Physics.gravity * Mass; // Engines provide thrust forwards. float thrust = FlightInput.Reheat ? ReheatThrust : FlightInput.Throttle * MilThrust; Vector3 thrustForce = transform.forward * thrust; // Drag holds the plane back the faster it goes, until it eventually reaches an equilibrium // between the drag force and thrust at the plane's top speed. float linearDrag = Mathf.Pow(Speed, 2f) * Drag; float totalDrag = linearDrag; // Extending things from the plane increases drag. if (Brakes.ExtendState > Mathf.Epsilon) { totalDrag += linearDrag * Brakes.DragMultiplier * Brakes.ExtendState; } if (Gear.ExtendState > Mathf.Epsilon) { totalDrag += linearDrag * Gear.DragMultiplier * Gear.ExtendState; } if (Flaps.ExtendState > Mathf.Epsilon) { totalDrag += linearDrag * Flaps.DragMultiplier * Flaps.ExtendState; } Vector3 dragForce = -transform.forward * totalDrag; // Induced drag decreases speed when turning. The higher the angle of attack, the more drag. var inducedAOA = Vector3.Angle(transform.forward, VelocityDirection); Vector3 inducedDragForce = -transform.forward * Mathf.Pow(Speed, 2f) * InducedDrag * inducedAOA; // Consider the forces only as they affect forward speed as a simplification of physics. var acceleration = (gravityForce + thrustForce + dragForce + inducedDragForce) / Mass; var forwardAccel = Vector3.Dot(transform.forward, acceleration); Speed += forwardAccel * deltaTime; // Stalling will turn the velocity vector down towards the ground. var stallAOA = Maths.Remap(DynamicStallSpeed, DynamicStallSpeed * 1.5f, StallAOA, 0f, Speed); // The direction that the velocity vector would ideally face. This includes things that // affect it such as stalling, which lowers the velocity vector towards the ground. var targetVelocityVector = transform.forward; targetVelocityVector = Vector3.RotateTowards(targetVelocityVector, Vector3.down, stallAOA * Mathf.Deg2Rad, 0f); // Change the direction of the velocity smoothly so that some alpha gets generated. VelocityDirection = SmoothDamp.Move( VelocityDirection, targetVelocityVector, Responsiveness, deltaTime); Velocity = VelocityDirection * Speed; transform.position += Velocity * Scale * deltaTime; }
private void FixedUpdate() { var targetVolume = Mathf.Lerp(MinVolume, MaxVolume, ship.Pilot.Throttle); smoothVolume = SmoothDamp.Move(audio.volume, targetVolume, 0.5f, Time.fixedDeltaTime); audio.volume = smoothVolume; var targetPitch = Mathf.Lerp(MinPitch, MaxPitch, ship.Pilot.Throttle); smoothPitch = SmoothDamp.Move(audio.pitch, targetPitch, 0.5f, Time.fixedDeltaTime); audio.pitch = smoothPitch; }
private void RunFlightModelRotations(float deltaTime) { PitchG = Maths.CalculatePitchG(transform, Velocity, PitchRate); PitchGSmoothed = SmoothDamp.Move(PitchGSmoothed, PitchG, 3f, deltaTime); // The stall speed affects low speed handling. The lower the stall speed, the more control // the plane has at low speeds. A high stall speed results in not only poor control at low // speed, but also requires more speed to generate the maximum turn rate. var controlAuthority = GetControlAuthority(); // Limit pitch input based on G. This is a reactive system. At low framerates (e.g. 10) the // sample rate will cause oscillations similar to RPMs bouncing off a rev limiter. A better // way to do this would be to pre-calculate a max turn rate based for a given G. float gLerp = PitchG > 0 ? Mathf.InverseLerp(MaxG, MaxG + MaxG * .1f, PitchG) : Mathf.InverseLerp(-MinG, -MinG - MinG * .1f, PitchG); var gLimiter = Mathf.Lerp(0f, 1f, 1f - gLerp); // For each axis, generate a rotation and then damp it to create smooth motion. var targetPitch = FlightInput.Pitch * MaxPitchRate * gLimiter * controlAuthority; PitchRate = SmoothDamp.Move(PitchRate, targetPitch, PitchResponse, deltaTime); var pitchRotation = Quaternion.AngleAxis(PitchRate * deltaTime, Vector3.right); var targetYaw = FlightInput.Yaw * MaxYawRate * controlAuthority; YawRate = SmoothDamp.Move(YawRate, targetYaw, YawResponse, deltaTime); var yawRotation = Quaternion.AngleAxis(YawRate * deltaTime, Vector3.up); var targetRoll = FlightInput.Roll * MaxRollRate * controlAuthority; RollRate = SmoothDamp.Move(RollRate, targetRoll, RollResponse, deltaTime); var rollRotation = Quaternion.AngleAxis(-RollRate * deltaTime, Vector3.forward); transform.localRotation *= pitchRotation * rollRotation * yawRotation; // When stalling, the plane pitches down towards the ground. var stallRate = GetStallRate(); if (stallRate > 0f) { // Generate stall rotation. var stallAxis = Vector3.Cross(transform.forward, Vector3.down); transform.rotation = Quaternion.AngleAxis(stallRate * deltaTime, stallAxis) * transform.rotation; } }
public void Update(Pilot pilot, ref ShipSpecs specs) { if (isDestroyed) { return; } rigidbody.AddRelativeTorque( pilot.Pitch * specs.Engine.Torque * Multiplier, pilot.Yaw * specs.Engine.Torque * Multiplier, -pilot.Roll * specs.Engine.Torque * Multiplier); // Ramp up/down speed for smoother acceleration. smoothThrottle = SmoothDamp.Move( smoothThrottle, pilot.Throttle, specs.Engine.Accel, Time.fixedDeltaTime); rigidbody.AddRelativeForce( Vector3.forward * specs.Engine.Thrust * Multiplier * smoothThrottle, ForceMode.Force); }
private void RunGroundHandling(float deltaTime) { var hitSomething = Physics.Raycast( origin: transform.position, direction: -transform.up, hitInfo: out RaycastHit hitInfo, maxDistance: GearHeight * 2f, layerMask: CollisionMask); // Panic early escape in something weird happened. // Might trigger if the player drove off a sheer cliff? if (!hitSomething) { IsGrounded = false; return; } Vector3 thrustForce = CalculateThrustForce(); Vector3 dragForce = CalculateDragForce(); Vector3 gravityForce = CalculateGravityForce(); var accelerationVector = (thrustForce + dragForce + gravityForce) / Mass; if (accelerationVector.y <= 0f) { // If the velocity vector is still pointing down, the plane is still grounded. // Care only about the speed/acceleration in the forward direction. (No slipping.) var acceleration = Vector3.Dot(transform.forward, accelerationVector); Speed += acceleration * deltaTime; // Brakes get an extra boost to being stopping because of wheel brakes. Speed = Mathf.MoveTowards(Speed, 0f, Brakes.ExtendState * 5f * deltaTime); // Stalling will turn the velocity vector down towards the ground. var stallAOA = Maths.Remap(DynamicStallSpeed, DynamicStallSpeed * 1.5f, StallAOA, 0f, Speed); // The direction that the velocity vector would ideally face. This includes things that // affect it such as stalling, which lowers the velocity vector towards the ground. var targetVelocityVector = transform.forward; targetVelocityVector = Vector3.RotateTowards(targetVelocityVector, Vector3.down, stallAOA * Mathf.Deg2Rad, 0f); if (targetVelocityVector.y < 0f) { // Velocity still isn't going up, so stay ground clamped. targetVelocityVector.y = 0f; Velocity = targetVelocityVector * Speed; VelocityDirection = targetVelocityVector; transform.position += Velocity * Scale * deltaTime; // Handle rotation. Re-uses a lot of the same code as the flying stuff. PitchG = 1f; PitchGSmoothed = 1f; // Pitching uses the same control authority as in flight to simulate aerodynamics. var controlAuthority = GetControlAuthority(); // Same pitching code as when in flight, but without the stalling rotation stuff. var targetPitch = FlightInput.Pitch * MaxPitchRate * controlAuthority; var stallRate = GetStallRate() * deltaTime; PitchRate = SmoothDamp.Move(PitchRate + stallRate, targetPitch, PitchResponse, deltaTime); LandedPitchAngle += PitchRate * deltaTime; // Prevent pitch down into the ground while grounded. The way the rotation math // works out, negative values for pitch are what result in a pitch up. LandedPitchAngle = Mathf.Clamp(LandedPitchAngle, -30f, 0f); var pitchRotation = Quaternion.AngleAxis(LandedPitchAngle, Vector3.right); // Nosewheel steering allows the plane to be turned while on the ground. // Blend roll and yaw so that roll can be used to steer on the ground like in old games. // You'd probably want this to be optional in a real game. const float NosewheelTurnRate = 45f; const float NosewheelSteeringResponse = 3f; float nosewheelSteeringYawRate = Speed >= 5f ? Mathf.InverseLerp(45f, 15f, Speed) : Mathf.InverseLerp(0f, 5f, Speed); // Allow for some yaw authority after nosewheel steering reaches zero power so that // adjustments can continue to be made at high speed. In real life, this would be // caused by the rudder rather than any kind of steering system. This specific // method has the side effect of allowing the plane to turn in place while stopped, // but I won't tell if you don't. float maxYawRate = Mathf.Max( NosewheelTurnRate * 0.1f, nosewheelSteeringYawRate * NosewheelTurnRate); var blendedYawInput = Mathf.Clamp(FlightInput.Yaw + FlightInput.Roll, -1f, 1f); var targetYaw = blendedYawInput * maxYawRate; YawRate = SmoothDamp.Move(YawRate, targetYaw, NosewheelSteeringResponse, deltaTime); var yawRotation = Quaternion.AngleAxis(YawRate * deltaTime, Vector3.up); // This SERIOUSLY breaks down if the ground beneath the player isn't flat. To fix // this requires some fancy vector math that I don't have a handle on quite yet. var flattenedForward = GetFlattenedForward(); transform.rotation = Quaternion.LookRotation(flattenedForward, hitInfo.normal); transform.localRotation *= yawRotation * pitchRotation; } else { // Acceleration vector now points skywards. Take off! IsGrounded = false; Debug.Log($"{name}: Took off!"); } } else { // The velocity vector is starting to point upwards, which means the plane wants to go // up and the plane has taken off. IsGrounded = false; } }