Swarm Missile - Adding random movement to a Homing Missile

Language: C#

Goal: To create a winding swarm missile that moves erratically but smoothly to its target.

Example: Swarm Missile in Warhawk

Progress So Far: I have created a regular homing missile using Vector3 math.

Need: I need to be able to add smooth motion to the missiles so they follow a smooth but erratic path to the target.

Code So Far:

	void FixedUpdate () 
	{
		//first we need to check that we have found a Hazard to target at all.  If not, the missile flys straight in the direction its facing
		if (closestTarget == false) rigidbody.velocity = transform.forward * missileSpeed;

		//if we do have a target selected....
		if (closestTarget == true)
		{
			//this rotates the missile in the direction of its target
			transform.LookAt(closestTarget.transform.position);

			//this moves the missile from its current position to its target's position at a speed we specify in the inspector
			transform.position = Vector3.MoveTowards(transform.position, closestTarget.transform.position, Time.deltaTime * missileSpeed);
		}
	}

You could use the perlin noise function rotate a bit.

Docs are here: Unity - Scripting API: Mathf.PerlinNoise

Basically, perlin noise is like a ‘smooth’ random variable. You give a 2d value (although that can just be a single value, with 0 as the 2nd component, and it returns you a random number (just like Mathf.rand). However perlin noise doesn’t jump around - a subtle change to the value you give it will result in a subtle change in the random number returned.

You could do something along the lines of:

//do this after the 'look at' call

//generate a 2d smooth random number using the position
float noiseyness = 1; //tweak this to adjust how random the motion is

//calculate random number using x and y position
float rand = Mathf.PerlinNoise(transform.position.x*noiseyness ,transform.position.y*noiseyness );

//if you're in 3d, you'll need to do it again to take into account the z position
rand = Mathf.PerlinNoise(rand,transform.position.z*noiseyness );

//pick a random angle from -30 to 30 degrees
float rand_angle = Mathf.Lerp(rand, -30, 30);

//now rotate the object around its own x axis to point it up or down a bit
transform.rotation = transform.rotation * Quaternion.AngleAxis(rand_angle, transform.right);

//we could do the same again to rotate around our own y axis as well. we'd probably negate the positions passed in or something to get different random numbers though!

That’s just 1 example of how to use perlin noise to randomly generate a value. This would randomly adjust your object’s rotation a bit over time (although your direct setting of the position would have to go - but that should be fine if you just move that setting of rigid body velocity to the bottom of FixedUpdate and do it every frame).

Of course, you might want to reduce the amount the angle changes as the missle homes in on its target. Or maybe if you want something more like a regular wave, you could use Mathf.Sin or Mathf.Cos instead of perlin noise. Maybe float rand = Mathf.Cos(transform.position.length*noiseyness).

I came up with a solution that works as intended and is noticeably fewer lines of code. The hit on Frames per second was non existent. Can someone double check for the efficiency of the code in terms of processing overhead?

void Update()
	{

		//defines a new random float for x,y,z every frame only if the time specfied has elapsed.  Creates random changes in x, y, z, variables at random times.
		if (Time.time > timeRandom)
		{
			float xRandom = Random.Range(-xRandom, xRandom);
			float yRandom = Random.Range(-yRandom, yRandom);
			float zRandom = Random.Range(0, zRandom);
			float timeRandom = Time.time + timeRandom;
		}
	}

	//now to control the actual missiles trajectory.  Since we are using rigidbodies and physics to move our missile, we need to use FixedUpdate.  This gives us a constant update unrestriced by framerate.
	void FixedUpdate () 
	{
		if (weaponGraphic == null)
		{
			smokeTrail.emissionRate = 0.0f;
			Destroy(gameObject, smokeTrail.duration);
		}

		GameObject player = GameObject.Find("Player");

		//first we need to check that we have found a Hazard to target at all.  If not, the missile flys straight in the direction its facing
		if (closestTarget == false) rigidbody.velocity = transform.forward * player.GetComponent<PlayerWeaponFire>().secondarySpeed;

		//if we do have a target selected....
		if (closestTarget == true)
		{
			//Moves the missile to its target
			transform.position = Vector3.MoveTowards(transform.position, closestTarget.transform.position, Time.deltaTime * player.GetComponent<PlayerWeaponFire>().secondarySpeed);
		
			//Adds randomized trajectory
			transform.Translate(xRandom, yRandom, zRandom, closestTarget.transform);

			//keeps the missile looking at its target
			transform.LookAt(closestTarget.transform.position);
		}
	}

I can think of several ways of winding a missile to a target. One thing about your current code that may or may not be important is that the missile never misses. Here is a bit of code that creates an randomized arching path to the target. It also never misses. It’s not the complete winding path seen in the video, but I believe a number of these launched at approximately the same time with similar speed will give a nice, organic swarm visual:

using UnityEngine;
using System.Collections;

public class WindingMissile2 : MonoBehaviour {

	public Transform target;
	public float speed = 6.0f;

	private float time;
	private float timer = 0.0f;

	private float xPow;
	private float yPow;
	private float zPow; 
	private Vector3 prevPos;
	private Vector3 startPos;
	private Transform trans;

	void Start () {
		xPow = Random.Range (0.4f, 3.0f);
		yPow = Random.Range (0.4f, 3.0f);
		zPow = Random.Range (0.4f, 3.0f);

		trans = transform;
		prevPos = trans.position;
		startPos = transform.position;
		time = (target.position - trans.position).magnitude / speed;
	}
	
	void Update () {
		Vector3 v3 = startPos;
		v3.x = Mathf.Lerp (v3.x, target.position.x, Mathf.Pow (timer/time, xPow));
		v3.y = Mathf.Lerp (v3.y, target.position.y, Mathf.Pow (timer/time, yPow));
		v3.z = Mathf.Lerp (v3.z, target.position.z, Mathf.Pow (timer/time, zPow));
		trans.position = v3;
		timer += Time.deltaTime;

		if (trans.position != prevPos) {
			trans.rotation = Quaternion.LookRotation (trans.position - prevPos);
		}

		prevPos = trans.position;

		if (timer > time) {
			Destroy(gameObject);
		}
	}
}