- Home /
Missiles move to already destroy objects [Problem][Update]
Hey guys I am having a issue with my homing missile.
We have to recreate a missile command game for class. Don't worry instructor says we can get help from the forums.
So here is my issue:
I have a working homing missile script that chooses the nearest target and moves to destroy it.
However the missile will still move towards a already destroyed object(Pictured below).
I know why it happens: I am using a Array for target selection so it is using the transform coordinates and moves towards that spot.
I do have my cities tagged as: "City" and I tried using "If" statements and "null" references to no avail. So how can I get my missiles to stop targeting already destroyed cities? I know it has to do with the Array but it is the only thing I could get working. Can you remove gameobjects from Arrays or make them smaller?
UPDATE: Made of changes based on your feedback and here it is however the Array is still choosing a already destroyed game object. I even untagged the game object and it still errors out. here is my code:
public GameObject[] targets;
public float speed;
private Rigidbody rb;
private GameObject chosenTarget;
void Awake()
{
rb = GetComponent<Rigidbody>();
targets = GameObject.FindGameObjectsWithTag("City");
ChooseTarget();
}
void FixedUpdate()
{
if (chosenTarget != null)
{
transform.LookAt(chosenTarget.transform.position);
float step = speed * Time.deltaTime;
transform.position = Vector3.MoveTowards(transform.position, chosenTarget.transform.position, step);
}
else
{
ChooseTarget();
transform.Translate(Vector3.forward * speed * Time.deltaTime);
}
}
void ChooseTarget()
{
float shortestDistance = Mathf.Infinity;
foreach(GameObject enemy in targets)
{
float distanceToEnemy = Vector3.Distance(transform.position, enemy.transform.position);
if(distanceToEnemy < shortestDistance)
{
shortestDistance = distanceToEnemy;
chosenTarget = enemy;
}
}
}
The objects get destroyed in a script attached to the city game objects. They are my targets. Even when they are removed from the scene the missiles keep moving towards the destroyed cities' location.
Answer by Thorny2000 · Apr 19, 2017 at 03:55 AM
First off, I'm assuming your "targets" array is having the destroyed targets removed in some other script, and that you either set them to inactive with "gameObject.SetActive(false)" or you make the entry in the array null.
What is happening at the moment is you chose the target in Start() with the call to ChooseTarget(). The missile doesn't choose a new target from that point and hence continues to the chosen point even if the target has gone.
You need to call ChooseTarget() occasionally to tell the missile to re-target on a new target. If the original target is still alive, the missile will simply choose that one again, as an added bonus if some other target is now closer the missile will move to that target instead.
You could check this every frame, it shouldn't cause many problems unless you had say 1000 missiles flying. You could also do it on a timer variable (as I've done below), or in a coroutine (a bit overkill for this but a good thing to know about - see https://docs.unity3d.com/ScriptReference/MonoBehaviour.StartCoroutine.html ).
public class HomingMissile : MonoBehaviour
{
public GameObject[] targets;
public float speed;
private Rigidbody rb;
private GameObject chosenTarget;
private float checkTargetsTime;
void Start()
{
rb = GetComponent<Rigidbody>();
ChooseTarget();
}
void Update() // Use Update, not FixedUpdate
{
transform.LookAt(chosenTarget.transform.position);
float step = speed * Time.deltaTime;
transform.position = Vector3.MoveTowards(transform.position, chosenTarget.transform.position, step);
// Every half second, re-choose the nearest target.
checkTargetsTime += Time.deltaTime;
if( checkTargetsTime >= 0.5f )
{
checkTargetsTime = 0;
ChooseTarget();
}
}
void ChooseTarget()
{
float shortestDistance = Mathf.Infinity;
foreach(GameObject enemy in targets)
{
if( enemy!=null && enemy.activeSelf ) // Only look at targets that are not null and set active
{
float distanceToEnemy = Vector3.Distance(transform.position, enemy.transform.position);
if(distanceToEnemy < shortestDistance)
{
shortestDistance = distanceToEnemy;
chosenTarget = enemy;
}
}
}
}
}
Actually yes the cities have a script attached to them that destroys itself when the missile comes in contact. However even when the missile is removed from the scene. The missile will still move towards the destroyed cities location.
public GameObject[] targets;
Are you actually assigning the targets in the inspector? I am not sure but maybe the reference still remains in the array of GameObjects, even after the individual target gameobject is destroyed or it is changed to 'missing'.
I mentioned that in the post. Yes the array is assigned in the inspector. However I made a new change based off some of the feedback on this post. I changed start to awake and in the awake function I called:
targets = GameObject.FindGameObjectsWithTag("City")
that seems to be working however the array will sometimes choose a already destroyed gameobject then error out saying its not there. I am on the right track.
How do you make entries in a array null? Cause that is my issue. It keeps selecting the same already destroyed game object and moves to that spot
If you look at my code in the Choosetarget() function, the line "if( enemy!=null && enemy.activeSelf )" only looks at that array entry if it is not null or if the gameObject is active. So in your other script when the gameObject is destroyed either of these lines will work: myTargetsArray[x] = null; ...or... myTargetsArray[x].SetActive(false);
Now, your second problem, the missile keeps going to the destroyed target will still happen unless you tell the missile to re-target. In my code, I am doing this with the lines after the comment " // Every half second, re-choose the nearest target.".
Answer by KEric · Apr 18, 2017 at 06:59 PM
Don't use Time.deltaTime in FixedUpdate as it is used for Physics related computation.
At the start make a copy of an array to preferably Generic.List and do all what you want with it during the game play - add or remove objects. You can also use a Set for that matter.
I agree, try using a list ins$$anonymous$$d of an array and remove objects from the list you are destroying.
There is no problem with using Time.deltaTime in FixedUpdate because Unity changes it to Time.fixedDeltaTime when used inside FixedUpdate.
According to Unity docs: When called (Time.deltaTime) from inside $$anonymous$$onoBehaviour's FixedUpdate, returns the fixed framerate delta time .
Time.deltaTime/fixedDeltatime -> http://answers.unity3d.com/questions/233918/fixed-update-and-timedeltatime.html
As the unity docs affirm, calling Time.deltaTime returns Time.fixedDeltaTime when called during the physics loop. This is easily provable by putting a Debug.Log in the update and fixed update of a script,
Ill give lists a try. Cause thanks to a few of you the code works if the Array chooses a gameobject that is not destroyed. $$anonymous$$issile changes direction and moves towards a new city. However if the object is destroyed and the Array chooses it again it errors out.
Thanks for your help :)
Okay I created the list but how do I remove it. All I did was
public List targets
but I am confused on how this works.
Answer by Cuttlas-U · Apr 18, 2017 at 06:24 PM
hi; u just need an if statement to check if the target is null or not ;
void FixedUpdate()
{
if ( chosenTarget != ull )
{
transform.LookAt(chosenTarget.transform.position);
float step = speed * Time.deltaTime;
transform.position = Vector3.MoveTowards(transform.position, chosenTarget.transform.position, step);
}
else
{
//your other code here
// transform.Translate(Vector3.forward * speed * Time.deltaTime) ;
}
}
u see there is an else function there u need to writhe something u can destroy the missile or make it to just move forward like this;
transform.Translate(Vector3.forward * speed * Time.deltaTime) ;
Hello your code half worked. If the game doesn't try accessing the same game object that has already been destroyed and it chooses any of the remain objects if will move toward that one. However if it chooses the any already destroy objects it errors out. On the right track thanks :)
I think a better way would be to not choose already destroyed target. That is what ChooseTarget() should actually do. It should just select available targets and not the one that is destroyed. Think along this line.
Yes I know that but my null references aren't working ill update post with my code.
Answer by BaldBeardedMonk · Apr 19, 2017 at 03:05 PM
Hello,
Using tags should actually solve your problem. you need to modify the code in Choosetarget() to only select gameobjects which are not destroyed. Lets consider the targets are tagged as "EnemyTargets". Use this when you choose the target
GameObjects[] Enemies = GameObject.FindGameObjectsWithTag("EnemyTargets");
foreach(GameObject enemy in Enemies)
Also make sure that once a target is destroyed the tag is set to null gameObject.tag = "Untagged";
Add this in the function wherever you are destroying the target.
This worked for me however the code will sometimes choose the same Arrayed gameobject and error out. Even if I use a != null reference.
The code is attached to a missile? and is it suppose to hit only one city? if only one target has to be chosen per missile (i.e the nearest one & both the missile and the city is destroyed after the collision) you should probably use co-routine ins$$anonymous$$d of FixedUpdate. Create a coroutine and call it from ChooseTarget() at the end. And do not use any if else inside the coroutine. The check is not needed because the coroutine is called only when the chosenTarget is selected. You just need to move it towards the city.
Yes the this script I am working with is attached to the missile. Yes it is only suppose to hit one city.
ill see if I can create one thanks
Call it outside. as it has to be called only once after the nearest enemy is selected