Buckle Up!
It's Only Rocket Science
It’s 1981. A time, where the world went completely mental, if you think about it. Nasa strapping a space-aeroplane with the aerodynamics of a cinderblock to a rocket and gliding it to a teeny-tiny runway at 25 times the speed of sound would be a good example of that… you could make a game out of that.
Heads Up: This project is unfinished, and to this date, anno 2024, a frequently continued work-in-progress project. Please, take everything in here with a grain of salt.
Introcution
Idea: Summary
This project is a VR-Hybrid Co-op game, where Player One find themselves in the cockpit of a space shuttle just after re-entry. The shuttle is in a stable glide at 200.000 feet, so there’s time to breathe – but still, a lot to figure out. Luckily, there’s Player Two in mission control who can help you out how to, and where to fly. The two have to work in tandem to land the shuttle safely on the runway to beat the game.
The project will feature multiple scenarios and levels of difficulty. Ranging from the handling of the shuttle, to possible threats like weather conditions or potential loss of instrumentation.
Idea: Background
They call it a reusable Space Shuttle, a space-plane-thingy that is capable of going to space and coming back in such a manner that it could be later used to do the same thing again. Now, this concept itself sounds pretty obvious to most - you don't bring an airliner to the chop shop after landing either. But just think about how much math you need to make this work.
For one, we don't have any engines when re-entering - so we only have one single attempt to land safely. So we need to treat it like a glider-plane, which wouldn't be a problem, if the Shuttle didn't have the aerodynamics of a brick. But even so, after sticking some wings to the brick, we still need to slow down from almost 28.000 kp/h to 400. All of this only after finding, and somewhat correctly, at least, aiming for, our target runway from space.
And they pulled it off successfully. And it was only 1981. Which led me to ask myself, taking the math away from it - how can I turn a one-shot space shuttle landing attempt into a game?
Implementation
Part One: Aircraft
A solid & predictable aircraft is - guess what, this is a no-brainer - key to a game having a focus on flying an aircraft. Setting out with the vision of making a fun game, I decided to approach the implementation of the aircraft controller by creating fake aerodynamics and not a flight simulation. I think that the latter "gap" in the market has already been filled more than once anyway.
So, what do I mean with fake aerodynamics? Allow me to explain:
We all somewhat know why an airplane can fly. Even when you're not a physicist. Somehow, at some point in life, someone have told you how the wings of an airplane create a pressure difference below its wings, causing it to lift up into the air. Or, how the directional authority of the control surfaces manipulate the aircrafts flight orientation.
That's what Fake Aerodynamics are. Implementing the aircraft in such a way users would expect it to behave - if not always close to reality and designing the aircraft to "feel satisfying" to fly.
In real aerodynamics, lift and drag can be calculated from the aircrafts wing properties and angle of attack. That's what would make it realistic.
In my implementation I apply artificial forces onto the aircraft. This means, e.g. adding an artificial torque force when applying roll/yaw/pitch input, instead of comparing a control surface to the airflow around it.
Here you can see the aircraft's currently implemented functionalities:
Making flight controls for games is complicated, iffy and requires a huge amount of mathematics, regardless of its level of realism. Time for me to dust off the ole' trusty calculator.
The Aerodynamics Component is a key element to the flight behavior system, as it handles all forces, torque and drag values of the airplane and applies those onto its Rigidbody component handling the physics. This element is cruicial to the overall handling of the aircraft, as it not only takes care of orienting it relative to its moving direction and dictating the handling responsiveness relative to its speed (and other factos), but also of lift forces that allow the vehicle to fly & glide in the first place.
As the name implies, the control surfaces allow for controlled steering of the aircraft. This component dictates the responsiveness of each control surface. When used, their current setting adds torque through the aerodynamics component onto the rigidbody.
To add a layer of depth, the efficiency of Flaps (the lift-potential) decreases relative to increasing airspeed, and create more drag in the same fashion. Those potentials are editable with a modifyable graph.
No matter the type, the landing gear helps operating the aircraft on the ground. Therefore, the gear is not only made of wheels, but also an assignable steering mechanism.
If the landing gear is of type "Retractable", the component will modify the aircraft drag through the aerodynamics component.
Yes, yes, I am aware of a space shuttle not having an engine upon reentry. This feature has been implemented mostly with future use in mind. Plus it helps figuring out how the entire system works in its entirety. The Propulsion component adds thrust to the aircraft based on the setting of the "Throttle Lever" (In our case, a value of 0-1).
The aircraft traverses through a medium, air, which turns out to be quite a dynamic one in nature. There's wind, weather, subsequent precipitation, fog, and, and, and. When enabling this component, lift, drag, efficiency of control surfaces etc. will be affected on the current environmental conditions.
Something I will dive into later in this post.
I do believe that visual feedback already during functionality testing can make a powerful contribution to the overall morale and motivation of continuing a project.
For quality of life, already during early testing, I implemented a simple, dynamic Control-Surface- and Avionics-Animation behavior that rotates the desired object using the appropriate current value from the Aircraft Flight Behavior System.
A solid suspension showing the wheight and forces of the aircraft when touching down is another satisfying factor. Being a matter of a few minutes, I felt like already adding such a game-changing feature.
Since the goal of this co-op-communications game is for the player commanding the space shuttle to land safely on a runway, the first order of business is implementing a configurable aircraft flight program. The aircraft should react to direct flight control input (Elevator/Pitch-Control, Rudder/Yaw-Control, Ailerons/Roll-Control) and feature other controls to manipulate the flight path.
This section covers the aerodynamics, and how various elements can manipulate the aircrafts orientation.
Basic Aerodynamics
The aerodynamics component calculates and applies forces onto the Physics Rigidbody Component.
This enables the user to control the aircraft, while also adding 'aircrafty' behavior. To achieve this, four functions take care of the required aerodynamics-variables.
1: Calculating the Aerodynamic Effect
This function is a very simple approximation of the effect that a plane will naturally try to align itself in the direction that it's facing when moving at speed. Without this, the plane would behave a bit like the asteroids spaceship! To create this effect, the script compares the moving direction versus the facing direction of the aircraft (Vector Dot) and multiplying the outcome by itself, which results in a rolloff curve for bending velocity and looking directions:
On one hand the aircraft will bend its velocity more towards its looking direction at high forward speeds, but will also turn less towards its moving direction the smaller the difference in directions is. This is a very small detail, but means that the plane actually ends up turning downwards when stalled.
if (_aircraftRigidbody.velocity.magnitude > 0)
{
_FcgDirDifFactor = Vector3.Dot(transform.forward, _aircraftRigidbody.velocity.normalized);
_FcgDirDifFactor *= _configBaseProcessor._FcgDirDifFactor;
var turnToVelocity = Vector3.Lerp(_aircraftRigidbody.velocity, transform.forward * _ForwardSpeed, _FcgDirDifFactor * _ForwardSpeed * _AerodynamicEffect * Time.deltaTime);
_aircraftRigidbody.velocity = turnToVelocity;
_aircraftRigidbody.rotation = Quaternion.Slerp(_aircraftRigidbody.rotation, Quaternion.LookRotation(_aircraftRigidbody.velocity, transform.up), (_AerodynamicEffect / 100) * Time.deltaTime * Mathf.InverseLerp(_MaximumLiftSpeed, 0, _ForwardSpeed));
}
2: Calculating Drag
In real-life, drag is handled by a lot of different factors, coefficients and so on. The attempt here was to let Unity do most of the drag-stuff, however with some light tweaking based on simplified real-life princinples. The thinking goes as follows:
1. Drag changes with velocity.
2. Drag changes with flap setting.
3. Drag changes with atmospheric properties & altitude.
4. Drag increases when the landing gear is extended.
float currentDrag = _DragStart;
float additionalSpeedDrag = _aircraftRigidbody.velocity.magnitude * _DragOverSpeed;
currentDrag += additionalSpeedDrag;
currentDrag += (_HasAirbrakes ? _CurrentAirbrakeAmount * _AirbrakeResponse * currentDrag : 0);
currentDrag += (_LandingGearInducesDrag ? _LandingGearDrag * currentDrag : 0);
currentDrag += (_HasFlaps ?_FlapDrag: 0);
currentDrag *= (_EnvironmentAtmosphereAffectsAerodynamics ?_DragPotential: 1);
_aircraftRigidbody.drag = currentDrag;
3: Calculating Linear Forces
The linear forces are forces excerted onto the rigidbody. In other words, this is the lift that the aircraft produces when its wings move through the "air". Using the Fake Aerodynamics approach, there obviously is no air to simulate or refer to, meaning, that, instead the forward speed of the aircraft soley dictates the produced lift. In a real-world-scenario, an aircraft could technically have 0 kp/h of landspeed but still stay airborne when facing into winds fast enough for the wings to produce enough lift, provided that the engines are powerful enough. This has been caught on video here and there, even with airliners, and it looks ridiculous. Check it out!
The space shuttle, however, does not have any form of propulsion after re-entry. Therefore, this effect is rather less relevant in this case eitherway. Lift here is calculated from the forward speed, flap setting, atmosphere and altitude. The aircraft's roll angle determines the lift direction.
var appliedForces = Vector3.zero;
var liftDirection = Vector3.Cross(transform.forward * _ForwardSpeed, transform.right).normalized;
var liftOverSpeedPotential = Mathf.InverseLerp(_MaximumLiftSpeed, 0, _ForwardSpeed);
var liftPower = (Mathf.Pow(_ForwardSpeed, 2) * _Lift * liftOverSpeedPotential * _FcgDirDifFactor);
var rollLiftFactor = Mathf.InverseLerp(2, 0.5f, Mathf.Abs(_RollAngle));
var windLift = _WindLiftPotential;
liftPower += (_EnvironmentWindAffectsAerodynamics ? liftPower * windLift : 0);
liftDirection.y *= rollLiftFactor;
appliedForces += (_EnvironmentAtmosphereAffectsAerodynamics ? (_HasFlaps ? (liftPower + (liftPower * _FlapLift)) * liftDirection : liftPower * liftDirection) * _LiftPotential : (_HasFlaps ? (liftPower + _FlapLift) * liftDirection : liftPower * liftDirection));
_LiftStrength = liftPower;
_LiftDirection = liftDirection;
_Forces = appliedForces;
_aircraftRigidbody.AddForce(_Forces);
4: Calculating Torque Forces
Based on the given control input, torque forces for either the pitch, yaw and roll of the aircraft are handled by this script. Normally when banking an aircraft, the pilot would also need to "pull up" on the yoke in order for the aircraft to turn. This function partially turns the aircraft automatically when banking to make the effect a little more responsive, since that's what people expect in a video game. The aircraft should also begin yawing downwards when banking, as less lift pushes the aircraft upwards in altitude.
The total torque is multiplied by the forward speed, so the controls have more effect at high speed, and little effect at low speed, or when not moving in the direction of the nose of the plane (i.e. falling while stalled).
Again, small effects with a big impact on creating solid controls for a video game.
var rollTurn = Mathf.InverseLerp(0, 2, Mathf.Abs(_RollAngle));
var inputTorque = Vector3.zero;
var torque = Vector3.zero;
inputTorque += _CurrentSetElevatorAmount * _ElevatorResponse * transform.right;
inputTorque += _CurrentSetAileronAmount * _AileronResponse * transform.forward;
inputTorque += _CurrentSetRudderAmount * _RudderResponse * transform.up;
inputTorque += Mathf.Sin(_RollAngle) * _BankingTurnResponse * transform.up;
torque += inputTorque;
torque += (_RollAngle == 0 ? Vector3.zero : (transform.up * _RollAngle / (rollTurn * _RudderResponse * Mathf.InverseLerp(_MaximumLiftSpeed, 0, _ForwardSpeed)) / 2));
torque *= (_EnvironmentAtmosphereAffectsAerodynamics ? _ForwardSpeed * _FcgDirDifFactor * _TorquePotential : _ForwardSpeed * _FcgDirDifFactor);
_Torque = torque;
_aircraftRigidbody.AddTorque(_Torque);
Flight Controls
1: User Flight Control Input
All flight characteristics are simplified to reduce an unneccessary layer of complexity to the players, but are still based on real aviation systems. Currently, the aircraft controller program features the following:
Via a gamepad, keyboard or virtual reality input controls, the user controls the aircraft. On input, the input processor translates the given input into a numeric value, which is then used to manipulate the corresponding control surface:
public float _HIDPitchInput = Mathf.Clamp(Input.GetAxis("Pitch"), -1, 1);
public float _HIDYawInput = Mathf.Clamp(Input.GetAxis("Yaw"), -1, 1);
public float _HIDRollInput = Mathf.Clamp(Input.GetAxis("Roll"), -1, 1);
Currently the aircraft controller features two ways to either add to or reduce the aircrafts lift: Flaps and the airbrake. While the airbrakes intensity can be variable (just like for the ailerons, elevator or rudder), the flaps can be only set to a predefined set of percentage values (like in a real aircraft: Flaps 2°, 5°, 15°, 30° and so on).
public float _HIDAirbrakeInput = Mathf.Clamp(Input.GetAxis("Airbrake"), 0, 1);
public bool _HIDFlapsInput = Input.GetButtonDown("Flaps");
2: Auto-Pilot
Next to this, there is a somewhat realistic Flight Management System, which consists of three flight-automatics-systems, namely the Auto-Stabiliser [A / L], Auto-Throttle [A / T] and the infamous Auto-Pilot [A / P]. To explain the functionality in Unity for complete Flight Management System, let's begin by looking at each automatics system individually first:
2.1: Automatic Flight Commander
While all systems, [A/L], [A/P]and [A/T], are able to work simultaneously, that, obviously does not mean that this means the system know of each others' planned actions, which, in the end can end up in either a lot of chaos when managing three flight computers at the same time. To avoid this, I've invented Otto, the automatic flight commander of our space shuttle. Otto monitors and manages, if allowed, all auto-flight systems to position the aircraft as requested by the player. Does my game idea need such an elaborate system like Otto? I don't know. I just enjoy playing around with different elements here and there, to see what sticks! So far, I personally like Otto, but do not see him as a cruicial, or key feature in the future.
Let me explain Otto's inner workings:
2.2: Otto vs. Player
dddd
Item #3
Occasionally, we need to know whether the aircraft is in a certain conditional state. For example, I want the landing gear steering colum only to be moving, when the aircraft actually is grounded.
To determine the conditional state of the aircraft, I created a Flight State Evaluator behavior, which continuously monitors the aircrafts parameters and assigns it a conditional flight state. In this section, the logic for each state is explained. There are... (add which all types of states there are...)
The Aircraft is considered to be [In Flight] when it is
[1] not On Ground and
[2] not Crashed.
The Aircraft is considered to be [Landing] when it is
[1] In Flight,
[2] not In Stall,
[3] within the landing detection altitude and
[4] within the landing detection speed regimes.
The Aircraft is considered to be [Taking Off] when
[1] it is On Ground,
[2] the throttle is set to the minimum takeoff thrust and
[3] above the minimum takeoff speed regime.
The Aircraft is considered to be [In Stall] when
[1] its facing and moving direction exceeds the stall detection threshold.
The Aircraft is considered to be [On Ground] when
[1] its landing gear is extended,
[2] 50% of the landing gear wheels have contact with the ground.
[3] below the minimum takeoff speed regime.
The Aircraft is considered to be [Stationary] when
[1] it is On Ground,
[2] its forward speed is, within an error margin, close to 0 and
[3] the throttle is set to 0% thrust.
The Aircraft is considered to be [Parked] when
[1] it is Stationary and
[2] all engines are shut off.
The Aircraft is considered to be [Crashed] when it has
[1] collided with any object without touching down the landing gear.
[2] touched down while exceeding the maximum landing speed regime.
[3] touched down outside the runway.
[4] rolled out of the runway zone while On Ground.
This ruleset determines whether or not the aircraft is in a stable flight. A stable flight in aviation meants that the aircraft is being flown within its normal operational limitations. If those are being exceeded, the aircraft is considered to be instable, and therefore in an upset position. The Stability of Flight Laws are: