Search Unity

Reference to multiple cloned prefabs

Discussion in 'Scripting' started by tarzanno, Jun 30, 2015.

  1. tarzanno

    tarzanno

    Joined:
    Apr 23, 2015
    Posts:
    58
    Hi.
    I am struggling with this script. I am creating multiple objects called "enemy" from a prefab, and I want to have a reference to this in my Sword.cs script. Everything works with prepositioned object "enemy" but it doesn't work with clones.
    I thought that I have to make an array of this clones, so I changed the script:

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class Sword : MonoBehaviour {
    5.  
    6.     Animator anim;
    7.     public GameObject[] enemy;
    8.     public EnemyHealth enemyHealth;
    9.     ScoreManager scoreManager;
    10.     //public int amount = 100;
    11.     bool enemyInRange;
    12.     public int attackDamage = 10;
    13.  
    14.  
    15.     void Awake()
    16.     {
    17.  
    18.         enemy = GameObject.FindGameObjectsWithTag ("Enemy");
    19.         enemyHealth = enemy[].GetComponent<EnemyHealth>();
    20.         anim = enemy[].GetComponent<Animator>();
    21.  
    22.  
    23.  
    24.     }
    25.  
    26. void OnTriggerEnter2D(Collider2D enemy)
    27.     {
    28.         if(enemy.tag== "Enemy")
    29.         {
    30.         //Destroy (enemy.gameObject);
    31.             enemyInRange = true;
    32.  
    33.         }
    34.     }
    35.     void OnTriggerExit2D (Collider2D enemy)
    36.     {
    37.         if(enemy.tag == "Enemy")
    38.         {
    39.            
    40.             enemyInRange = false;
    41.         }
    42.    
    43.     }
    44.  
    45.     void Update ()
    46.     {
    47.  
    48.         //if the enemy is in range and this enemy is alive...
    49.         if (enemyInRange)
    50.         {
    51.  
    52.             Attack();
    53.         }
    54.         /*
    55.         //if the enemy has zero or less health///
    56.         if (enemyHealth.currentHealthEnemy <= 0)
    57.         {
    58.             //tell the animator that enemy is dead
    59.        
    60.             anim.SetTrigger ("enemyDead");
    61.  
    62.         }*/
    63.     }
    64.  
    65.     public void Attack()
    66.     {
    67.    
    68.  
    69.         //if the enemy has health to lose///
    70.         if (enemyHealth.currentHealthEnemy >= 0)
    71.         {
    72.             //damage the enemy
    73.  
    74.             enemyHealth.TakeDamage (attackDamage);
    75.         }
    76.         else {
    77.             return;
    78.         }
    79.        
    80.     }
    81. }
    82.  
    But got an error on
    Code (CSharp):
    1.     enemyHealth = enemy[].GetComponent<EnemyHealth>();
    2.         anim = enemy[].GetComponent<Animator>();
    "Assets/Sword.cs(19,39): error CS0117: `enemy[]' does not contain a definition for `GetComponent'".

    I don't know if I am heading in right direction? Is it a good idea to make an array?
     
  2. Deleted User

    Deleted User

    Guest

    enemy[] is a collection of GameObjects so you have to specify which one you want such as:

    enemy[0] .GetComponent<EnemyHealth>();
     
  3. tarzanno

    tarzanno

    Joined:
    Apr 23, 2015
    Posts:
    58
    Yeah, but as the "enemy" prefab is cloned multiple times (there can be up to 10 clones at the same time), can I make a dynamic collection or array of clones? Maybe there is some other way to do that?
     
  4. SubZeroGaming

    SubZeroGaming

    Joined:
    Mar 4, 2013
    Posts:
    1,008
    Create a list in a manager class to "hold" the cloned objects. It will be empty at runtime, and then whenever you create a clone.

    Code (CSharp):
    1. GameObject obj = Instantiate() as GameObject;
    2.  
    3. Manager.cloneList.Add(obj);
     
  5. tarzanno

    tarzanno

    Joined:
    Apr 23, 2015
    Posts:
    58
    I made a list in my SpawnEnemyScript:
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4.  
    5. public class SpawnEnemyScript : MonoBehaviour {
    6.  
    7.     public PlayerHealth playerhealth;
    8.     //public GameObject enemy;
    9.     public float spawnTime = 3f;
    10.     public Transform[] spawnPoints;
    11.     [SerializeField] GameObject enemy;
    12.     List<GameObject> prefabTransforms;
    13.  
    14.     void Start () {
    15.         InvokeRepeating("Spawn", spawnTime, spawnTime);
    16.         prefabTransforms = new List<GameObject>();
    17.             }
    18.  
    19.     void Spawn ()
    20.     {
    21.         if(playerhealth.currentHealth>= 0f)
    22.         {
    23.             int spawnPointIndex = Random.Range (0,spawnPoints.Length);
    24.  
    25.  
    26.             prefabTransforms.Add (Instantiate (enemy, spawnPoints[spawnPointIndex].position, spawnPoints[spawnPointIndex].rotation) as GameObject);
    27.         }
    28.  
    29.         else{
    30.             Debug.Break ();
    31.             return;
    32.         }
    33.     }
    34. }
    35.  
    But nothing has changed. What do you think, maybe it is because the reference in Sword.cs script to enemy shoud not be the "FindGameObjectWithTag, but there should be a reference to a list of clones? I don't know how to do it...

    Sword.cs:
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class Sword : MonoBehaviour {
    5.  
    6.     Animator anim;
    7.     public GameObject enemy;
    8.     public EnemyHealth enemyHealth;
    9.  
    10.     //public int amount = 100;
    11.     bool enemyInRange;
    12.     public int attackDamage = 10;
    13.  
    14.  
    15.     void Awake()
    16.     {
    17.  
    18.         enemy = GameObject.FindGameObjectWithTag ("Enemy");
    19.         enemyHealth = enemy.GetComponent<EnemyHealth>();
    20.         anim = enemy.GetComponent<Animator>();
    21.  
    22.  
    23.  
    24.     }
    25.  
    26. void OnTriggerEnter2D(Collider2D enemy)
    27.     {
    28.         if(enemy.tag== "Enemy")
    29.         {
    30.         //Destroy (enemy.gameObject);
    31.             enemyInRange = true;
    32.  
    33.         }
    34.     }
    35.     void OnTriggerExit2D (Collider2D enemy)
    36.     {
    37.         if(enemy.tag == "Enemy")
    38.         {
    39.          
    40.             enemyInRange = false;
    41.         }
    42.  
    43.     }
    44.  
    45.     void Update ()
    46.     {
    47.  
    48.         //if the enemy is in range and this enemy is alive...
    49.         if (enemyInRange)
    50.         {
    51.  
    52.             Attack();
    53.         }
    54.         /*
    55.         //if the enemy has zero or less health///
    56.         if (enemyHealth.currentHealthEnemy <= 0)
    57.         {
    58.             //tell the animator that enemy is dead
    59.      
    60.             anim.SetTrigger ("enemyDead");
    61.  
    62.         }*/
    63.     }
    64.  
    65.     public void Attack()
    66.     {
    67.  
    68.  
    69.         //if the enemy has health to lose///
    70.         if (enemyHealth.currentHealthEnemy >= 0)
    71.         {
    72.             //damage the enemy
    73.  
    74.             enemyHealth.TakeDamage (attackDamage);
    75.         }
    76.         else {
    77.             return;
    78.         }
    79.      
    80.     }
    81. }
    82.  

    When I make a list of objects in Sword:
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class Sword : MonoBehaviour {
    5.  
    6.     Animator anim;
    7.     public GameObject[] enemy;
    8.     public EnemyHealth[] enemyHealth;
    9.  
    10.     //public int amount = 100;
    11.     bool enemyInRange;
    12.     public int attackDamage = 10;
    13.  
    14.  
    15.     void Awake()
    16.     {
    17.  
    18.         enemy = GameObject.FindGameObjectsWithTag ("Enemy");
    19.         for(int i=0;i<enemy.Length;i++){
    20.         enemyHealth[i] = enemy[i].GetComponent<EnemyHealth>();
    21.         //anim = enemy[i].GetComponent<Animator>();
    22.  
    23.         }
    24.  
    25.     }
    26.  
    27. void OnTriggerEnter2D(Collider2D enemy)
    28.     {
    29.         if(enemy.tag== "Enemy")
    30.         {
    31.         //Destroy (enemy.gameObject);
    32.             enemyInRange = true;
    33.  
    34.         }
    35.     }
    36.     void OnTriggerExit2D (Collider2D enemy)
    37.     {
    38.         if(enemy.tag == "Enemy")
    39.         {
    40.            
    41.             enemyInRange = false;
    42.         }
    43.    
    44.     }
    45.  
    46.     void Update ()
    47.     {
    48.  
    49.         //if the enemy is in range and this enemy is alive...
    50.         if (enemyInRange)
    51.         {
    52.  
    53.             Attack();
    54.         }
    55.         /*
    56.         //if the enemy has zero or less health///
    57.         if (enemyHealth.currentHealthEnemy <= 0)
    58.         {
    59.             //tell the animator that enemy is dead
    60.        
    61.             anim.SetTrigger ("enemyDead");
    62.  
    63.         }*/
    64.     }
    65.  
    66.     public void Attack()
    67.     {
    68.    
    69.  
    70.         //if the enemy has health to lose///
    71.         for(int i=0;i<enemy.Length;i++){
    72.         if (enemyHealth[i].currentHealthEnemy >= 0)
    73.         {
    74.             //damage the enemy
    75.  
    76.             enemyHealth[i].TakeDamage (attackDamage);
    77.         }
    78.         else {
    79.             return;
    80.         }
    81.         }
    82.     }
    83. }
    84.  
    The enemyHealth value is not changing in the base object ("enemy", which I put just before the game starts) either.
     
    Last edited: Jul 17, 2015
  6. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    You need to use a for loop to iterate through your enemy collection and call GetComponent on each one.

    Incidentally this strikes me as bad design. Knowing about every enemy in the game isn't typically part of a sword's job.

    A better structure would be to use a collection such as a list or hashset. Add each enemy when in OnTriggerEnter, and remove it in OnTriggerExit.
     
    tarzanno and SubZeroGaming like this.
  7. tarzanno

    tarzanno

    Joined:
    Apr 23, 2015
    Posts:
    58
    I got rid of Sword script. Instead, I updated EnemyHealth:

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using UnityEngine.UI;
    4.  
    5. public class EnemyHealth : MonoBehaviour {
    6.  
    7.     public  int startingHealth = 10;
    8.     public int currentHealthEnemy;
    9.     public Image damageImage;
    10.     public AudioClip deathClip;
    11.     public float flashSpeed = 5f;
    12.     public Color flashColour = new Color(1f, 0f, 0f, 0.1f);
    13.     public int scoreValue = 10;
    14.  
    15.     public GameObject ScoreText;
    16.     ScoreManager scoreManager;
    17.  
    18.     AnimationClip clip;
    19.     Animator anim;
    20.     AudioSource enemyAudio;
    21.     public GameObject hitParticles;
    22.     BoxCollider2D boxCollider2D;
    23.         public Transform sword;
    24.     PolygonCollider2D polygonCollider2D;
    25.  
    26.     GameObject[] enemy;
    27.     bool isDead;
    28.     bool damaged;
    29.     private Animator animator;
    30.     bool swordInRange;
    31.  
    32.    
    33.     void Awake ()
    34.     {
    35.         //Seeting up the references
    36.         //sword = GetComponent <PolygonCollider2D>;
    37.         for(int i=0;i<enemy.Length;i++){
    38.         enemy[i] = GameObject.FindGameObjectsWithTag ("Enemy")[i];
    39.         }
    40.         boxCollider2D = GetComponent <BoxCollider2D> ();
    41.         currentHealthEnemy = startingHealth;
    42.  
    43.         ScoreText = GameObject.FindGameObjectWithTag ("ScoreManager");
    44.         scoreManager = ScoreText.GetComponent <ScoreManager>();
    45.  
    46.  
    47.  
    48.         anim = GetComponent <Animator> ();         //można przenieść do start()
    49.         enemyAudio = GetComponent <AudioSource> ();
    50.         //hitParticles = GetComponentInChildren <ParticleSystem>();
    51.         sword = GameObject.FindGameObjectWithTag("Sword").transform;
    52.        
    53.        
    54.  
    55.     }
    56.  
    57.     void OnTriggerEnter2D(Collider2D other)
    58.     {
    59.         if(other.tag== "Sword")
    60.         {
    61.             for(int i=0;i<enemy.Length;i++){
    62.             polygonCollider2D = sword.GetComponent <PolygonCollider2D>();
    63.             damageImage.color = flashColour;
    64.             damaged=true;
    65.             Instantiate (hitParticles, enemy[i].transform.position,Quaternion.Euler(0, 180,0 ));
    66.             swordInRange = true;
    67.             //Destroy (this.gameObject);
    68.             //return;
    69.             }
    70.         }
    71.  
    72.         else{
    73.             return;
    74.         }
    75.     }
    76.     void OnTriggerExit2D (Collider2D other)
    77.     {
    78.         for(int i=0;i<enemy.Length;i++){
    79.         if(enemy[i].tag == "Sword")
    80.         {
    81.            
    82.             swordInRange = false;
    83.         }
    84.         }
    85.     }
    86.  
    87.     void Start()
    88.     {
    89.         //setting up initial health
    90.         sword = GameObject.FindWithTag("Sword").transform;
    91.         Debug.Log(enemy);
    92.         Debug.Log(this);
    93.         if (boxCollider2D.isTrigger == true){
    94.         GameObject go = GameObject.FindWithTag("DamageImage");
    95.         damageImage = go.GetComponent<Image>();
    96.             isDead = false;
    97.         }
    98.         else{
    99.             return;
    100.         }
    101.  
    102.     }
    103.     void Update()
    104.     {
    105.         if (swordInRange)
    106.         {
    107.            
    108.             Attack();
    109.         }
    110.  
    111.         //if the enemy has just been damaged...
    112.         if(damaged)
    113.         {
    114.             damageImage.color = flashColour;
    115.  
    116.        
    117.         }
    118.    
    119.         else
    120.         {
    121.             damageImage.color = Color.Lerp (damageImage.color, Color.clear, flashSpeed * Time.deltaTime);
    122.         }
    123.  
    124.    
    125.         damaged = false;
    126.  
    127.  
    128.     }
    129.     public void Attack()
    130.     {
    131.        
    132.        
    133.         //if the enemy has health to lose///
    134.         //for(int i=0;i<enemy.Length;i++){
    135.             if (currentHealthEnemy >= 0)
    136.             {
    137.                 //damage the enemy
    138.                
    139.                 TakeDamage(10);
    140.             }
    141.             else {
    142.                 return;
    143.             }
    144.         //}
    145.     }
    146.    
    147.     public void TakeDamage (int amount)
    148.     {
    149.         damaged = true; //błyskanie
    150.  
    151.         currentHealthEnemy -= amount;
    152.         enemyAudio.Play ();
    153.             if(currentHealthEnemy <= 0 && !isDead)
    154.         {
    155.             Death ();
    156.         }
    157.     }
    158.    
    159.     void Death ()
    160.     {
    161.  
    162.         isDead = true;
    163.         //boxCollider2D.isTrigger = true;
    164.  
    165. //        Animation.Play(clip.name);
    166.         anim.SetTrigger ("enemyDead");
    167.         //transform.GetComponent<Animation>().Play("enemyDead");
    168.         enemyAudio.clip = deathClip;
    169.         enemyAudio.Play ();
    170.         Destroy (this.gameObject);
    171.  
    172.  
    173.         scoreManager.Score(1000);
    174.  
    175.        
    176.     }
    177.  
    178. }
    179.  
    And my SpawnEnemyScript is:

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4.  
    5. public class SpawnEnemyScript : MonoBehaviour {
    6.  
    7.     public PlayerHealth playerhealth;
    8.     //public GameObject enemy;
    9.     public float spawnTime = 3f;
    10.     public Transform[] spawnPoints;
    11.     [SerializeField] GameObject enemy;
    12.     List<GameObject> prefabTransforms;
    13.  
    14.     void Start () {
    15.         InvokeRepeating("Spawn", spawnTime, spawnTime);
    16.         prefabTransforms = new List<GameObject>();
    17.             }
    18.  
    19.     void Spawn ()
    20.     {
    21.         if(playerhealth.currentHealth>= 0f)
    22.         {
    23.             int spawnPointIndex = Random.Range (0,spawnPoints.Length);
    24.  
    25.  
    26.             prefabTransforms.Add (Instantiate (enemy, spawnPoints[spawnPointIndex].position, spawnPoints[spawnPointIndex].rotation) as GameObject);
    27.         }
    28.  
    29.         else{
    30.             Debug.Break ();
    31.             return;
    32.         }
    33.     }
    34. }
    35.  
    When on EnemyHealth there wasn't any for loop, when there was only one object with tag "Enemy", everything worked fine. Unfortunately, when tehre are multiple objects on a screen, none is getting the information to TakeDamage.
    I think my understanding of using the for loops in Lists is wrong. Should I put the information in EnemyHealth about the list made in SpawnEnemyScript?