Language: C#
Goal:
To create swarming homing missiles that target the closest Hazards (asteroids, debris, etc)
Problem:
Right now all my missiles only target the single closest hazard. When that hazard is destroyed, the remaining missiles all swarm to the next closest hazard. This is not the desired behavior.
video example
Swarm Missile Demo
My Need:
I need each missile fired to have unique targets based on closest position. If the first missile targets the closet hazard, the second missile must target the NEXT closest hazard. The third missile must target the third closest hazard. etc…
Current Progress:
I have an empty game object with a script that creates a dynamic list that grows and shrinks as hazards enter the screen. The same script also sets the missile’s target to the closest hazard. A script attached to the missile prefab takes this target and directs the missile towards it.
Code Attached to SpawnController gameobject:
using UnityEngine;
using System.Collections;
public class TargetAssign : MonoBehaviour
{
public Vector3 playerPosition;
public Vector3 distanceDifference;
public float oldDistance = Mathf.Infinity;
public float currentDistance;
public GameObject target;
public GameObject closest;
public GameObject[] HazardArray;
void Update ()
{
playerPosition = GameObject.Find ("Player").transform.position;
HazardArray = GameObject.FindGameObjectsWithTag("Hazard");
if (HazardArray.Length == 0) return;
foreach (GameObject hazard in HazardArray)
{
distanceDifference = hazard.transform.position - playerPosition;
currentDistance = distanceDifference.sqrMagnitude;
if (currentDistance < oldDistance)
{
closest = hazard;
target = closest;
oldDistance = currentDistance;
}
}
oldDistance = Mathf.Infinity;
}
}
Code Attached to the Missile Prefab:
using UnityEngine;
using System.Collections;
public class HomingMissle : MonoBehaviour
{
public float speed;
public GameObject missileTarget;
public GameObject explosion;
public GameObject[] HazardArray;
void Start ()
{
GameObject targetAssignment = GameObject.Find("SpawnController");
missileTarget = targetAssignment.GetComponent<TargetAssign>().target;
}
void Update()
{
GameObject targetAssignment = GameObject.Find("SpawnController");
missileTarget = targetAssignment.GetComponent<TargetAssign>().target;
if (targetAssignment.GetComponent<TargetAssign>().HazardArray.Length == 0) Destroy(gameObject);
}
void FixedUpdate ()
{
transform.LookAt(missileTarget.transform.position);
transform.position = Vector3.MoveTowards(transform.position, missileTarget.transform.position, Time.deltaTime * speed);
}
}
You can add a bool to the hazards that turns true when it is targeted by a missle. Then add a “for” loop to the missles that check if the first hazard is already targeted, if not set that missles bool to true, if not, go to the next nearest hazard and check again.
Solved. I moved the Array Create and Cycle Check to the actual Missile. So when the missile is actually instantiated, we only have to do all that array stuff once. Further explanations found in comments in the code.
using UnityEngine;
using System.Collections;
public class HomingMissle : MonoBehaviour
{
//controls speed of the missile
public float missileSpeed;
//holds Gameobjects of the missile's target and the closest GameObject to the player
private GameObject closestTarget;
//holds vector values of the distance between distances of targets and the player's current position
private Vector3 distanceDifference;
private Vector3 playerPosition;
//decimal values that hold distances of the player to GameObjects
private float currentDistance;
private float oldDistance;
//declares the array that holds all the hazards currently on screen
private GameObject[] hazardArray;
// we write the following code in Awake because we only want to create an Array and cycle through it once. This keeps instructions computed by the engine low.
void Awake()
{
//oldDistance needs to be infinite at first to make it a huge value so currentDistance can be compared to it at the start of the for loop below
oldDistance = Mathf.Infinity;
//we set the local variable playerPosition to the player's position by finding the player GameObject by name.
playerPosition = GameObject.Find("Player").transform.position;
//instaniates the hazards' array
hazardArray = GameObject.FindGameObjectsWithTag("Hazard");
//the for loop goes through each hazard in the array. It checks the current size of the array with .Length and uses int i to cycle through.
for (int i = 0; i < hazardArray.Length ; i++)
{
//the first step in the for loop is to find the distance between the player and the current GameObject we are solving for in the Array
distanceDifference = hazardArray*.transform.position - playerPosition;*
-
//we set the float currentDistance to the square magnitude of the distanceDifference. Since distanceDifference is a Vector 3, sqrMagnitude helps us find the float value*
-
currentDistance = distanceDifference.sqrMagnitude; *
_ /now that we have the currentDistance of the Hazard we need to check three conditions_
_ 1st condition - We need to compare the oldDistance of the last Hazard in the array to the currentDistance of the current Hazard in the array we are solving for*_
* 2nd condition - We need to acces the current Hazard’s IsTargetted componet. It has a bool variable called isTargeted. If the hazard hasn’t been targeted its all good.*
* 3rd condition - We don’t want missiles to launch backwards from the ship. So we compare the player’s position to the hazard’s position and make sure the hazards are in about 5 units in front.*
_ if all 3 of the conditions are true…We have found our new closest Target./
if (currentDistance < oldDistance && hazardArray.GetComponent().isTargeted == false && playerPosition.z < hazardArray.transform.position.z - 5)
{
//this sets the closestTarget to the current Hazard we are solving for in our array*
closestTarget = hazardArray*;*_
* //this sets the oldDistance to the currentDistance for the next cycle through the loop. If the next Hazard in the array is closer, this will let us know*
* oldDistance = currentDistance;*
* //when we find the closest target we need to change the Hazard’s IsTargetted componet to isTargeted true. This keeps missiles from targetting already targetted Hazards.*
_ hazardArray*.GetComponent().isTargeted = true;
}
}
}*_
* //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 ()*
* {*
* //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);
* }
}*_
}