Today’s Goal #
On this whimsical Wednesday, I set out with one simple ambition: to end the day with a plane in Unity that could… well, more or less fly. From past experiments, I knew this wouldn’t be as easy as it sounds, but isn’t that part of the fun?
The hardest part for every new project #
Alright, let’s get started. First, I created a new Unity project. Unity 6 felt like a solid choice - since it’s the current LTS. The Universal Render Pipeline should be more than enough and will give me more freedom later on to use other build targets. Now, the project name… oh boy. This is always the trickiest part. I could just call it “ThisNewPlaneProject”, but that feels uninspired. I wanted something relaxing, something dreamy… maybe even whimsical? To highlight the flying aspect, I should add something else: Whimsical-Sky! Yep, I think I can work with that for now.
First taste of flight (sort of) #
Next, the fun part: I imported my assets into unity and gave my plane a Rigidbody. At this point, the plane still couldn’t fly - but it could do something equally exciting… fall.
But, falling is just the beginning. The real goal: flight. I decided to start simple. I added a PlaneBehaviour script and experimented with forces:
// Apply Thrust Force based on the ThrottleInput
private void ApplyThrust()
{
var enginePower = _power * ThrottleInput;
rb.AddForce(transform.forward * enginePower, ForceMode.Force);
}
// Apply Pitch Torque based on the PitchInput
private void ApplyPitch()
{
float pitchTorque = PitchInput * pitchAuthority * Lift;
rb.AddRelativeTorque(pitchTorque ,0, 0, ForceMode.Force);
}
// Apply Yaw Torque based on the YawInput
private void ApplyYaw()
{
float yawTorque = YawInput * yawAuthority;
rb.AddRelativeTorque(0, yawTorque, 0, ForceMode.Force);
}
// Apply Roll Torque based on RollInput and self level forces
private void ApplyRoll()
{
// Get the current roll angle
float roll = transform.localEulerAngles.z;
if (roll > 180f) roll -= 360f;
float selfLevel = -roll * selfLeveling * Lift;
float inputLevel = -RollInput * Lift * rollAuthority;
float rollTorque = inputLevel + selfLevel;
rb.AddRelativeTorque(0, 0, rollTorque, ForceMode.Force);
}
For lift, I kept things simple. I just multiplied the plane’s forward speed by a lift factor:
private void CalculateLift()
{
if (maxSpeed <= 0) // Prevent divide by zero
maxSpeed = 0.0001f;
float relativeSpeed = Mathf.Abs(Airspeed) / maxSpeed;
Lift = liftCurve.Evaluate(relativeSpeed) * liftMultiplier;
}
Surprisingly, this naive approach worked. My plane moved, wobbled, and most importantly, it started feeling like a real plane.
To test it out, I quickly slapped together some runway tiles and loaded a random heightmap for the terrain. And just like that, the adventure was underway.
What goes up #
To wrap things up - and because my crude flight physics often led to… let’s say “unplanned landings” - I added a new component. This little script checks if the plane crashes and gently detaches any loose objects.
void FixedUpdate()
{
var difference = lastSpeed - plane.Airspeed;
if (difference > 5)
OnCrash();
lastSpeed = plane.Airspeed;
}
private void OnCrash()
{
var collider = GetComponentsInChildren<Collider>();
var direction = transform.forward;
foreach (var coll in collider) // Go through all collider
{
// Detach it from the parent
coll.transform.parent = null;
// Add a Rigidbody to the object
var rb = coll.gameObject.AddComponent<Rigidbody>();
//Keep the planes velocity
rb.linearVelocity = direction * lastSpeed;
}
}
And that’s it for this Whimsical Wednesday…
Okay, BYE!