How to make a homing missile in Unity3d

Have you ever wondered how to make a homing missile in Unity3d? In this short tutorial, I will show you how I approached this problem in my game.

To give you a bit of context, here’s what the end effect will look:

As you can see, the missile passes by the obstacle and then turns. I didn’t want this behaviour to be too perfect, it’s a homing missile after all, not an AI-driven drone, so if you were to put several obstacles close to each other, you would find that the missile hits the second one. And that’s perfectly fine (at least in my game πŸ™‚ ).

The algorithm consists of only two methods: Update and FixedUpdate. The first one is used to calculate the missile rotation and to move it, and the second one is used to calculate waypoints – points in space the missile should temporarily aim to reach, in case there’s an obstacle on the way to the actual target. The first method looks like this:

public void Update() {
    if (_target == null) {
        return;
    }

    Vector3 currentGoal;

    if (!_passedWaypoint && _waypoint != default) {
        currentGoal = _waypoint;

        if (Vector3.Distance(transform.position, _waypoint) < 2) {
            _passedWaypoint = true;
            _waypoint = default;
        }
    } else {
        var targetCenter = _target.GetGeometricalCenter();

        currentGoal = targetCenter;
    }

    var direction = (currentGoal - transform.position).normalized;

    transform.rotation = Quaternion.Slerp(
        transform.rotation,
        Quaternion.LookRotation(direction),
        _angularSpeed * Time.deltaTime);
    transform.Translate(0, 0, _speed * Time.deltaTime);
}

Starting from the top:

  1. If the missile has no target, then we don’t perform any action.
  2. If we haven’t already passed the waypoint and the waypoint variable is initialized to a certain value, then we need to set the current goal to the waypoint position, so that the missile takes a proper turn. If the distance to the waypoint is small enough, we can consider it passed, set the _passedWaypoint flag accordingly and reset the _waypoint variable.
  3. If the above is not fulfilled, we set the current goal to the geometrical center of the target. Why am I doing this? Well, because my model happened to be anchored at the bottom. Imagine if it were a person – in this case the homing missile would always target that person’s feet. Is this a foot fetish, or what? πŸ™‚
  4. Next, we calculate the direction in which the missile would move and set its rotation in relation to the current goal (waypoint, or the actual target). After that we move the object using the transform.Translate method.

Btw. this fancy looking GetGeometricalCenter extension method looks like this:

transform.GetComponent<Collider>().bounds.center;

Not so fancy after all, eh? πŸ™‚

The actual fanciness happens in the FixedUpdate method, because it calculate the alternative target – the waypoint the missile has to go to in order to avoid hitting the obstacle:

public void FixedUpdate() {
    if (_target == null) {
        return;
    }

    const int CheckRange = 10;
    var isHit = Physics.Raycast(transform.position, transform.forward, out var hitInfo, CheckRange);

    if (isHit && hitInfo.collider.transform == _target || !isHit) {
        return;
    }

    var tiltFactor = transform.lossyScale.x;
    var sign = hitInfo.point.x > hitInfo.collider.transform.position.x ? 1 : -1;
    var xBound = sign > 0 ? 
        hitInfo.collider.transform.position.x + hitInfo.collider.transform.lossyScale.x / 2 + tiltFactor :
        hitInfo.collider.transform.position.x - hitInfo.collider.transform.lossyScale.x / 2 - tiltFactor;
    var waypoint = new Vector3(xBound, hitInfo.point.y, hitInfo.point.z);

    _passedWaypoint = false;
    _waypoint = waypoint;
}

Again, starting from the top:

  1. If the missile has no target, then don’t perform any action.
  2. We perform a hit check in a range of 10 units. If nothing was hit, return from the method, because no more calculations should be performed here.
  3. There’s no reason to break the explanation of each line of the calculations down into separate points, so I’m going to give it as a single point. This is where the waypoint calculation happens. We check where the raycast has hit the obstacle and then calculate if that hit happened closer to its left edge or the right one (in other words, the missile is trying to make the smallest possible turns on its way to the actual target). Then we add a tiltFactor to the equation in order to put the waypoint outside the bounds of the obstacle in the way.

And this is how we make an ordinary missile game object a homing missile. You can find the full version of the code under the link below:

https://github.com/mostlyunity/homing-missile

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 )

Google photo

You are commenting using your Google 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 )

Connecting to %s