Skip to main content
Background Image

Whimsical Wednesday #2

Huston, we have liftoff!

·588 words·3 mins·
Table of Contents

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!

Related

Whimsical Wednesday #1
·796 words·4 mins
I’m starting a new personal game project. I want to make a cozy flying game with animal characters in the Australian Outback - relaxed but fun, simple but expandable. I’ve already started prototyping some assets in Blender.