Back to Blog

Basic Helicopter Control System in Unity

Enes Efe Tokta

Enes Efe Tokta

May 1, 2024 • 9 min read

Unity Tutorial Physics C#

Many helicopter games exist today. The mere mention of helicopters can intimidate people. Issues like heavy physics controls can scare game developers. Actually, it's not that difficult, but this depends on the features and realism of the helicopter control system you want to create.

If you want to create a realistic helicopter system, you need to dedicate a significant amount of time. However, creating a system that is not realistic but has basic helicopter features is quite easy. Basic helicopter systems are generally not physics-based.

About This Tutorial

I will explain to you, valuable developers, step-by-step, as best as I can, how to create a basic helicopter control system. In the helicopter control system we will create, the helicopter's movements will be based on physics using Unity's Rigidbody component.

Basic Helicopter Features

Let's start by explaining the basic features a helicopter should have. These basic movements are actually similar to basic character control features:

  • Ascend
  • Descend
  • Forward
  • Backward
  • Turn right
  • Turn left
  • Move right
  • Move left

Control Mapping

In the helicopter control system we will build, the properties assigned to each feature are as follows:

Action Key Binding
Ascend ⬆️ Left Shift
Descend ⬇️ Left Alt
Forward ⬆️ W
Backward ⬇️ S
Turn Right ↪️ Q
Turn Left ↩️ E
Move Right ➡️ A
Move Left ⬅️ D

I wanted to use the old Unity Input system for this. However, our system will work completely smoothly with the new Input system as well.

Understanding Helicopter Physics

When helicopters move, they normally tilt in that direction. For example:

  • When moving forward, they make a 15-degree clockwise tilt
  • When moving backward, they make a 15-degree counterclockwise tilt
  • Similarly, when turning right and left, they make a 15-degree turn

Of course, when turning, the helicopter no longer stays at 15 degrees. When the movement is finished, it needs to return to its original rotation value. This is the basic logic of a helicopter.

Setting Up the Component

First, let's add our Rigidbody component to our helicopter object. I want to do this code-wise, not through the Inspector. I'm using the RequireComponent attribute for this:

[RequireComponent(typeof(Rigidbody))]
public class Helicopter : MonoBehaviour
{
    // Class code here
}

This way, our Rigidbody object will be automatically added to every object containing our Helicopter.cs code. Also, when you download an object from GitHub, the Rigidbody will be added directly.

Defining Variables

I wanted to divide the variables into four groups: Force, Rotation Speed, Angle, and Propeller. I used [SerializeField] for better organization and to make the code more readable:

// Force Variables
[Header("Forces")]
[SerializeField] private float engineForce = 10f;
[SerializeField] private float altitudeChangeForce = 5f;

// Rotation Speed Variables
[Header("Rotation Speeds")]
[SerializeField] private float rotateSpeed = 50f;
[SerializeField] private float anglesSpeed = 2f;

// Angle Variables
[Header("Tilt Angles")]
[SerializeField] private float maxTiltAngle = 30f;

// Propeller Variables
[Header("Propellers")]
[SerializeField] private Transform propeller;
[SerializeField] private Transform propellerTail;
[SerializeField] private float propellerSpeed = 500f;

// Rigidbody
private Rigidbody rb;

Variable Explanations:

  • engineForce and altitudeChangeForce - Determine the speed of movement
  • rotateSpeed and anglesSpeed - Control rotation speeds
  • maxTiltAngle - Maximum angle for maneuvers (I set it to 30 degrees)
  • propeller and propellerTail - References to propeller transforms
  • rb - Reference to the Rigidbody component

Actually, the presence or absence of propellers doesn't affect the operation of our system, but what truly makes a helicopter a helicopter, different from airplanes, is its propellers!

Initialization

We didn't write much code in the Start method. We only used it to access our Rigidbody component and disable gravity:

void Start()
{
    rb = GetComponent();
    rb.useGravity = false;
}

We're turning off the force of gravity to keep the helicopter airborne. Otherwise, the helicopter would fall if we release our fingers from the keys.

Physics Update Loop

We use FixedUpdate instead of Update because Unity recommends using FixedUpdate for physics operations. First, let's gather our inputs:

void FixedUpdate()
{
    // Get inputs
    float verticalInput = Input.GetAxis("Vertical");      // W/S
    float horizontalInput = Input.GetAxis("Horizontal");  // A/D
    float altitudeInput = Input.GetAxis("Altitude");      // Left Shift/Alt
    float rotationInput = Input.GetAxis("Rotation");      // Q/E
    
    // Process movement and rotation
    Move(verticalInput, horizontalInput, altitudeInput);
    Rotate(rotationInput);
    Tilt(verticalInput, horizontalInput);
    RotatePropellers();
}

Input Manager Setup

For altitudeInput and rotationInput, special settings need to be made in Unity → Edit → Project Settings → Input Manager:

Altitude Input:

  • Name: "Altitude"
  • Negative Button: "left alt"
  • Positive Button: "left shift"

Rotation Input:

  • Name: "Rotation"
  • Negative Button: "e"
  • Positive Button: "q"

Movement Implementation

Now let's define our movement vectors and apply forces:

void Move(float vertical, float horizontal, float altitude)
{
    // Forward/Backward movement
    Vector3 forwardForce = transform.forward * vertical * engineForce * Time.deltaTime;
    
    // Right/Left movement
    Vector3 sidewaysForce = transform.right * horizontal * engineForce * Time.deltaTime;
    
    // Altitude control
    Vector3 altitudeForce = transform.up * altitude * altitudeChangeForce * Time.deltaTime;
    
    // Apply all forces
    rb.AddForce(altitudeForce);
    rb.AddForce(forwardForce + sidewaysForce);
}

Rotation and Tilting

Now we come to the turns that will make our helicopter resemble a real helicopter. The tilting creates realistic-looking movement:

void Rotate(float rotationInput)
{
    // Rotate helicopter around Y-axis
    float rotation = rotationInput * rotateSpeed * Time.deltaTime;
    transform.Rotate(Vector3.up, rotation);
}

void Tilt(float vertical, float horizontal)
{
    // Calculate tilt angles based on input
    float WS_TiltAngle = -vertical * maxTiltAngle;  // Forward/back tilt
    float AD_TiltAngle = horizontal * maxTiltAngle;  // Side tilt
    
    // Create target rotation
    Quaternion targetRotation = Quaternion.Euler(WS_TiltAngle, transform.eulerAngles.y, AD_TiltAngle);
    
    // Smoothly interpolate to target rotation
    transform.rotation = Quaternion.Lerp(transform.rotation, targetRotation, anglesSpeed * Time.deltaTime);
}

How Tilting Works:

  1. Calculate the desired tilt angle based on input (multiplied by maxTiltAngle)
  2. Create a target rotation quaternion
  3. Use Quaternion.Lerp to smoothly transition to the target rotation
  4. When input stops, the helicopter naturally returns to level position

Propeller Rotation

With these two lines of code, we can rotate our main rotor and tail rotor:

void RotatePropellers()
{
    if (propeller != null)
        propeller.Rotate(Vector3.up * propellerSpeed * Time.deltaTime);
        
    if (propellerTail != null)
        propellerTail.Rotate(Vector3.right * propellerSpeed * Time.deltaTime);
}

Actually, you could also rotate them with animation, although it wouldn't be very efficient in my opinion.

Complete Code

Full Implementation

Here's the complete helicopter control script ready to use:

using UnityEngine;

[RequireComponent(typeof(Rigidbody))]
public class Helicopter : MonoBehaviour
{
    [Header("Forces")]
    [SerializeField] private float engineForce = 10f;
    [SerializeField] private float altitudeChangeForce = 5f;

    [Header("Rotation Speeds")]
    [SerializeField] private float rotateSpeed = 50f;
    [SerializeField] private float anglesSpeed = 2f;

    [Header("Tilt Angles")]
    [SerializeField] private float maxTiltAngle = 30f;

    [Header("Propellers")]
    [SerializeField] private Transform propeller;
    [SerializeField] private Transform propellerTail;
    [SerializeField] private float propellerSpeed = 500f;

    private Rigidbody rb;

    void Start()
    {
        rb = GetComponent();
        rb.useGravity = false;
    }

    void FixedUpdate()
    {
        float verticalInput = Input.GetAxis("Vertical");
        float horizontalInput = Input.GetAxis("Horizontal");
        float altitudeInput = Input.GetAxis("Altitude");
        float rotationInput = Input.GetAxis("Rotation");

        Move(verticalInput, horizontalInput, altitudeInput);
        Rotate(rotationInput);
        Tilt(verticalInput, horizontalInput);
        RotatePropellers();
    }

    void Move(float vertical, float horizontal, float altitude)
    {
        Vector3 forwardForce = transform.forward * vertical * engineForce * Time.deltaTime;
        Vector3 sidewaysForce = transform.right * horizontal * engineForce * Time.deltaTime;
        Vector3 altitudeForce = transform.up * altitude * altitudeChangeForce * Time.deltaTime;

        rb.AddForce(altitudeForce);
        rb.AddForce(forwardForce + sidewaysForce);
    }

    void Rotate(float rotationInput)
    {
        float rotation = rotationInput * rotateSpeed * Time.deltaTime;
        transform.Rotate(Vector3.up, rotation);
    }

    void Tilt(float vertical, float horizontal)
    {
        float WS_TiltAngle = -vertical * maxTiltAngle;
        float AD_TiltAngle = horizontal * maxTiltAngle;

        Quaternion targetRotation = Quaternion.Euler(WS_TiltAngle, transform.eulerAngles.y, AD_TiltAngle);
        transform.rotation = Quaternion.Lerp(transform.rotation, targetRotation, anglesSpeed * Time.deltaTime);
    }

    void RotatePropellers()
    {
        if (propeller != null)
            propeller.Rotate(Vector3.up * propellerSpeed * Time.deltaTime);
            
        if (propellerTail != null)
            propellerTail.Rotate(Vector3.right * propellerSpeed * Time.deltaTime);
    }
}

Tips and Customization

Enhancement Ideas

  • Add drag: Set Rigidbody drag values for more realistic deceleration
  • Camera shake: Add screen shake when the helicopter moves
  • Sound effects: Include propeller and engine sounds
  • Particle effects: Add dust or smoke when near the ground
  • Damage system: Implement health and collision damage
  • Fuel system: Add limited fuel requiring refueling

Conclusion

Congratulations! You now have a basic helicopter control system. This is a foundation that you can build upon and customize to suit your own game's needs.

I want to tell you, valuable developers, that this is a basic helicopter control system I've created. You can add or remove features to it to suit your own system. I just wanted to explain the basic logic.

If you want to download the complete project, you can check my GitHub. Happy coding!