Search Unity

Find and Destroy Question.

Discussion in 'Scripting' started by Deleted User, Feb 10, 2020.

  1. Deleted User

    Deleted User

    Guest

    Hi, I have the following code, which creates indicators on screen to let me know where enemies are when they are off screen, when the enemies are destroyed, the Indicator(Clone) is left behind, how can I find the clone that is instantiated from each enemy and then destroy it when the enemy is destroyed ? I can instantiate many enemies, so I might end up with a list like this :

    Indicator(Clone)
    Indicator(Clone)
    Indicator(Clone)
    Indicator(Clone)
    Indicator(Clone) ... etc


    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class AddTargetIndicatorCanvas : MonoBehaviour
    4. {
    5.     [Header("Canvas | Indicator")]
    6.     public GameObject indicatorCanvas;
    7.  
    8.     [Header("Canvas | Scene")]
    9.     public GameObject canvas;
    10.  
    11.     private int currentHealth;
    12.  
    13.     private void Awake()
    14.     {
    15.         if (canvas == null)
    16.         {
    17.             canvas = GameObject.Find("Canvas");
    18.             return;
    19.         }
    20.     }
    21.  
    22.     private void Start()
    23.     {
    24.         CreateTargetInCanvas();
    25.     }
    26.  
    27.     private void Update()
    28.     {
    29.         currentHealth = gameObject.GetComponent<PoliceDamage>().currentHealth;
    30.  
    31.         if (currentHealth <= 0)
    32.         {
    33.             // Destroy indicatorCanvas that has spawned for this individual object?
    34.         }
    35.     }
    36.  
    37.     void CreateTargetInCanvas()
    38.     {
    39.         // Instantiating indicator
    40.  
    41.         TargetIndicatorInCavas targetCanvasLocal = Instantiate(indicatorCanvas).GetComponent<TargetIndicatorInCavas>();
    42.         targetCanvasLocal.objectWorld = this;
    43.  
    44.         // Setting indicator as child of Canvas
    45.  
    46.         targetCanvasLocal.transform.SetParent(canvas.transform);
    47.  
    48.         // Setting a starting position for the indicator (0,0)
    49.  
    50.         targetCanvasLocal.transform.localPosition = Vector3.zero;
    51.         targetCanvasLocal.Target = this.transform;
    52.  
    53.         // Getting current render type
    54.  
    55.         Canvas canvasScript = canvas.GetComponent<Canvas>();
    56.         targetCanvasLocal.renderMode = canvasScript.renderMode;
    57.  
    58.         targetCanvasLocal.padding = 30f;
    59.  
    60.         targetCanvasLocal.thisObject = targetCanvasLocal.GetComponent<RectTransform>();
    61.         targetCanvasLocal.mainCanvas = canvas.GetComponent<RectTransform>();
    62.  
    63.         // Setting a size for the indicator
    64.  
    65.         targetCanvasLocal.thisObject.localScale = new Vector3(0.5f, 0.5f, 0.5f);
    66.     }
    67. }
     
  2. adi7b9

    adi7b9

    Joined:
    Feb 22, 2015
    Posts:
    181
    Maybe placing this script into enemy itself and activate it when you want. If the enemy is destroyed then the script and all the objects within enemy will be destroyed.

    Another way is to keep a "link" between the enemy and the indicator and when the enemy is destroyed .. on indicator update when that the enemy is null destroys himself :)
     
    Deleted User likes this.
  3. kdgalla

    kdgalla

    Joined:
    Mar 15, 2013
    Posts:
    4,634
    It looks like AddTargetIndicatorCanvas is only creating one indicator (On Start). Is that correct? What is it attached to?
     
    Deleted User likes this.
  4. TheGameNewBie

    TheGameNewBie

    Joined:
    Jul 27, 2017
    Posts:
    92
    Here's a basic example of how this could be done,

    You can create a class for Enemy,
    Code (CSharp):
    1. public class Enemy {
    2.           public GameObject enemy_obj; //The actual enemy gameobject which moves in the scene
    3.           public TargetIndicatorInCanvas indicator; //The indicator which is created for this enemy object
    4.           public int health;  //It's health
    5. }
    Next, you can have a list of this class. So, basically it's a list of all the enemies in the scene.
    public List<Enemy> enemies_list;


    When you create a new Enemy in the scene, You would create it like this,
    Code (CSharp):
    1. void CreateEnemy () {
    2.          Enemy enemy = new Enemy();
    3.    
    4.          //Instantiate the Enemy Object in the scene
    5.         GameObject obj = Instantiate(.......);
    6.         //Set the enemy object in the class we have
    7.         enemy.enemy_obj = obj;
    8.    
    9.         //Create a new Indicator for this enemy, using you method above. Get the reference to newly created indicator
    10.         enemy.indicator = newly_made_indicator_using_your_method_above;
    11.         enemy.health = 100; //Just some random health
    12.    
    13.          //Finally, add this to our enemies list
    14.          enemies_list.Add(enemy);
    15. }
    Now, In the update function, You can check an enemy's health and destory it's indicator. You can also disable/enable the indicator based on whether the enemy is visible on screen or not.
    Code (CSharp):
    1. void Update () {
    2.         for(int i = 0; i < enemies_list.Count; i++) {
    3.                //Now you can check for various conditions here
    4.                if(enemies_list[i].health <= 0) {
    5.                       //You have the reference to the indicator object for this enemy
    6.                       Destroy(enemies_list[i].indicator.gameObject);
    7.                }
    8.  
    9.               //You can also check whether the enemy is offscreen or not, and disable/enable their indicators accordingly
    10.              // If enemies_list[i].enemy_obj is visible, enable enemies_list[i].indicator and disable if not visible
    11.         }
    12. }
    You may need to adjust this code according to your needs. This is just a working skeleton of the code I use.

    Also, I would recommend switching to Pooling Objects. You can find plenty of tutorials for Pooling on Google.
     
    Last edited: Feb 10, 2020
    Deleted User likes this.
  5. Deleted User

    Deleted User

    Guest

    @adi7b9 Thanks for your reply. The script itself 'is' attached to each 'enemy' game object as they are spawned, but I the target indicators,when spawned, are made children of the ui canvas.

    @kdgalla Thanks, yes it only adds one Target Indicator at a time.

    @TheGameNewBie Thanks for your reply, and for going into so much detail with it. I very much appreciated that! What you said inspired me to go in another direction, and I've solved the problem now.

    So, in the script I have for handling the damage for 'each' of my enemies, I have created 2 new variables :
    Code (CSharp):
    1. private GameObject[] targetIndicators;
    2. private int currentTargetIndicator;
    in my Update method, I then have :
    Code (CSharp):
    1.         // Find the associated Indicator reference
    2.  
    3.         targetIndicators = GameObject.FindGameObjectsWithTag("Indicator");
    4.  
    5.         for (int i = 0; i < targetIndicators.Length; i++)
    6.         {
    7.             currentTargetIndicator = targetIndicators.Length;
    8.         }
    so, this gets the total amount of target indicators in the scene when 'that' object is spawned in, and assigns that value to currentTargetIndicator. Then when my enemies health reaches 0, I can destroy the associated Target Indicator by using :
    Code (CSharp):
    1.  // Destroy Target Indicators
    2. Destroy(targetIndicators[currentTargetIndicator]);
    Now, it solves the problem, but I'm not sure how efficient this is ( especially the update method, would there be a better way ?, I can't do this in Start, because usually the target indicators are spawned in after the object is initially spawned, so Start doesn't see them at all).

    I might try calling this as a function from Update, and using a boolean to 'confirm' a value has been assigned to currentTargetIndicator other than 0 instead, seems like it might be a bit more efficient to do that instead.

    Once again, thanks all for the suggestions !

    EDIT : Spoke too soon, as 'indicators' are destroyed, it messes up the array indices, so I still need to find a better fix... :oops:
     
    Last edited by a moderator: Feb 10, 2020
  6. TheGameNewBie

    TheGameNewBie

    Joined:
    Jul 27, 2017
    Posts:
    92
    Try using a list instead of Arrays. It works better with removing and adding items to it.
     
    Deleted User likes this.
  7. Deleted User

    Deleted User

    Guest

    Thanks,
    Creating lists and arrays, etc isn't the problem, I'm just finding it difficult to reference the spawned objects original parent.

    So, an enemy is spawned in, the enemy has the targeIndicator script on it, a targetIndicator is instantiated in from that script and immediately made a child of the canvas in the scene. So, later the enemy is destroyed, how do I get a reference that the targetIndicator that was spawned belonged to the enemy that was destroyed, so I can destroy the targetIndicator at the same time. I've tried numerous methods so far, but nothing is giving me what I want, which is the spawn origin object, so that when it is destroyed, anything it spawned, even though it is now a child of another object in the scene, it also destroyed.
     
  8. TheGameNewBie

    TheGameNewBie

    Joined:
    Jul 27, 2017
    Posts:
    92
    Did you try using the method I gave above? I'm pretty sure it would work for what you're trying to do.
    The Enemy class contains
    1.enemy_obj (enemy Object) which you spawn in the scene
    2.indicator which you create for that enemy

    When you spawn an enemy, You assign the spawned object to the enemy_obj in the class.
    When you create an Indicator for this enemy, You assign the indicator to the indicator in the class.

    So, the class holds two references,
    The enemy object which was spawned in
    and the indicator for this specific enemy

    Now, you have a list of this class. Which basically means you have a list of Enemies in the scene.
    It's something like this,
    List of Enemies :-
    Element 1: enemy_obj : The object for this enemy
    indicator : Indicator for this enemy

    Element 2: enemt_obj : Object for this enemy
    indicator : Indicator for this enemy
    And so on.
    Suppose, enemy from element 1 health reaches 0.
    So, you would do something like,
    Destroy(Enemies[1].enemy_obj);
    Destroy(Enemies[1].indicator);
     
    Deleted User likes this.
  9. Deleted User

    Deleted User

    Guest

    @TheGameNewBie
    Thanks again for the in depth reply.

    In a moment of sheer stupidity on my behalf, I have 'now' actually solved the problem. I couldn't 'see the forest for the trees', clearly, if I have a target indicator that tracks the enemy object on screen, then I always have a reference to the enemy object that instantiated it, and so, with a little additional code added to my targetIndicator 'tracking' script, I simple check if the 'Transform' I'm tracking becomes 'null', and if so, destroy the gameObject. :oops: Apologies, I should've thought about this much earlier, all I could focus on were the Player and Enemy scripts, so angry with myself for not seeing the obvious... :mad:
    Code (CSharp):
    1.         if (Target)
    2.         {
    3.             UpdateInfoIndicatorCanvas();
    4.  
    5.             Debug.Log("Target is : " + Target);
    6.         }
    7.         else
    8.         {
    9.             Debug.Log("Target is DESTROYED!");
    10.  
    11.             Destroy(gameObject);
    12.         }
     
    Last edited by a moderator: Feb 11, 2020