Tutorial | Unity3D & C# | Helicopter Controller

https://gfycat.com/ifr/TalkativeBleakCassowary

Helicopters are commonly found in lots of games these days, but I’ve noticed a real lack of good, current learning resources for those who want to try building a helicopter controller in Unity. This oft-recommended tutorial is a good starting point, but by the author’s own admission it is “inaccurate, and may no longer hold true.” As such, I thought I’d provide a more up-to-date guide for anyone who wants to build their own physics-based helicopter using Unity and C#.

This tutorial is designed to be accessible to people who have a basic familiarity with Unity and C#. I have tried to provide links to external resources wherever I think they might be useful.

Throughout each section of the tutorial, I will build classes incrementally. I’ll present a unified view of the class at the end of each section. Please feel free to copy code in this tutorial, but please don’t paste it. If you want to learn how a piece of code works, it’s always best to write it down yourself! In doing so, hopefully you’ll produce a few typos, forcing you to debug the code and maybe understand it a little better.

Here’s what we’re going to do:

  • Learn about helicopters
  • Build a simple helicopter model
  • Set up the helicopter model in Unity
  • Write a few useful classes
  • Tie everything together in a manager class

How Does a Helicopter Work?

Before we try to simulate a helicopter, let’s take a moment to understand how real helicopters function. Both rotary-wing aircraft (helicopters) and fixed-wing aircraft (airplanes) generate lift by passing air over their wings. Airplanes pass air over their wings by going forward really fast. Helicopters pass air over their “wings” (a.k.a. rotors) by spinning their wings around in a circle really fast.

Just like an airplane can change the amount of lift it generates by raising or lowering its flaps, a helicopter can change the amount of lift it generates by changing the angle of attack on each of its rotors. Additionally, a helicopter can change the speed at which the rotors are spinning.

A helicopter rotor is a big, heavy thing. As you can imagine, it takes a lot of energy to spin it. Newton’s Third Law tells us that for every action, there is an equal and opposite reaction. For helicopters, this means that when we spin the main rotor, the body of the helicopter is spun in the opposite direction with equal force. Different kinds of helicopters solve this problem in different ways, but the most common solution is to attach a second rotor, perpendicular to the main rotor, to counter the main rotor’s torque. In this video, you can see why having a counter-torque solution is so important.

A pilot has a number of inputs that he can use to control his helicopter:

  • Collective & Throttle
  • Cyclic
  • Pedals

The pilot must use all of these controls simultaneously in order to fly. The throttle controls the speed at which the rotor spins. The collective adds pitch collectively to all of the rotor blades, increasing lift. Throttle and collective are physically attached to each other, and are functionally very closely tied together. Without adequate throttle, the rotor will not spin fast enough and the blades will be unable to generate lift, regardless of the collective’s setting. Cyclic adds additional pitch to individual rotor blades, allowing the pilot to pitch and roll the entire helicopter. The pedals are used to control the speed of the counter-torque rotor. Increasing or decreasing the amount of counter-torque will cause the helicopter to yaw either left or right.

Getting Set Up

Screenshot (77)

To build a helicopter controller, we’ll first need a helicopter to control. I built this quick helicopter model in Modo but you can use any DCC app. Your model doesn’t have to be fancy-looking for now; you can always swap it out with a better-looking one later on. The only thing you need to worry about for now is that it looks sort of like a helicopter, and that it is realistically scaled. If you make a helicopter that is 20m tall or 20mm tall, things are going to behave strangely when we start setting up our physics in Unity. So make sure it’s a reasonable size.

You’ll need to export your helicopter model in three separate files since we’re going to have three independently moving pieces: the tail rotor, the main rotor, and the body. Make sure that you appropriately zero-out the transforms of each of these components before you export them. Doing so will help to ensure that each object spins around the correct point.

Once you’ve exported your three files, you can open Unity. Go ahead an drag your files into the project window and then drag them into your scene hierarchy. If you properly zeroed-out all three of your objects, you’ll need to move your rotors back into position. Also, make sure that your helicopter is facing forward in the z axis. If you don’t make sure your helicopter is facing the right direction, all of the directional stuff we’ll be doing with physics forces will be screwed up for you.

Screenshot (81)

Now that we’ve got the helicopter model all set up, let’s set up our hierarchy and our physics components. First, go ahead and parent your three objects to an empty, zeroed-out GameObject called “Helicopter.”

In Unity, the Rigidbody component is used for physics calculations, so let’s attach one to the Body of our helicopter. We’ll need to initialize it with some realistic values for mass, drag, etc. Here’s what I’m using on mine:

Screenshot (84)

For the duration of this tutorial, I strongly recommend sticking to these settings because all of the other settings (torque, force, etc.) that I mention will be specifically for these physics settings. Once you’ve completed this tutorial, you can (and will probably want to) go in and change the settings to make the helicopter feel just how you want it to.

Now, if you press play, you can watch the helicopter drop out of the sky. That’s exactly what we want. But, wait! The rotors get left behind! Let’s attach them to the Body, starting with the main rotor.

Watch the first few seconds of this video of a helicopter starting up. We can see that the rotor starts spinning very slowly at first, but picks up speed over time. We can imagine that if the engine were shut off, it would slowly lose speed until finally coming to a complete stop. We’ll be using a second Rigidbody to simulate those rotational forces. Go ahead and attach a Rigidbody component to your Main Rotor object. Because you shouldn’t parent Rigidbodies to each other in Unity, we’ll need to use a Hinge Joint to attach our Main Rotor to our Body. Here are the values with which you should initialize your Rigidbody and your Hinge Joint:

Screenshot (86)

The “Connected Anchor” values may be different for your helicopter setup, so don’t worry if they’re not exactly the same as mine. The important part is that this joint spins around the Y axis.

If you’re like me and you want to see results immediately, you can turn off “Use Gravity” on both of your Rigidbodies. Then, enable “Use Motor” on your Hinge Joint and set the “Target Velocity” and “Force” to something like 100. If you hit play, you should see your Main Rotor spinning. Notice that the Body spins also – remember Newton’s Third Law from earlier? Don’t forget to set your values back to the way they were when you finish playing around.

You probably noticed that your Tail Rotor is still not attached to your helicopter’s Body. Because Rigidbodies are kind of calculation-heavy, we’ll skip using one for the Tail Rotor. Instead, we can just write a class to spin it based on the spin-speed of the main rotor. This will give us realistic rotational motion without requiring an additional Rigidbody. Go ahead and parent your Tail Rotor to your Body in the hierarchy. Now would also be a good time to add some Colliders to your helicopter. I recommend creating an empty GameObject called “Colliders” and filling it with an empty GameObject for each Collider you want. Attach a Collider to each of those objects, move and scale it appropriately, and you’re in business. Here’s what my setup looks like – make sure yours is more or less the same for the purposes of this tutorial.

Screenshot (88)

Planning the C# Controller

Now that you have your helicopter asset all set up in Unity, we can begin writing some functionality for it. First, let’s spell out what we want to end up with.

We need to be able to:

  • Move the helicopter up and down with physics.
  • Twist the helicopter around various axes (x = pitch, y = yaw, z = roll) with physics.
  • Get input from the player to control how much we move the helicopter.

When I plan on writing some code, I try to find patterns in my requirements that will allow me to write reusable classes. The first two points both involve applying force to a Rigidbody. If we could apply a percentage of a maximum force to our Rigidbody, we would have no problem satisfying the third requirement. So let’s do it!

Creating the Workhorse Classes

First, we’ll create a class called ForceApplicator and add some fields to it. We need to attach a Rigidbody so that we can apply the forces to something. We also need to know what direction or axis (Vector3) to use when applying force. We don’t need this class to inherit from Monobehaviour because we won’t be attaching it to an object as a component. Instead, we’ll have a Helicopterclass that runs everything associated with our helicopter, and that will inherit from Monobehaviour. This is what our ForceApplicator will look like for starters.

using UnityEngine;  
using System.Collections;

public class ForceApplicator  
{
    protected Rigidbody _rigidbody;
    protected Vector3 _forceAxis;
}

We also want to have a way to define the maximum amount of force our ForceApplicator can apply. For that, we’ll use a property called MaxForce. We don’t want other classes setting the value of MaxForce, so we’ll set its permissions in the following way.

(If you’re not sure what a Property is, watch this).

public float MaxForce  
{
    get;
    protected set;
}

Next we’ll want to add a constructor method. Constructor methods are similar to the Start() method in Monobehaviour; constructor methods allow you to initialize variables and perform one-time actions when a class is first created. Ours will look like this:

public ForceApplicator(Rigidbody _newRigidbody, float _newMaxForce, Vector3 _newForceAxis)  
{
    _rigidbody = _newRigidbody;
    _forceAxis = _newForceAxis;
    MaxForce = _newMaxForce;
}

Now we can create a new force applicator from anywhere in our game using new ForceApplicator() and providing it with the appropriate parameters.

Our ForceApplicator doesn’t really do anything yet, so let’s add some functionality by including a method calledApplyForcePercentage().

Now, let’s take a step back and think about where we should go next. Sometimes we might want to apply force linearly (lift), and sometimes we might want to apply torque (pitch, roll, yaw). Because of this, it might make sense to make ApplyForcePercentage() a virtual method and then create two inheriting classes, RotationalForceApplicator and LinearForceApplicator, to override it. For now, we’ll leave ApplyForcePercentage()empty.

(If you’re not sure what Virtual Methods are, watch this).

public virtual void ApplyForcePercentage(float _percent)  
{
    // Apply forces from inherited classes.
}

Finally, we’ll want a method to calculate the force that we need to apply. We already have all the parts we need, so let’s put them together into one very simple method called CalculateForce().

protected Vector3 CalculateForce(float _percentMaxForce)  
{
    return _forceAxis * (MaxForce * _percentMaxForce);
}

If we put it all together, we get this:

using UnityEngine;  
using System.Collections;

public class ForceApplicator  
{
    protected Rigidbody _rigidbody;
    protected Vector3 _forceAxis;

    public float MaxForce
    {
        get;
        protected set;
    }

    public ForceApplicator(Rigidbody _newRigidbody, float _newMaxForce, Vector3 _newForceAxis)
    {
        _rigidbody = _newRigidbody;
        _forceAxis = _newForceAxis;
        MaxForce = _newMaxForce;
    }

    public virtual void ApplyForcePercentage(float _percent)
    {
        // Apply forces from inherited classes.
    }

    protected Vector3 CalculateForce(float _percentMaxForce)
    {
        return _forceAxis * (MaxForce * _percentMaxForce);
    }
}

Now let’s write the LinearForceApplicator class. It’ll be pretty simple, since it’s mostly just building on top of ForceApplicator. First, make sure it inherits from ForceApplicator and then add the constructor method.

If you’re not sure what inheritance is, watch this).

using UnityEngine;  
using System.Collections;

public class LinearForceApplicator : ForceApplicator  
{
    public LinearForceApplicator(Rigidbody _rigidbody, float _maxForce, Vector3 _forceAxis) : base(_rigidbody, _maxForce, _forceAxis)
    {
        // Nothin' to see here folks.
    }
}

Finally, let’s override that ApplyForcePercentage() method from earlier so it can do something useful for us.

public override void ApplyForcePercentage(float _percent)  
{
    _rigidbody.AddRelativeForce(CalculateForce(_percent));
}

Put it all together and you get this nice little class:

using UnityEngine;  
using System.Collections;

public class LinearForceApplicator : ForceApplicator  
{
    public LinearForceApplicator(Rigidbody _rigidbody, float _maxForce, Vector3 _forceAxis) : base(_rigidbody, _maxForce, _forceAxis)
    {
        // Nothin' to see here folks.
    }

    public override void ApplyForcePercentage(float _percent)
    {
        _rigidbody.AddRelativeForce(CalculateForce(_percent));
    }
}

Now let’s do the RotationalForceApplicator class. It’ll be very similar to the LinearForceApplicator, but it will ApplyRelativeTorque() instead of ApplyRelativeForce()and it will be able to tell us at what percentage of our top speed we are spinning. This will be very useful for all kinds of stuff, so let’s hop to it.

We don’t need any additional fields in RotationalForceApplicator, but we do need to make some additions to the constructor. If we want to be able to get the percent of our max rotations-per-minute (RPM), we’ll need know our maximum possible RPM. The easiest way to do that is just to set that value outright. In order to do that, we’ll supply an additional parameter to the constructor function and then set _rigidbody.maxAngularVelocity in the constructor. Take a look:

using UnityEngine;  
using System.Collections;

public class RotationalForceApplicator : ForceApplicator  
{
    public RotationalForceApplicator(Rigidbody _rigidbody, float _maxForce, float _maxRPM, Vector3 _forceAxis) : base(_rigidbody, _maxForce, _forceAxis)
    {
        // This converts _RPM to degrees/second, and then to rads/second.
        _rigidbody.maxAngularVelocity = ((_maxRPM / 60F) * 360F) / 57.29577951308F;
    }
}

Now let’s add a property called PercentMaxRPM.

public float PercentMaxRPM  
{
    get
    {
        return _rigidbody.angularVelocity.magnitude / _rigidbody.maxAngularVelocity;
    }
}

Finally, let’s override ApplyForcePercentage().

public override void ApplyForcePercentage(float _percent)  
{
    _rigidbody.AddRelativeTorque(CalculateForce(_percent));
}

All together now:

using UnityEngine;  
using System.Collections;

public class RotationalForceApplicator : ForceApplicator  
{
    public RotationalForceApplicator(Rigidbody _rigidbody, float _maxForce, float _maxRPM, Vector3 _forceAxis) : base(_rigidbody, _maxForce, _forceAxis)
    {
        // This converts _maxRPM to degrees per second, and then to radians per second.
        _rigidbody.maxAngularVelocity = ((_maxRPM / 60F) * 360F) / 57.29577951308F;
    }

    public float PercentMaxRPM
    {
        get
        {
            return _rigidbody.angularVelocity.magnitude / _rigidbody.maxAngularVelocity;
        }
    }

    public override void ApplyForcePercentage(float _percent)
    {
        _rigidbody.AddRelativeTorque(CalculateForce(_percent));
    }
}

Creating the Input Class

Before we create our Helicopter class to tie everything together, let’s first build a HelicopterControls class that we can use to get input for our helicopter. In this class, we’ll define some properties such as Pitch, Roll, and Yaw that other classes can access. We’ll then create a child class called PilotHelicopterControls that will set these properties using player input. If we wanted to, we could also create a flavor of HelicopterControls that setsPitch, Roll, and Yaw using some kind of of autopilot. As long as our Helicopter has some flavor of HelicopterControls from which to get control values, it doesn’t need to know or care how those values are set.

(If you’re not sure what polymorphism is, watch this.)

using UnityEngine;  
using System.Collections;

public class HelicopterControls  
{
    public float Pitch
    {
        get;
        protected set;
    }

    public float Roll
    {
        get;
        protected set;
    }    

    public float Yaw
    {
        get;
        protected set;
    }

    public float Collective
    {
        get;
        protected set;
    }

    public float Throttle
    {
        get;
        protected set;
    }
}

Now, let’s set those values from a new child of HelicopterControls called PilotHelicopterControls. I’m going to use a fairly standard control scheme, similar to the helicopter controls used in the Battlefield franchise. You’ll need to define your axes within Unity’s input manager for any of this to work. You can call them whatever you like.

using UnityEngine;  
using System.Collections;

public class PilotHelicopterControls : HelicopterControls  
{
    public void HandleInput()
    {
        Pitch = Input.GetAxis("R Stick V");
        Roll = Input.GetAxis("R Stick H");
        Yaw = Mathf.InverseLerp(-1F, 1F, Input.GetAxis("L Stick H"));
        Collective = Mathf.InverseLerp(-1F, 1F, Input.GetAxis("L Stick V"));
        Throttle = 1.0F;
    }
}

By default, Unity’s input axes are ranged from -1 to 1. This is fine for Pitch and Roll, but we need an actual 0-1 percentage value for Collective and Yaw. I find thatMathf.InverseLerp() is a really convenient way to remap ranges in Unity, so that’s what I’m using it for in the above code.

Spinning the Tail Rotor

Lastly, let’s create a small class to rotate the tail rotor for us.

using UnityEngine;  
using System.Collections;

public class Rotater  
{
    private Transform _transform;
    private Vector3 _rotationAxis;
    private float _maxDegreesPerSecond;

    public Rotater(Transform _newTransform, Vector3 _newRotationAxis, float _maxRPM)
    {
        _transform = _newTransform;
        _rotationAxis = _newRotationAxis;
        _maxDegreesPerSecond = (_maxRPM / 60F) * 360F;
    }

    public void Rotate(float _percentMaxSpeed)
    {
        _transform.Rotate(_rotationAxis * (Time.deltaTime * (_maxDegreesPerSecond * _percentMaxSpeed)));
    }
}

Tying Everything Together

Now we have all of the classes that we’ll need to get input from the player and apply forces to our helicopter. The hard part is done. Let’s create a Helicopter class so that we can bring them all together. We’ll grab our two Rigidbodies, as well as our Tail Rotor, the Rotater and whatever flavor of HelicopterControls class we want (probably PilotHelicopterControls). We’ll need to create a LinearForceApplicator for our lift and 5 instances of RotationalForceApplicator, one for each of the following:

  • Twisting the main rotor
  • Twisting the body (remember Newton’s 3rd law)
  • Pitching the helicopter
  • Rolling the helicopter
  • Counter-twisting the body (remember the counter-torque rotor?)

Our Helicopter class will start out looking like this:

using UnityEngine;  
using System.Collections;

public class Helicopter : Monobehaviour  
{
    [Header("Components")]
    public Rigidbody _mainRotor;
    public Rigidbody _body;
    public Transform _tailRotor;

    private LinearForceApplicator _lift;
    private RotationalForceApplicator _mainRotorTorque;
    private RotationalForceApplicator _bodyTorque;
    private RotationalForceApplicator _bodyCounterTorque;
    private RotationalForceApplicator _pitchTorque;
    private RotationalForceApplicator _rollTorque;
    private HelicopterControls _controls;
    private Rotater _tailRotorRotater;

    void Start()
    {
        _lift = new LinearForceApplicator(_body, 1000062, Vector3.up);
        _mainRotorTorque = new RotationalForceApplicator(_mainRotor, 3, 240, Vector3.down);
        _bodyTorque = new RotationalForceApplicator(_body, 25000, 120, Vector3.down);
        _bodyCounterTorque = new RotationalForceApplicator(_body, 50000, 120, Vector3.up);
        _pitchTorque = new RotationalForceApplicator(_body, 20000, 100, Vector3.right);
        _rollTorque = new RotationalForceApplicator(_body, 15000, 100, Vector3.back);
        _controls = new PilotHelicopterControls();
        _tailRotorRotater = new Rotater(_tailRotor, Vector3.Right, 500);

        // This should account for any weird wobbling caused by misaligned pivots.
        _body.centerOfMass = Vector3.zero.
    }
}

In Update(), we simply HandleInput() on our controls.

void Update()  
{
    _controls.HandleInput();
}

For all of our physics work, we need to use FixedUpdate(). First, we’ll use our _controls.Throttle to apply torque to the main rotor. Then we’ll need to apply torque in the opposite direction to the body of the helicopter. Rather than apply a constant amount of torque to the body, we want to scale the amount of torque we apply by how fast the rotor is spinning. We can use our handy RotationalForceApplicator.PercentMaxRPM property for this.

void FixedUpdate()  
{
    // Throttle
    _mainRotorTorque.ApplyForcePercentage(_controls.Throttle);
    _bodyTorque.ApplyForcePercentage(_mainRotorTorque.PercentMaxRPM);
}

Now we can add our code for Yaw. First, let’s make the tail rotor spin. Then, let’s apply our counter-torque to the helicopter Body. Don’t forget to scale your counter-torque by Yaw.

// Yaw
_tailRotorRotater.Rotate(_mainRotorTorque.PercentMaxRPM);  
_bodyCounterTorque.ApplyForcePercentage(_mainRotorTorque.PercentMaxRPM * _controls.Yaw);  

Collective, Pitch, and Roll will be very similar to what we’ve already done:

// Collective
_lift.ApplyForcePercentage(_mainRotorTorque.PercentMaxRPM * _controls.Collective);

// Cyclic
_pitchTorque.ApplyForcePercentage(_mainRotorTorque.PercentMaxRPM * _controls.Pitch);  
_rollTorque.ApplyForcePercentage(_mainRotorTorque.PercentMaxRPM * _controls.Roll);  

And that’s all there is to it! Here’s what our whole Helicopter class looks like:

using UnityEngine;  
using System.Collections;

public class Helicopter : Monobehaviour  
{
    [Header("Components")]
    public Rigidbody _mainRotor;
    public Rigidbody _body;
    public Transform _tailRotor;

    private LinearForceApplicator _lift;
    private RotationalForceApplicator _mainRotorTorque;
    private RotationalForceApplicator _bodyTorque;
    private RotationalForceApplicator _bodyCounterTorque;
    private RotationalForceApplicator _pitchTorque;
    private RotationalForceApplicator _rollTorque;
    private HelicopterControls _controls;
    private Rotater _tailRotorRotater;

    void Start()
    {
        _lift = new LinearForceApplicator(_body, 1000062, Vector3.up);
        _mainRotorTorque = new RotationalForceApplicator(_mainRotor, 3, 240, Vector3.down);
        _bodyTorque = new RotationalForceApplicator(_body, 25000, 120, Vector3.down);
        _bodyCounterTorque = new RotationalForceApplicator(_body, 50000, 120, Vector3.up);
        _pitchTorque = new RotationalForceApplicator(_body, 20000, 100, Vector3.right);
        _rollTorque = new RotationalForceApplicator(_body, 15000, 100, Vector3.back);
        _controls = new PilotHelicopterControls();
        _tailRotorRotater = new Rotater(_tailRotor, Vector3.Right, 500);

        // This should account for any weird wobbling caused by misaligned pivots.
        _body.centerOfMass = Vector3.zero.
    }

    void Update()
    {
        _controls.HandleInput();
    }

    void FixedUpdate()
    {
        // Throttle
        _mainRotorTorque.ApplyForcePercentage(_controls.Throttle);
        _bodyTorque.ApplyForcePercentage(_mainRotorTorque.PercentMaxRPM);

        // Yaw
        _tailRotorRotater.Rotate(_mainRotorTorque.PercentMaxRPM);
        _bodyCounterTorque.ApplyForcePercentage(_mainRotorTorque.PercentMaxRPM * _controls.Yaw);

        // Collective
        _lift.ApplyForcePercentage(_mainRotorTorque.PercentMaxRPM * _controls.Collective);

        // Cyclic
        _pitchTorque.ApplyForcePercentage(_mainRotorTorque.PercentMaxRPM * _controls.Pitch);
        _rollTorque.ApplyForcePercentage(_mainRotorTorque.PercentMaxRPM * _controls.Roll);
    }
}

Wrapping Up

Now all you need to do is drop your Helicoptercomponent onto your Helicopter GameObject, drag in the required components, and hit play! You should be flying around with no problem.

I recommend parenting your MainCamera to your Body so that you can see where you’re going a little easier. You might also like to write a simple camera controller to make things look a little better. Throw in a few environmental props, and you’re in business!

I hope you’ve enjoyed this tutorial and learned a few things along the way. If you have any questions, feel free to hit me up on Twitter or send me a message on Reddit.

 
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s