- Home /
Dodgeable homing missiles in Unity3D
Hello, I've been searching a lot for a good script for a missile. I've tried to use LookAt, but it wasn't exactly what I needed. I need a missile, that can be launched, folow the taget, but the target can dodge it, and then the missile would go on forward. I will really appreciate your help.
You could limit the missiles' turn rates, then either add a timer for how long they "track" or wait until the angle from the missile's forward direction to the player is obtuse.
Answer by Michael_BCM · Jan 09, 2018 at 06:56 PM
You can add this MonoBehaviour to an object (the missile). Then, using the Inspector, set up the tag of the object you'd like for it to target.
using UnityEngine;
/// <summary>
/// An object that will move towards an object marked with the tag 'targetTag'.
/// </summary>
public class HomingMissile : MonoBehaviour
{
/// <summary>
/// The base movement speed of the missile, in units per second.
/// </summary>
[SerializeField]
private float speed = 15;
/// <summary>
/// The base rotation speed of the missile, in radians per second.
/// </summary>
[SerializeField]
private float rotationSpeed = 1000;
/// <summary>
/// The distance at which this object stops following its target and continues on its last known trajectory.
/// </summary>
[SerializeField]
private float focusDistance = 5;
/// <summary>
/// The transform of the target object.
/// </summary>
private Transform target;
/// <summary>
/// Returns true if the object should be looking at the target.
/// </summary>
private bool isLookingAtObject = true;
/// <summary>
/// The tag of the target object.
/// </summary>
[SerializeField]
private string targetTag;
/// <summary>
/// Error message.
/// </summary>
private string enterTagPls = "Please enter the tag of the object you'd like to target, in the field 'Target Tag' in the Inspector.";
private void Start()
{
if(targetTag == "")
{
Debug.LogError(enterTagPls);
return;
}
target = GameObject.FindGameObjectWithTag(targetTag).transform;
}
private void Update()
{
if (targetTag == "")
{
Debug.LogError(enterTagPls);
return;
}
Vector3 targetDirection = target.position - transform.position;
Vector3 newDirection = Vector3.RotateTowards(transform.forward, targetDirection, rotationSpeed * Time.deltaTime, 0.0F);
transform.Translate(Vector3.forward * Time.deltaTime * speed, Space.Self);
if(Vector3.Distance(transform.position, target.position) < focusDistance)
{
isLookingAtObject = false;
}
if(isLookingAtObject)
{
transform.rotation = Quaternion.LookRotation(newDirection);
}
}
}
Here's a link to the same MonoBehaviour on GitHub.
If you'd like, I can annotate the content of Start and Update so you might get a better understanding of what it all does.
The missile follows the target, but no matter what I set rotation speed to, it always faces the player.
So to be clear, you want for the object to follow the target but not face it, and when it gets close enough to stop targeting it just continues on its current course.
I've amended the script on GitHub to reflect those changes, it's available here.
Thank you, the added feature will be useful, but now I understood, that the missile was always following the player because the model was too big. Your script works really well. But I wanted to ask a question: the rotation speed seems not to change anything for me, can you please explain what it does exactly?
Answer by OneCept-Games · Jan 09, 2018 at 04:20 PM
Compare the vectors and distance from Missile to Target. If Distance is somehow still large, then you can Lerp() the LookAt(Target) so it turns slowly against the Target. When the Distance is short, then you could compare the Angle, and if the Angle is too big, then set a Flag on the Missile and stop the LookAt() turn, so it will just continue straight ahead.
do a dot product between the missiles transform.forward and vector from missile to (toTarget = target.pos - missile.pos).Normalized; float dotAngle = Vcctor3.dot( missile.forward, toTarget); then the result is the cosine of the angle between them 1.0= looking right at target. 0 = 90 degrees -1 = looking directly away from target. So you can do a simple check for if it can 'see' the target or not now. if (dotAngle > threshold) then turn.... else go straight.
Answer by MrDropC · Jan 10, 2018 at 05:50 PM
There is a guidance technique called Proportional guidance, which has the property you're looking for; that the object in pursuit (in your case, a missile) can be made reasonably "dodgeable", yet be able to reliably hit a target if no evasive action (or too sloppy or late) was taken by that target.
To the point... Here you can find someone else's implementation written in Python, along with explaination, illustrations and a video showing the result of it all. And here are some (technical) descriptions of various different guidance techniques.
Why I recommend this as a solution for your problem... You see, with LookAt(...)
, you basically implement something called Line-of-sight guidance, which has the tendency to cause the object in pursuit to end up "chasing down" the target. If this becomes the case (and it almost always does, really), the missile must travel quite a bit faster than the target in order for it to hit it within an acceptable amount of time AND will almost always be approaching the target from behind. Result: a missile that becomes nearly impossible to dodge.
Proportional guidance is a more "efficient" way for an object such as a missile to chase a target down, in the sense that it doesn't need to travel much faster than the target for it to hit it. In fact, the missile could be just as fast as the target (or even slower, if launched some distance ahead of the target) and yet manage to hit it with reasonable odds. The basic idea is that the missile continuously adjusts its direction of flight to ensure it will eventually intercept the target -- actually not unlike how one would fire a bullet (with finite speed) ahead of a target, if that target happens to be in in motion. (Alternatively, you can think of this as the missile actively trying to "set up" a future scenario in which the missile and the target happen to arrive at the same spot at the same time.)
Note that with such a guidance technique, the missile does not necessarily need to approach the target from behind; it could be from the front, from the sides -- any direction, really. If its "turning speed" is sufficiently low, the target simply needs to abruptly "get out of the way" of the missile's path/trajectory just right before it would be intercepted by it. The missile won't be able to correct for the maneuver quickly enough, misses, and then just continue to fly past.
I wish could provide you with a ready-to-use script right now, but the one I developed for a project/prototype of mine is full of code dependencies that you'd be missing. And unfortunately I don't have the time either to write something new right now -- I just happened to stumble upon your question and figured I could at least point you to the right direction.
I hope this doesn't look too technical/advanced of an approach for solving your problem. If you really want it -- and if you'd ask me -- go for it. This particular technique can be used to produce some really good gameplay that can be made very challenging, while also never feeling "unfair". (Also, missiles that fly like this already kind of look menacing, in a way. Although maybe that's just me -- haha.)
Thank you very much for your answer, I will try to understand this, but it will be kinda hard, as I don't know Python. I thought that the only supported languages were JavaScript and C#. Well, I was wrong I guess.
No, you were not wrong! That implementation was not done in Unity. Y'know what, I can take some time and write you a brand new C# script. Just not right now, but maybe within a few hours or so from now.
Okay, I would appreciate that. Just no hurry, I won't be here for a few hours anyway.